diff --git a/Changelog b/Changelog
new file mode 100644
index 0000000..d3752d3
--- /dev/null
+++ b/Changelog
@@ -0,0 +1,283 @@
+Changelog for libmawk
+~~~~~~~~~~~~~~~~~~~~~
+libmawk 1.0.0 (released: 2018-11-11, r1276)
+ [API] -Change: resume capable function call entries
+ [build] -Fix: make clean should remove custom C test objects and executables
+ [build] -Fix: incomplete install headers list
+ [build] -Add: Makefile variable for selecting zmalloc alternatives
+ [build] -Add: optional DESTDIR for debian
+ [build] -Add: --prefix, --debug, --symbols
+ [build] -Add: help
+ [build] -Chnage: upgrade to tmpasm and latest version of scconfig
+ [build] -Add: optional support for soname
+ [build] -Change: use $(MAKE) instead of make in central Makefiles for parallel build
+ [build] -Add: detect -rdynamic
+ [core] -Change: extend mpow2 for covering new celltypes
+ [core] -Cleanup: rename _STOP and _RANGE to _RANGE_STOP and _RANGE_CHK to make their purpose more clear
+ [core] -Fix: return read error in getline doesn't fall in infinite loop in fill buff anymore
+ [core] -Add: MAWK_MEM_PEDANTIC compile time option; when set, try to free all allocation instead of letting zmalloc catch them all
+ [core] -Fix: don't free the hash before freeing argv and others
+ [core] -Fix: make sure code blocks are free'd only once, but are really free'd
+ [core] -Fix: memory leak when initializing argv
+ [core] -Fix: let mawk_delete() free arrays, don't manually do that on argv
+ [core] -Change: let mawk_delete() free user function
+ [core] -Change: fin is under files - higher level code should do all access through the file infra
+ [core] -Fix: FIN is only about buffering, no open/close should go through that API
+ [core] -Fix: reference count vf structs since one vf might be open multiple times from FILE_NODEs (for input and output) and should be destroyed only once
+ [core] -Fix: closing all files, not only output files, is not a pedantic-mem feature - not doig it could lead to leaking fds and other resources even if zmalloc pools are freed centrally
+ [core] -Fix: there is only one way files are closed, only one code is needed
+ [core] -Fix: mawk_fill_buff returns a signed long for indicating temporary error (fixed for 64 bit)
+ [core] -Fix: rename state field userdata to func_userdata to make the purpose more clear
+ [core] -Add: ctx_userdata intended for the host app for storing whatever data, per context
+ [core] -Add: have a field in the state struct for the C function being called back so that subsequent execute()s may know about that
+ [core] -Fix: memory leak: free data of c function calls compiled into the code at uninit
+ [core] -Fix: C89 fixes (func-data ptr conv through union, don't use strcasemp(), strdup, snprintf, //, vararg macros)
+ [core] -Fix: wrong arg passed to isnan - cell instead fo double value
+ [core] -Fix: vio fclose survives if there's no close function available (double close?)
+ [doc] -Add: short desc about the vm
+ [doc] -Add: explain how execution/resume works
+ [doc] -Add: describe deep recursion
+ [doc] -Add: minimalistic install
+ [example_apps] -Add: blocking fifo test also tests bifunct getline in an user function
+ [example_apps] -Add: make sure to demonstrate how the code is suspended even in user functions
+ [example_apps] -Update: 15_call for the new function call API
+ [example_apps] -Add: example on run limit resume
+ [example_apps] -Add: central Makefile that runs all examples
+ [example_apps] -Add: call a c function from c using awk's func call (demonstrate that awk and c functions should not make any difference)
+ [example_apps] -Fix: hash_re won't init std pipes twice
+ [example_apps] -Fix: custom vio test sets up pipes correctly and closes the hass from the app's end of the deal at the end
+ [example_apps] -Add: better explanation of the c_func_call example and hook it in the all-test-chain
+ [libmawk] -Fix: C function call from execute with new function call conventions
+ [libmawk] -Fix: call_functionp increases sp before push else the stack pointer points to the wrong place
+ [libmawk] -Fix: restore sp on function call failure
+ [libmawk] -Fix: before closing stdio on awk side, close stdin on app side to make sure both ends can be closed
+ [libmawk] -Fix: do not leak function arguments already pushed on stack if an awk function call fails due to bad arguments
+ [libmawk] -Fix: allow c functions to be called using mawk_call_function*()
+ [libmawk] -Fix: don't close stdin if it's not open
+ [libmawk] -Fix: don't use strdup() for c function reg
+ [libmawk] -Fix: don't close non-fifo stdin as fifo
+ [libmawk] -Fix: priting uninitialized cell should work and should result in empty string
+ [libmawk] -Add: wants_to_exit flag so that a libmawk caller knows a script wants to exit
+ [libmawk] -Add: function call API with cell argc/argv
+ [parser] -Fix: really reset the parser stack when it gets freed - used to segfault for two includes on the topmost level
+ [parser] -Fix: allow excess newlines in the main context - this fixes a corner case around multiple includes
+ [regression] -Add: test for bifunct call()
+ [regression] -Add: test for acall() (dynamic call with array as argument)
+ [regression] -Add: test for stack grow on deep recursion
+ [regression] -Add: test program for awk function call corner cases
+ [regression] -Add: libmawk corner case test base
+ [regression] -Fix: libmawk func call test diag messages are more clear on what's wrong or good
+ [rexp] -Fix: infinite loop bug (gawk test noloop1 and noloop2) - contributed by Thomas E. Dickey as a mawk fix
+ [vio] -Fix: set eof flags on fifo creation (fixes a conditional jump on uninited var bug)
+ [vio] -Fix: closing FIN closes the vio layer even if fin buffer had not been initialized
+ [vio] -Fix: close vf throug FIN to make sure buffers are also freed
+ [vio] -Fix: simpler common header with refco and who-knows-what in the future
+ [vio] -Fix: initialize refco to 0
+ [vm] -Change: avoid recursive calls to execute() on plain hardwired awk function calls
+ [vm] -Change: wants_to_ret is execution state instead - need to remember whether we are running range1 or range2
+ [vm] -Fix: leak in function return value stack handling
+ [vm] -Add: new convention in bi_funct calling: return stack may be a directive for executing an user functioin (for bi_call and bi_acall)
+ [vm] -Fix: mawk_bi_call() and bifunct acall() follow the new no-recurse calling convention
+ [vm] -Add: new low level c->awk call functions trying to follow the no-recurse execute_() conventions
+ [vm] -Move: execute API into new execute.h
+ [vm] -Add: a separate execution exit status for exit()
+ [vm] -Add: execution run limit and resume
+ [vm] -Fix: bi_funct getline can indicate 'nomore' instead of eof
+ [vm] -Fix: properly save and restore old_stack_base and old_sp on stack danger (stack growth)
+ [vm] -Cleanup: remumber types (continous numbering)
+ [vm] -Add: more stack-related cell types and make room for future non-string additions
+ [vm] -Fix: compare() returns double so that nan-checks can be performed
+ [vm] -Fix: error return with value from execute()
+ [vm] -Fix: make MAWK->pow big enough and make it safe (need to be unsigned long for these values and for the bit ops)
+
+
+libmawk 0.10.1 (released: 2014-07-11, r1056)
+ [compiler] -Fix: zero-offset bug in jmp push (xref test crash)
+ [build] -Add: make test should run the standard regression tests; do not run tests automatically on build
+ [core] -Add: zfifo: generic fifo implemented using zmalloc/zfree
+ [core] -Add: mawk_file_find() has a create flag that can disable creating a new file node - useful for forcing existing/open files
+ [core] -Cleanup: move fin struct to fin.h
+ [core] -Cleanup: rename FIN to mawk_input_t for naming consistency
+ [core] -Fix: rewritten mawk_FINgets - cleaner code that is non-blocking-safe
+ [core] -Fix: zrealloc() should not crash if old ptr is NULL
+ [core] -Fix: safer vf close, close outputs only in uninit, when EXIT has finished for sure
+ [vio] -Fix: input is always fd, output is always FILE *; remove fgets and stdin related API
+ [vio] -Clenaup: cleaner ways recognizing "/dev/std*"
+ [vio] -Add: support for /dev/fd/xxx (fdopen() wrapper)
+ [vio] -Add: an extra layer to dispatch vio calls - multiple vio implementations can coexist
+ [vio] -Add: virtualize vio init (non-per-file vio hooks); use MAWK->vio_init instead of hardwired vio_orig (so that vio_orig can be omitted)
+ [vio] -Del: setbuf from API - this callback was a hack, now all done by the call that sets up stdio
+ [vio] -Change: replace is_stdouts with a more generic mark_no_close hook
+ [vio] -Fix: use zmalloc/zfree for the mawk_vio_t allocations
+ [vio] -Add: a generic fifo implementation that also replaces the artificial FIN buf
+ [regression] -Cleanup: remove test/ and convert all shell script based tests to make-based tests in regression/
+ [tools] -Fix: find_globals uses the right dir
+ [tools] -Fix: sym prefix validator won't complain for main()
+ [API] -Add: file name validation hook
+ [API] -Add: call to register a file by name:type and vio handle in the high level list of files; also sets up input buffering as needed
+ [API] -Add: call to close the default input fifo (sending eof to the script)
+ [API] -Add: convenient stdio-init call for multi-staged init
+ [example_app] -Add: custom output pipe hack examples and update pipe in buffer fill examples with all possible corner cases
+ [example_app] -Add: file name validation and vio hook examples
+
+
+libmawk 0.10.0 (released: 2014-06-26, r937)
+ Major changes in core, mostly for virtual arrays:
+ [array] -Add: introduce new VM instructions for all array-element-write operations and split the lvalue rule of the grammar; besides being new instructions, they do the same in the execution switch as before
+ [array] -Add: virtualize array operations
+ [array] -Add: virtual, per instance ENVIRON[] that copies environ[] upon the first access and affects exec()
+ [array] -Del: ST_ENV: ENVIRON[] is not a global special case anymore, just a builtin array with a different implementation
+ [core] -Fix: replace "short int type" and a bunch of C_ macros for cell type with a proper enum
+ [core] -Add: macro option for cell operators for speed
+ [core] -Fix: set ERRNO in fin after a read error
+ [core] -Cleanup: mawk_ prefix zmalloc, zfree, ZMALLOC, ZFREE and a lot of other constants and macros
+ [init] -Add: a flag for indicating that it is ok for initialize_argv() to end up without a script
+ [dump] -Fix: print the code of illegal instructions
+ [dump] -Add: da_text: use blank lines to separate blocks: functions, BEGIN, MAIN, END
+ [dump] -Change: da_text: after user functions, dump code in BEGIN-MAIN-END order - it is more intuitive this way
+ [dump] -Add: text dump includes a summary of the global symbol table
+ [dump] -Change: introduce -W dumpsym for dumping symbols independently of dumping code
+ [da_bin] -Fix: properly save/load C_SNULL and C_SPACE (special split argument types) to/from binary dump
+ API CHANGES affecting libmawk:
+ [core] -Clenaup: rename STRING to mawk_string_t
+ [core] -Clenaup: rename CELL to libmawk_cell_t
+ [core] -Change: use an union instead of dval in CELL, to make room for other data types
+ [core] -Add: new cell type C_ARR_REF (should be execute()-internal); an array reference by name and index (instead of pointer to a cell)
+ [API] -move parsing the script from init stage1 to stage2 to give the host app a chance to inject things into the context before any code is parsed
+ [API] -Change: get_var should return a read-only CELL - direct modification of the cell is a bad idea due to possible side effects required at write
+ [API] -Fix: libmawk_print_cell shall be called with the context (mawk_state_t) as any other call in the API
+ [API] -Change: new, easier-to-use c-func calling conventions and helpers
+ [array] -Cleanup: rename ARRAY to mawk_array_t
+ New libmawk API features:
+ [API] -Add: helper functions to return the numeric value of a cell
+ [API] -Add: an easy way to set a cell to empty
+ [API] -Add: high level array set_at calls and scalar set calls
+
+libmawk 0.9.7 (released: 2014-05-17, r732)
+ Major code structure changes:
+ [vio] -Split: file/pipe open/close operations to virtual IO (vio)
+ [linux] -Add: initial linux kernel module effort (defunct yet)
+ [array] -Add: virtualized array access (except for write) with callback functions
+ [libmawk] -Split: lmawk-comp and lmawk-exec frames - most code is in common yet
+ [libmawk] -Add: MAWK_NO_FORK for configuring against system() and fork()
+ Code cleanup:
+ [da_bin] -Cleanup: compiler warnings around da_bin (binary script save/load)
+ [da_bin] -Split: da_bin and da_bin_helper: precompiled binary script load doesn't directly call read() but an user provided callback for virtualization
+ [da_bin] -Fix: array creation on binary load
+ [da_bin] -Fix: when resetting code base size for loading binary, use zmalloc for proper allocation size
+ [build] -Fix: Makefile cleanup for portability
+ [libmawk] -Fix: prefix NF, RS and other similar macro names with MAWK_ - they collide in the Linux kernel
+ [test_3rd] -Add: scripts for running optional 3rd party tests from vendor/ (for the gawk set, without much configuration or filtering)
+ [regex] -Change: error handling without setjmp/longjmp (return values and a bunch of checks)
+ [regex] -Fix: regex lib uses zmalloc memory allocation to ensure all memory is free'd after a context is destroyed
+ [regex] -Add: tiny test program to trigger almost all possible error conditions in the regex lib
+ [regex] -Fix: move runtime states of the regex lib in the mawk struct to make it reentrant
+ [regex] -Fix: bi_vars are part of the mawk struct because parallel scripts may have different values for the same variables or even modify them from script
+ More portable doubles (and numeric):
+ [numeric] -Add: use porty's math_wrap for log() to get rid of undeterministic fpe
+ [numeric] -Add: use PM math protection copied from libporty
+ [numeric] -Add: new builtin function isnan()
+ [numeric] -Fix: virtualize strtod int strtonum: on a real int-only platform (e.g. Linux kernel) there won't be strtod at all
+ Fixes of the core functionality:
+ [libmawk] -Fix: make sure runtime error exit code takes over in final_error_code
+ [bi_func] -Fix: substr for start indices less than 1 behave similar to how substr() handles length overruns (silently truncate) - gawk has the same behavior
+ [bi_func] -Fix: gsub() patch for the ^ bug
+ [io] -Fix: redirection conditions messed up in r349
+ [libmawk] -Fix: disable -v only when MAWK_NO_EXEC is set, and don't even consider MAWK_NO_COMP
+ [libmawk] -Fix: fin_exec should respect a MAWK exit request and not retry reading
+ [libmawk] -Fix: if FIN can not be open (invalid file name, nonexisting file), don't segfault but return error
+
+libmawk 0.9.6 (missed)
+
+libmawk 0.9.5 (released: 2012-10-13, r527)
+ [libmawk] -Add: memory usage accounting figures; -Wmaxmem=size sets maximum
+ memory allocation in bytes (optional suffixes: k and m)
+ [libmawk] -Add: binary save/load functionality (-Wcompile and -b)
+ works on 32 bit systems only (will be fixed later)
+ [libmawk] -Change: replace hardwired double references with generin numeric
+ (C_NUM, num_t) - libmawk can be configured to use int or
+ double as numeric format (./configure --numeric=)
+ [libmawk] -Cleanup: (portability tweak) const correctness in mawk_reverse_find
+ [libmawk] -Add: (portability tweak) _POSIX_SOURCE
+ [libmawk] -Fix: (portability tweak) explicit rules for rexp/*.o incuding
+ $(CC) command line for old fashioned make having the wrong
+ implicit rule
+ [libmawk] -Cleanup: (portability tweak) k&r style function
+ declarations/definitions removed from rexp lib
+ [libmawk] -Fix: (portability tweak) missing prototypes to avoid implicit
+ declaration warning
+ [libmawk] -Change: disable two zfree() calls that would free main code to
+ avoid double frees
+ [libmawk] -Add: compile with app clfags
+ [libmawk] -Cleanup: move scconfig'd -D options to conf.h from Makefile.conf
+ to make compiler command lines shorter
+ [libmawk] -Cleanup: new vars.[ch] for collecting variable-related code
+ [libmawk] -Fix: mawk_mm (aka free_later) mechanism memory handling erros
+ when realloc()'d
+ [doc] -Add: explain design decisions behind our two gnu-dependencies
+ [doc] -Add: portability table (per system: compiles out of the box, has
+ FPE problems, awklib test ran fine)
+
+libmawk 0.9.4 (released: 2010-12-26, r392)
+ [scconfig] -Add: detect pipe(2) and set NO_PIPE, replacing HAVE_REAL_PIPES
+ [scconfig] -Add: detect size_t include file (removes the ifdef mess)
+ [scconfig] -Add: require detection of cc/fpic (fixes compilation on amd64)
+ [libmawk] -Cleanup: del PROTO() macro: libmawk requires at least C89 compiler
+ [libmawk] -Cleanup: remove revision history and file name comments from *.[ch]
+ copyright notice above all mawk copyright notices
+ [libmawk] -Cleanup: version to clearly indicate that this is lmawk
+ [libmawk] -Cleanup: unified indentation in all .c and .h files
+ [libmawk] -Cleanup: remove a lot of compiler warnings, fix const correctness
+ [libmawk] -Cleanup: Makefile.dep generation rule; proper, up-to-date Makefile.dep
+ [libmawk] -Del: remove FAKE_PIPES support (was only for DOS anyway)
+ [libmawk] -Del: void * is a must, PTR should not fall back to char *
+ [libmawk] -Del: cut out MSDOS support with all its "dreaded segment nonsense"
+ [libmawk] -Fix: get bison to generate a reentrant parser
+ [libmawk] -Fix: move static globals to mawk state struct
+ [libmawk] -Fix: memory leaks
+
+
+libmawk 0.9.3 (released: 2010-12-21, r307)
+ [libmawk] -Fix: use -fPIC for compiling (helps on amd64)
+ [libmawk] -Add: new builtin variable ERRNO with a different error code for each corner case in extension calls
+ [libmawk] -Add: new built-in variable LIBPATH for library search path
+ - skip loading a script if name started with '+' and the script was already loaded
+ - if script file name starts with '/', assume it to be an absolute path and skip LIBPATH search
+ [scconfig] -Add: scconfig configures/generates Makefiles of libmawk after autodetecting system features
+ [scconfig] -Del: config-user, v7, atarist: scconfig should take care of all supported systems
+ [scconfig] -Add: central ./configure and Makefile
+ [libmawk] -Fix: order of running BEGINs with "include"
+ [libmawk] -Fix: memory leaks around parser state
+ [awklib] -Add: three awk libraries:
+ - lib_rand for reproducible pseudo random numbers
+ - lib_array for packing/unpacking/printing arrays
+ - lib_ascii for converting characters to ASCII codes
+
+
+libmawk 0.9.2 (released: 2010-12-12, r229)
+ [libmawk] -Fix: invalid memory handling around cells
+ [libmawk] -Fix: manual page dates and author and project/executable name
+ [libmawk] -Add: dynamic awk function calls (call() and acall())
+ [libmawk] -Add: dynamic awk variable value fetch (valueof())
+ [libmawk] -Add: include
+
+libmawk 0.9.1 (released: 2009-08-14, r198)
+ [API] -Add: remember userdata when registering and calling back c functions
+ [API] -Add: option to suppress undefined function calls
+ [API] -Add: new call allowing awk function calls without varargs
+ [API] -Change: split up libmawk_initialize in 3 stages (optional)
+ [libmawk] -Del: autotools, keep last generated Makefile for hand editing
+ [libmawk] -Del: config.h (merged in Makefile.conf.in)
+ [libmawk] -Rename: mawk -> lmawk (the binary)
+ [libmawk] -Change: print cell prints integers without .000000
+ [testapp] -Move: testapp out from libmawk (new dir structure)
+ [testapp] -Fix: stack handling bug
+ [doc] -Add: manual pages for libmawk calls
+
+libmawk 0.9.0 (released: 2009-07-22, r146)
+ * Initial release, based on mawk 1.3.3
+ * r3...r8 Separate libmawk_call() from execute(); allow undefined functions
+ * r9...r42 globals to struct (reentrant)
+ * r43...r92 rename non-static functions to have a mawk_ prefix
+ * r92...r145 libmawk
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e0fc168
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+all: src/libmawk/Makefile.conf
+ cd src && $(MAKE)
+
+install: src/libmawk/Makefile.conf
+ cd src && $(MAKE) install
+ cd doc && $(MAKE) install
+
+uninstall:
+ cd src && $(MAKE) uninstall
+
+linstall: src/libmawk/Makefile.conf
+ cd src && $(MAKE) linstall
+
+clean:
+ cd src && $(MAKE) clean
+
+distclean:
+ cd src && $(MAKE) distclean
+ cd scconfig && $(MAKE) clean
+
+test:
+ cd src && $(MAKE) test
+
+src/libmawk/Makefile.conf:
+ @echo "Please run ./configure first."; false
diff --git a/README b/README
new file mode 100644
index 0000000..ebb6ee0
--- /dev/null
+++ b/README
@@ -0,0 +1,42 @@
+1. Introduction
+
+Libmawk is a fork of mawk 1.3.3, restructured for embedding. This means the
+user gets libmawk.h and libmawk.so and can embed awk scripting language
+in any application written in C. For more information, check out the web page
+at http://repo.hu/projects/libmawk and the documentation in doc/.
+
+2. Requirements and compiling
+
+ANSI C compiler, POSIX shell and make are required for compiling libmawk.
+Bison should be installed for developing libmawk; if it is not installed,
+local changes to the grammar will be ingored.
+
+./configure; make
+
+On top of usual scconfig arguments, ./configure accepts --numeric=TYPE,
+where TYPE is int or double. Default is double. This switch affects
+what type libmawk stores numbers in.
+
+3. installation
+
+Run "make install" or "make linstall". The linstall version
+will use symlinks instead of actual copying of files which is useful if
+you develop libmawk, the library itself.
+
+Debian package can be installed from http://repo.hu/debian.
+
+4. Compatibility with mawk
+
+Compatibility with mawk is maintained to some degree. Currently libmawk
+offers a small set of extra features on awk level while providing
+everything that mawk provides. A valid mawk script will work with
+libmawk/lmawk without modification. However, the new features will work
+only with libmawk/lmawk and not mawk, so portable scripts shouldn't
+depend on them. All extensions are clearly marked in the manual.
+
+Conclusion: libmawk will compile and install mawk executable, which is
+backward compatible with mawk executable but also adds some extension
+features.
+
+Awklib depends on one of the libmawk features (include), thus awklib
+scripts won't work with other awk implementations without tweaking.
diff --git a/Release_notes b/Release_notes
new file mode 100644
index 0000000..eebcfd1
--- /dev/null
+++ b/Release_notes
@@ -0,0 +1,5 @@
+Release notes for libmawk 1.0.0
+
+This release introduces runlimits, better floating point handling on
+corner cases (i.e. NaN) and memory leak cleanups.
+
diff --git a/configure b/configure
new file mode 100755
index 0000000..a960b35
--- /dev/null
+++ b/configure
@@ -0,0 +1,3 @@
+#!/bin/sh
+cd scconfig && make && ./configure "$@"
+
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..a4ce05e
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,7 @@
+all:
+
+include ../src/libmawk/Makefile.conf
+
+install:
+ $(MKDIR) $(DOCDIR)
+ $(CP) $(PWD)/*.html $(DOCDIR)
diff --git a/doc/Release_howto b/doc/Release_howto
new file mode 100644
index 0000000..a225262
--- /dev/null
+++ b/doc/Release_howto
@@ -0,0 +1,5 @@
+1. update the changelog
+2. update the release notes
+3. change the version number in src/scconfig/hooks.c
+4. test a fresh checkout
+5. svn tag
diff --git a/doc/TODO b/doc/TODO
new file mode 100644
index 0000000..96264eb
--- /dev/null
+++ b/doc/TODO
@@ -0,0 +1,77 @@
+000. full doc rewrite
+
+Features:
+ - c->awk func call should be able to pass arrays
+
+00. vio rewrite
+ - add all vio in a linked list in MAWK for garbage collection at mawk_uninit
+ - add a force-clenup hook for this; but also replace mawk_close_out_pipes()
+ - hooks? wrapper?
+ - document file_name_rewrite
+ - document vio
+
+0. bugs
+ - string ref crash:
+ for(n = 0; n < 1000000; n++) A[n] = "foo"
+ if refcount of "foo" reaches 65535, it crashes
+ - mpow2 should be static
+
+1. restrictions
+ - detect and use LDFLAGS -dynamic
+ - split compile and run into separate libs:
+ - implement a Single Safe number->string converter;
+ grep for OFMT and CONVFMT and INT_FMT to find all the buggy sprintf based
+ implementations
+ - split da_bin to exec and comp
+ - fix error.c: it shouldn't depend on stdio.h and it shouldn't print
+ to stderr anyway (incompatible with the lib idea)
+ - check whether int-compiled lmawk handles OFMT/CONVFMT properly
+ - floating point:
+ - try to find a platform with FPE for overflow and test
+ - consider a -W nandebug option so that the user knows where the script
+ went wrong; but he could also just check from the script
+ - if isinf() is avaialable, check for inf() result and convert them to nan
+
+
+1.5.
+ - check all zmalloc() and zrealloc() calls - they may return NULL and callers
+ should return in that case so that runtime error takes over
+
+2. porting
+ - test on UNIX
+ - provide a real alternative to realpath()
+
+3.0 extend arrays
+ - array copy
+ - array in array (for orig implementation only?)
+ - length(array) as in gawk? POSIX: length() works on strings only
+ update test_3rd funlen accordingly!
+
+3.1 features, minor bugs
+ - debugging (gdb scripts); location-file instruction to track src file changes
+ - consider printf with no arg to work like in gawk; also check posix about this
+ test_3rd: printf0
+ - decide whether regex "^+" (and "^*"?) should be accepted and treated as
+ plain + and * at the beginning of the string; update test_3rd reindops
+ (check posix regex)
+ - decide whether regex should support binary; related tests to update:
+ test_3rd jared, regx8bit
+ - posix FS point 3. requires that ^ work in FS; check test_3rd uparrfs
+ - introduce a new symtab flag for remembering builtin vars and arrays;
+ when -W dumpsym, do not dump these unless verbose
+ - introduce a -W dumpallsym (for verbose symdumps)
+ - write regression test for flush() (it used to pass the wrong pointer)
+
+4. lib fineprint
+ - expose mawk_append_input_file
+
+5. optimization
+ - mawk_find_bi_ptr(): use perfect hash instead of linear search
+ does it really matter?
+ - peephole:
+ - 'print "a", a, "b", b, "c", c' results in push/cat pairs;
+ make the parser emit a lot of pushes and replace cat with catN
+ - 'expr = 1' will add a pop; there should be another type of assignment
+ that doesn't push anything
+ - replace tail recursion (call+ret)
+
diff --git a/doc/autotools.html b/doc/autotools.html
new file mode 100644
index 0000000..be8a329
--- /dev/null
+++ b/doc/autotools.html
@@ -0,0 +1,22 @@
+
+
+ Why not using autotools
+
+I believe autotools is the wrong designed and poor implementation, partly
+trying to solve the wrong problem. The fix for this is a better design.
+For example scconfig - which is the system that configures libmawk. It
+can do everything it needs to do (yes, including cross compilation).
+
+Mawk generally dosn't require too many special things and should compile
+fine on POSIX systems. If it doesn't compile for you, you have the following
+choices:
+
+ - you can contact me, report bugs, so I can fix scconfig
+
- you can bypass scconfig and manually create Makefile.conf
+ and conf.h for your system yourself (won't help much in
+ fixing scconfig, tho)
+
- you can create and maintain your own autotools version (but official
+ libmawk will not include support for autotools)
+
+
+
diff --git a/doc/developer/README b/doc/developer/README
new file mode 100644
index 0000000..1433e34
--- /dev/null
+++ b/doc/developer/README
@@ -0,0 +1,6 @@
+This directory hosts documentation about libmawk internals.
+
+These files are useful for developers of libmawk and in some specail
+case for application developers if they plan to use advanced features
+of libmawk.
+
diff --git a/doc/developer/array_vs_func.txt b/doc/developer/array_vs_func.txt
new file mode 100644
index 0000000..77d5000
--- /dev/null
+++ b/doc/developer/array_vs_func.txt
@@ -0,0 +1,114 @@
+API: Virtual array vs. function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Introduction: virtual arrays
+
+Libmawk implements support for virtual arrays: arrays that do not simply
+store data in a hash. This feature is designed for maximal flexibility:
+awk arrays have a set of hooks (function pointers) and whenever the
+bytecode interpreter has to read, list or modify an array, it will
+call the hook functions. The original array implementation is just
+a set of hooks simply storing data without side effects. The ENVIRON[]
+array is set up with another set of hooks that syncs the environment
+with awk array (and calls the original hooks for the actual data storage).
+
+
+2. API: arrays are function calls at the end of the day
+
+This also means that an application developer has two alternative paths
+to provide bindings to the application code:
+ - explicit function calls
+ - implicit function calls through an awk array
+
+For example if the application wants to expose direct I/O port access,
+it may:
+ - implement functions io_out(port, value) and io_in(port)
+ - implement an array IO[port]
+
+However, having side effects is usually not desirable. An awk program that
+uses functions not declared in the awk code makes it clear that these functions
+are external, whereas an array may be just a global awk variable, side effects
+are not obvious.
+
+
+3. how to chose?
+
+While technically the two ways of the API design are equivalent in the sense
+that both end up in function calls, there are always considerations that
+may make one better than the other for a specific application. Below are
+the pros and cons:
+
+Function calls, pro:
+ - easy to recognize external calls in the awk code
+ - can implement much more than lookup, set and list
+ - may provide faster listing
+
+Function calls, con:
+ - longer awk source
+ - when there are multiple different implementations under different names,
+ an awk function that needs to operate on all may need to get a function
+ name prefix and do dynamic function calls that makes the awk
+ code look more complicated
+
+Array, pro:
+ - simple and short awk code, especially for listing ("for in")
+ - when there are multiple different implementation, passing one of them
+ to an awk function (by reference) hides the differences, keeping awk
+ function code simple
+ - the whole set of data can be handled together: output of split, generic
+ array print or load functions
+
+Array, con:
+ - hidden side effects (risks awk source readability)
+ - always have to implement lookup, set and list
+
+NOTE: a major advantage of arrays is listing (the "for in" construction).
+In mawk this is implemented by saving a list of all indices that exist at
+the time of entering the loop. While most of the time this means duplicating
+string references only, it may still be slow and may take considerable amount
+of memory if the array is large. What counts as large may vary, but generally
+a 10^6 indices may cause memory allocations in the megabyte range already.
+
+In practice the following considerations could easily decide the question:
+ A. if there are more operations than lookup/set/list, use functions
+ B. if only set/get is required, check if listing looks useful or not;
+ if useful, go for arrays (where listing is done via "for in");
+ similarly: if there's a function based API for set/get/list, reconsider
+ using an array instead of custom listing. Unless arrays are large!
+ C. are generic array functions useful in common applications? If so, arrays
+ may be better. Generic array functions include:
+ - awk function that prints all indices of an array
+ - awk function doing some complex lookup, e.g. regex search on all indices
+ - loading the array from a string using split(); useful when indices
+ are small integers, typically counting from 0 or 1
+ D. would there be alternative implementations and generic awk functions
+ operating on them depending on their arguments? If so, arrays may be better
+ as they can be passed as reference
+
+4. examples
+
+According to these, the I/O port example is better implemented with functions
+as arrays offer no benefit in any of the above points:
+ A. has only set and get, at this point arrays are as good as functions
+ B. "for in" listing is not a typical application: array has no benefit
+ C. printing all ports is rarely useful; complex lookups are not common;
+ loading I/O space with split() is not useful;
+ no obvious example of generic array code being useful on an IO array:
+ array has no benefit
+ D. having multiple alternative I/O spaces and passing one of these
+ to an awk function as array is not probable: array has no benefit
+
+An example where array is more suitable is an interface for network interfaces
+(ifconfig): arrays NIC_IP[], NIC_NETMASK[], NIC_MTU[], etc, indexed by
+the name of the nic:
+ A. has lookup, set and list; array is as good as functions
+ B. it's a reasonable application to list all interfaces: "for in" is useful,
+ array looks better
+ C. printing all interfaces makes sense; complex lookup
+ (e.g. "all alias interfaces") makes sense; loading the array may make
+ sense (e.g. restoring network settings); split wouldn't work, tho; array
+ looks better
+ D. no obvious alternative arrays to be passed in arg; array has no benefit
+
+In point A. and D. arrays are not better than functions (but not worse either),
+but in B. and C. arrays definitely have an advantage for this app.
diff --git a/doc/developer/vm.html b/doc/developer/vm.html
new file mode 100644
index 0000000..2ec18af
--- /dev/null
+++ b/doc/developer/vm.html
@@ -0,0 +1,231 @@
+
+
+ The Virtual Machine (VM)
+
+The VM lives in execute.c function mawk_execute_().
+
+In principle it takes an array of instructions (INST), executing them
+one by one, operating on the current evaluation
+stack (or stack for short). Most of the execution really goes in
+order as the compiler prepares up everything to achieve linearity.
+This makes the execution loop relatively simple and efficient: it is
+just a large while(dont_need_to_exit) execute_next_instruction;.
+The actual instruction execution is a real large switch on the instruction
+opcode.
+
+An usual example on how this is implemented in practice can be
+obtained by lmawk -Wdump -f test.awk on some simple arithmetic
+script:
+
+ awk | asm (VM instructions)
+ |
---|
+
+
+BEGIN { a = 3 + 4 * 5 }
+
+ |
+
+BEGIN
+000 pusha a
+002 pushd 3
+004 pushd 4
+006 pushd 5
+008 mul
+009 add
+010 assign
+011 pop
+012 exit0
+
+ |
+
+First the lvalue (target variable, left side) of the assignment is pushed,
+then the expression (right side). The stack is, from top to down: {5, 4, 3, a}.
+The top of the stack is 5, the second element is 4 by the time mul runs.
+Mul will replace these two elements by 20, decreasing the stack size by
+one, leaving the result on the top. Next add does a similar job,
+replacing the top two items of the stack {20, 3} with their sum, 23. At
+the end assign runs on the stack {23, a}, removing both items, copying
+the value 23 to the global variable a. At the end it also puts
+the result on the top of the stack, leaving the stack as {23} - this is
+the result (output) of the assignment operation. Since the script doesn't
+need to use the result, it runs a pop that removes and discards
+the top item, leaving the stack empty. Since the script didn't have main
+or END parts, the script can quit at this point, executing the exit0
+instruction (exiting with value 0 - the implicit exit).
+
+NOTE: currently there's absolutely no optimization in the parser: everything
+is calculated as written in the script and some values are saved just to be
+discarded by the next instruction.
+
+An interesting and important feature of execute_() is that it can save all
+states and return to the caller at any point of the execution, i.e.
+between any two instruction in the code. It can also resume execution from
+the next instruction. This provides the host application full control over
+scheduling the script, while the script can be built of sequential, blocking
+instructions.
+
+
Jumps and conditions
+
+There are a few instructions that have to break linear execution flow, tho:
+
+ - if() needs to make a jump depending on some condition
+
- conditional code (e.g. /^A/ { ... }) is the same
+
- range conditional code (e.g. /^A/,/^B/ { ... }) is still the same with some extra complications (internal state tracking whether the script is within the range)
+
- loops obviously need to do a similar conditional jump back
+
- function calls have to temporarily suspend executing the current code and jump to the function code from which a ret or ret0 instruction jumps back
+
+
+Some of the above are implemented using conditional and unconditional jumps
+to direct addresses (first column on the asm). For example a simple if
+is compiled to contain 2 jumps:
+
+ awk | asm (VM instructions)
+ |
---|
+
+
+BEGIN {
+ if (bool)
+ a = 6
+ else
+ a = 7
+}
+
+ |
+
+BEGIN
+000 pushi bool
+002 jz 012
+004 pusha a
+006 pushd 6
+008 assign
+009 pop
+010 jmp 018
+012 pusha a
+014 pushd 7
+016 assign
+017 pop
+018 exit0
+
+ |
+
+The first one is a conditional jump, "jump if [top of the stack is] zero"
+(jz) - this makes the VM jump to the else branch at address 10.
+The then branch ends in an unconditional jump to the next instruction
+after the if (which is the implicit exit in this example), bypassing
+the code of the else branch.
+
+A jump is carried out by a simple modification of the "next instruction"
+pointer before running the next iteration of the execution loop.
+
+
Recursion: function calls
+
+A slightly more complicated mechanism is used when jumps are of recursive
+nature: the code has to jump to somewhere to do some work and then
+return here and continue execution from the next instruction. A typical
+example on this is executing user functions.
+
+The original mawk implementation simply called mawk_execute_() recursively.
+This meant the C compiler took care of saving all internal states on the
+C stack for the detour. However, this wouldn't allow the code to be suspended
+during such detour as it would be problematic to rebuild the C stack on a resume.
+
+Thus libmawk's mawk_execute_() does not recurse on C level but on VM level.
+For example when a function is called (using the call instruction):
+
+ - 1. prepare the eval stack for the function (as mawk did): push arguments and local variables
+
- 2. push execution state on top of the eval stack
+
- 3. unconditional jump to the function body
+
+
+Upon a ret instruction from the function:
+
+ - 1. save and remove the return value from the top of the stack
+
- 2. restore previous execution state from the top of the stack
+
- 3. remove all arguments and local variables from the stack (remove the frame)
+
- 4. push the return value on top of the stack
+
- 5. continue execution normally - because step 2., this will run the instruction right after the call
+
+
+ additional cases of recursion
+A range pattern is recursive as well: it needs to evaluate one or two pattern
+matching before it decides whether to execute the action and/or update
+the state. The range check starts with instruction _RANGE_CHK which
+encodes expression code offsets and state in the next few instruction slots. It
+recurses to evaluate expressions which are terminated by the _RANGE_STOP command.
+Entering an expression evaluation is similar to a function call while
+_RANGE_STOP is very similar to a ret.
+
+ deep recursion
+ At any time the eval stack has to have enough space after sp for
+ evaluation the longest awk expression. Any user function recursion will
+ bump sp leaving less room for expressions and further recursion. Relocating
+ the stack (with a realloc()) is not a good idea as there might be cell
+ pointers pointing to stack elements all around.
+
+ Instead, mawk limits expression length in compile time to a fixed
+ maximum. If entering a new function would not leave at list this
+ amount of eval stack above sp, "deep recursion" is performed. This
+ starts by allocating an entire new stack for the call. Call stacking
+ saves enough pointers so that the code can switch back to the
+ previous stack easily. The allocation is done using zmalloc(), the overhead
+ is minimal. Since the original stack/stacks is/are kept intact, any
+ pointer stays valid. sp points into the new stack block and will increase
+ there until another deep recursion.
+
+ This wastes some stack space on the old stack (potentially max expression
+ length minus one slot) but guarantees that:
+ - checks and special things need to be done only at entering/leaving functions
+ - even that happens rarely as a stack block is large enough to host
+ many functions besides the longest expression
+ - the stack can grow as big as it likes to, without having to allocate
+ one large block of memory
+ - all allocation is done from normal instance memory - allocation limit,
+ and auto cleanup at the end are granted
+
+ Resuming execute_()
+
+Since the far most common thing in an embedded setup is to resume a
+code interrupted by execution limit or a blocking getline, mawk_execute_() is
+doing that by default. The top few slots in the eval stack is always a full
+state dump, the same thing used in recursion. Entering mawk_execute_() pops this
+section and initializes all internal states from it. When execution needs
+to be interrupted, mawk_execute_() saves internal states onto the top
+of the stack.
+
+ Entering execute_() (fresh start)
+
+Entering in run state involves setting up internal states pointing to
+the beginning of the code in question, pushing these states on top of
+the stack and calling mawk_execute_() which will "resume" from these states.
+Similar thing happens when the application calls an awk function.
+
+It may be that the execution is interrupted in the middle of running of
+a large block of code, for example in BEGIN. The top of the stack holds
+the current execution state so that mawk_execute_() will be able to
+continue execution. The application may decide to run an awk
+function before resuming the code: this operation would push a new
+set of execution state on top of the stack and call mawk_execute_().
+When the current state finishes at the _RET instruction, mawk_execute_()
+would take the next frame from the stack and would automatically resume
+execution of the interrupted BEGIN block. This would cause the return value
+of the function to be lost and would attempt to resume BEGIN as a side effect
+of the function call!
+
+To avoid such confusion, any new enter to mawk_execute_() is required to
+push two sets of states: an EXEST_EXIT and the actual state it wants
+to "resume" at (start execution at). When mawk_execute_() hits the _RET
+instruction in the above example, it does pop the next frame, but that
+frame would be the EXEST_EXIT which would cause it to interrupt immediately.
+This leaves the stack exactly as it looked like before the function call,
+and the application later may decide to resume execution.
+
+Fresh start entries:
+
+ - running BEGIN (last stage of initialization)
+
- running main on new input, in non-interrupted state
+
- running END (first stage of uninitialization)
+
- an awk function is called by the application
+
+
+
+
diff --git a/doc/example.7libmawk.html b/doc/example.7libmawk.html
new file mode 100644
index 0000000..a4ab012
--- /dev/null
+++ b/doc/example.7libmawk.html
@@ -0,0 +1,1102 @@
+
+
+
+
+
+
+
+
+
+EXAMPLE
+
+
+
+
+EXAMPLE
+
+NAME
+SYNOPSIS
+DESCRIPTION
+Example application
+
+
+
+
+NAME
+
+
+
+
+libmawk example
+− how to use the library
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+DESCRIPTION
+
+
+
+
+Libmawk is a
+library that lets applications to embed awk scripts using
+the code of the popular implementation mawk. The
+normal process is to call libmawk_initialize() to set up a
+new mawk context (with script(s) loaded), then in the main
+loop feed it using libmawk_append_input(). For "out of
+band" communication, the program may also call
+functions implemented in awk and read (or modify) global
+variables of the awk script. The hos tapplication usally
+will also bind some of its functions to the context using
+libmawk_register_function, which allows the awk script to
+call the host applicaiton’s functions directly as they
+were awk builtins or user defined functions. After the main
+loop, the application destroys the context freeing up all
+memory allocated for the script(s).
+
+One context is
+for one awk program. One awk program may consist of multiple
+script files (just as with command line awk, with multiple
+-f filename arguments). Libmawk is instance safe, the host
+application may create multiple instances of contexts with
+the same or with different set of awk scripts loaded. These
+contexts are totally separate, no variables, functions or
+any sort of states are shared. However, the host application
+may provide means of communication between those scripts by
+custom functions or by copying variable contents between
+them.
+
+Example application
+
+
+
+
+The following
+example application creates a single context to demonstrate
+all the above mentioned functionality.
+#include <stdio.h>
+#include <libmawk.h>
+
+CELL
+*blobb(mawk_state_t *context, CELL * sp, int a_args)
+{
+
+
+
+ |
+ |
+
+
+
+ int n; |
+
+ |
+
+ |
+ |
+
+
+
+ char buff[64]; |
+
+ |
+
+ |
+ |
+
+
+
+ /* do something - print BLOBB and all arguments */ |
+
+ |
+
+ |
+ |
+
+
+
+ printf("BLOBB! "); |
+
+ |
+
+ |
+ |
+
+
+
+ for(n = a_args-1; n >= 0; n--) |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ printf("%d=’%s’ ", n,
+libmawk_print_cell((sp-n), buff, sizeof(buff))); |
+
+ |
+ |
+
+
+
+ printf("0); |
+
+ |
+
+ |
+ |
+
+
+
+ /* restore the stack (remove all arguments) */ |
+
+ |
+
+ |
+ |
+
+
+
+ sp = sp - a_args; |
+
+ |
+
+ |
+ |
+
+
+
+ /* set a return value (find out where the return value
+is on the stack, |
+
+ |
+
+ |
+ |
+
+
+
+ using libmawk_stackret()) */ |
+
+ |
+
+ |
+ |
+
+
+
+ libmawk_set_cell(context, libmawk_stackret(sp),
+’f’, (double)1234); |
+
+ |
+
+ |
+ |
+
+
+
+ /* return the new stack pointer - should be the one that
+it was before |
+
+ |
+
+ |
+ |
+
+
+
+ arguments had been pushed on the stack */ |
+
+ |
+
+ |
+ |
+
+
+
+ return sp; |
+
+ |
+
+
+}
+
+int main(int
+argc, char **argv)
+{
+
+
+
+ |
+
+
+
+ mawk_state_t *m; |
+
+ |
+
+
+
+ CELL ret, arrv, *vr; |
+
+ |
+
+
+
+ char buff[64]; |
+
+
+/* the simpler
+way is:
+
+
+
+ |
+
+
+
+ m = libmawk_initialize(argc, argv); |
+
+ |
+
+
+
+ However, if the application wants to change the
+environment before |
+
+ |
+
+
+
+ executing BEGIN, the following, 3 stage initialization
+should be done: |
+
+
+*/
+
+
+
+ |
+ |
+
+
+
+ m = libmawk_initialize_stage1(); /* set up m */ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ custom_array_init(m); /* set up a new builtin array with
+side effects, before parsing scripts */ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ m = libmawk_initialize_stage2(m, argc, argv); /* parse
+args loads the script(s) */ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ m = libmawk_initialize_stage3(m); /* execute BEGIN {}
+*/ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ ret.type = C_NOINIT; |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ if (m != NULL) { |
+ |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ /* test function call */ |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ if (libmawk_call_function(m, "func", &ret,
+"dfs", (int)42, (double)1.234, (char *)"test
+string.") == 0) { |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ printf("Return value of func is ’%s’0,
+libmawk_print_cell(&ret, buff, sizeof(buff))); |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ libmawk_cell_destroy(m, &ret); |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ } |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ printf("Failed to call func()0); |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ /* this is the same function call with a different
+syntax */ |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ { |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ int i = 42; |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ double d = 1.234; |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ char *s = "test string."; |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ void *args[] = {&i, &d, s}; |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ if (libmawk_call_functionp(m, "func",
+&ret, "dfs", args) != 0) { |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ printf("Return value of func is ’%s’0,
+libmawk_print_cell(&ret, buff, sizeof(buff))); |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ libmawk_cell_destroy(m, &ret); |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ } |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ } |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ /* register a C function (resolved runtime) */ |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ if (libmawk_register_function(m, "blobb",
+blobb) != 0) { |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ fprintf(stderr, "ERROR: Unable to register function
+blobb0); |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ } |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ /* run some data */ |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ libmawk_append_input(m, "This is a0ultiline test
+input0ut in the artificial input buffer.0); |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ libmawk_run_main(m); |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ /* print var: scalar */ |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ vr = libmawk_get_var(m, "var"); |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ if (vr != NULL) |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ printf("Variable var = ’%s’0,
+libmawk_print_cell(vr, buff, sizeof(buff))); |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ else |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ printf("No such variable |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ /* print var: array */ |
+ |
+
+ |
+
+
+#warning TODO
+
+
+
+ |
+
+ |
+
+ |
+
+
+
+ arrv.type = C_NOINIT; |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ if (libmawk_get_array_at(m, "arr",
+"hello", &arrv, 0) > 0) |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ printf("Variable arr[ |
+
+ |
+
+ |
+
+ |
+
+
+
+ else |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ printf("No such variable |
+
+
+#warning todo:
+array set
+
+
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ /* set var: array; change the existing
+(arr["hello"]) */ |
+
+ |
+
+ |
+ |
+
+
+
+ // |
+
+ |
+
+
+
+ if (ret != NULL) |
+
+ |
+
+ |
+ |
+
+
+
+ // |
+
+ |
+
+ |
+
+
+
+ libmawk_set_cell(m, ret, ’s’,
+"WORLD"); |
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ /* set var: array; create a new index */ |
+
+ |
+
+
+#warning todo: array set
+
+
+
+ |
+ |
+
+
+
+ // |
+
+ |
+
+
+
+ libmawk_get_array_at(m, "arr",
+"bye", &arrv, 1); |
+
+ |
+ |
+
+
+
+ // |
+
+ |
+
+
+
+ libmawk_set_cell(m, ret, ’s’,
+"universe"); |
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ /* run some more data */ |
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ libmawk_append_input(m, "Second0); |
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ libmawk_append_input(m, "run.0); |
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ libmawk_run_main(m); |
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ custom_array_print(m); |
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ /* run end */ |
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ libmawk_uninitialize(m); |
+
+ |
+ |
+ |
+
+
+
+ } |
+
+ |
+
+ |
+ |
+ |
+
+
+
+ else { |
+
+ |
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ printf("Init failed.0); |
+
+ |
+ |
+ |
+
+
+
+ } |
+
+ |
+
+ |
+ |
+ |
+
+
+
+ printf("END0); |
+
+ |
+
+ |
+ |
+ |
+
+
+
+ return 0 ; |
+
+ |
+
+
+}
+
+
+
diff --git a/doc/execution.html b/doc/execution.html
new file mode 100644
index 0000000..33fbd42
--- /dev/null
+++ b/doc/execution.html
@@ -0,0 +1,155 @@
+
+
+ awk script execution
+Libmawk runs the bytecode of the script in a virtual machine.
+The VM takes the bytecode as a series of instructions that operate on data
+stored on the execution stack and in global states of the script instance
+(libmawk_state_t).
+
+There is only one thing at a time an instance is doing, however that
+one thing may be interrupted and resumed any time. This one thing is
+always one of these:
+
+ - running BEGIN
+
- running END
+
- running main (the stdin read and pattern match rules)
+
- running an awk function called from the application
+
- nothing: empty stack; before starting or after finishing any of the above activities
+
+
+BEGIN, END, main and awk functions are the four entry points of executing
+the script. Normally BEGIN is run right after setting up the script, then
+main is run on all input and END is run when the script exits, right
+before uninitialization of the script instance. This is a 1:1 copy
+of the standard way awk works. The fourth, calling awk functions directly
+from the application is an extra entry point.
+
+The script is not doing anything unless the application commands it to. Some
+of the simplified API does this automatically, but the raw API (staged
+init/uninit) always lets the app decide when to start running the script.
+This document calls an execution transaction when the application calls
+the API to start running a script.
+
+Any execution related call is non-blocking, thus it will return after a
+reasonable time spent running the script and will never stuck running
+an infinite loop. When such an API call returns, the return value
+is a mawk_exec_result_t that indicates the reason of the return:
+
+ - 1. if the script attempts to read a file/pipe that would block, it interrupts execution and returns (with MAWK_EXER_INT_READ) instead
+
- 2. when reaching the run limit (a given number of instructions has been executed), the script is interrupted (return value is MAWK_EXER_INT_RUNLIMIT)
+
- 3. the script may finish executing the current execution transaction (MAWK_EXER_DONE or MAWK_EXER_FUNCRET)
+
- 4. the script may decide to exit
+
+
+Execution transaction are collected on the evaluation stack. If
+the application requests an execution and the API call returns before
+finishing, the transaction is still active. The application is
+free to initiate a new execution transaction, without
+first finishing the previous one. However, the VM will always resume and
+progress running the most recent execution transaction. This means
+execution transactions are sort of nested. When the top, most recent
+execution transaction finishes (return 3), the next resume request
+will go on with the previous transaction.
+
+Note, however, that the script has global states. The most obvious state
+is the exit state: if the script runs exit(), it will discard all open
+transactions. For example consider a script that is running a main part
+processing the input. When the application is in this phase, the topmost
+transaction is always a "running main" transaction that returned
+previously because there was no more input to be processed. If the
+application calls an awk function that decides to do an exit(), that will
+affect not only discard the function transaction but the pending "running main"
+transaction as well. Whenever the application requests a resume on
+the code, that will start running the END section.
+
+
+
return path 1.: MAWK_EXER_INT_READ
+Assume stdin is a FIFO between the application and the script. The
+first script tries to prefix each line:
+
+{
+ print "prefix:", $0
+}
+
+The application fills the FIFO with some data that may contain one or
+more full records, potentially ending with a partial (unterminated)
+record. If the application resumes the script, it will try to
+read all full records and process them. It will interrupt
+execution and return MAWK_EXER_INT_READ the first time a full
+record can't be read. This always happens "before the {}".
+
+A slightly more complicated script prefixes odd and even lines differently:
+
+{
+ print "odd:", $0
+ getline
+ print "even:", $0
+}
+
+This script may return with MAWK_EXER_INT_READ either before {}
+or in the getline instruction. This means the application should not
+assume that when main returns it was not in the middle of such
+a block. (In the actual VM main starts with an implicit getline so
+there's no difference between the two cases).
+
+A similar situation is when an awk function is executing getline on a FIFO:
+the application that calls the function shall not expect that the function
+finishes and produces its return value in the initial execution request.
+Instead the request will create a new execution transaction and
+multiple resume calls may be needed until the function actually returns.
+
+Obviously the application shall fill the FIFO while executing resumes:
+if there is no new input and the script is waiting for new input, the
+resume call will return immediately.
+
+
+
return path 2.: MAWK_EXER_INT_RUNLIMIT
+When runlimit is set the VM returns after executing a certain amount of
+instructions. The application shall decide whether to simply resume or
+to stop executing the script.
+
+This feature is useful when the application is implemented as a single
+threaded async loop: running a blocking script would block the entire loop.
+
+
+
return path 3.: MAWK_EXER_DONE or MAWK_EXER_FUNCRET
+When BEGIN or main or END finishes MAWK_EXER_DONE is returned. When
+an awk function called by the application returns, MAWK_EXER_FUNCRET
+is returned and the retc argument is filled with the return value cell
+(which may be of cell type NOINIT in case there was no return value).
+
+The application shall never expect the initial call that
+created the new execution transaction will end in
+MAWK_EXER_DONE or MAWK_EXER_FUNCRET; when it does not,
+a subsequent resume call eventually will.
+
+ return path 4.: MAWK_EXER_EXIT
+Similar to MAWK_EXER_DONE, but means the script called exit.
+This is legal from even an awk function call, in which case the
+function will never have a return value (as the code can not be resumed
+any more). Normal awk rules apply: calling exit() from BEGIN or main
+(or subsequent functions, called by the script or the application) puts
+the script in exit mode and next resume will run END. Calling exit from
+END will exit immediately leaving the script in non-runnable state.
+
+
+ conclusion: script execution
+It is safe to assume calling any script execution will return with
+a conclusion if, and only if:
+
+ - the script is not allowed to use getline on FIFOs (which can not be guaranteed!) or there are no FIFOs or otherwise blocking input (i.e. all files are plain files); and
+
- there is no run limit configured
+
+
+Since these are not guaranteed in most common use cases, the code should prepare
+to:
+
+ - start executing the code and check if it's already finished
+
- resume until it actually does finish
+
- if the script returned MAWK_EXER_INT_READ: fill FIFOs or if that's not possible stop resuming as there won't be any progress
+
+
+Thus following c-pseudo-code should be used:
+
+TODO
+
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 0000000..68bad5f
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,28 @@
+
+
+ Table Of Contents
+ Manual pages
+
+ Design decisions
+
+
+
+
diff --git a/doc/libmawk_append_input.3libmawk.html b/doc/libmawk_append_input.3libmawk.html
new file mode 100644
index 0000000..c354eda
--- /dev/null
+++ b/doc/libmawk_append_input.3libmawk.html
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_APPEND_INPUT
+
+
+
+
+LIBMAWK_APPEND_INPUT
+
+NAME
+SYNOPSIS
+DESCRIPTION
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_append_input
+− append a string to an input buffer
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+void
+libmawk_append_input(mawk_state_t *m, const
+char *input_str);
+
+void
+libmawk_append_ninput(mawk_state_t *m, const
+char *input, intlen);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_append_input() and
+libmawk_append_ninput() functions allow the
+application to fill the input buffer of a libmawk context.
+No record separator is appended, only the bytes donated by
+input_str or input, thus it is possible to append partial
+records. Appending to the input doesn’t have the side
+effect of any script being run. There may be multiple
+libmawk_append_input() calls before a call to
+libmawk_run_main(). The latter all is used to let the script
+process the input buffer.
+
+The only
+difference between the two calls are the input format:
+libmawk_append_input() expects a nul-terminated
+string, whereas libmawk_append_ninput() takes an
+arbitrary binary data and its length.
+
+Argument m is a
+libmawk context previously returned by libmawk_initialize()
+or libmawk_initialize_stage3().
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_initialize(3libmawk),
+libmawk_run_main(3libmawk).
+
+
+
diff --git a/doc/libmawk_call_function.3libmawk.html b/doc/libmawk_call_function.3libmawk.html
new file mode 100644
index 0000000..f82ac85
--- /dev/null
+++ b/doc/libmawk_call_function.3libmawk.html
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_CALL_FUNCTION
+
+
+
+
+LIBMAWK_CALL_FUNCTION
+
+NAME
+SYNOPSIS
+DESCRIPTION
+RETURN VALUE
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_call_function
+− call an user defined (script) function
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+int
+libmawk_call_function(mawk_state_t *MAWK,
+const char *fname, CELL *res,
+const char *argtpes, ...);
+int libmawk_call_functionp(mawk_state_t *MAWK,
+const char *fname, CELL *res,
+const char *argtpes, void **args);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_call_function() function looks up an user
+defined awk function called fname , fills the stack
+with arguments converted from the varargs and calls the
+function. The libmawk_call_functionp() performs the
+same action but avoids using vararg by requiring an array of
+generic pointers to the function arguments.
+
+Argtype is a
+zero terminated string for both functions, each character
+corresponding to an argument. Type characters are described
+in libmawk_set_cell() manual page.
+
+If res is
+non-NULL, it is cell_destroyed (regardless of errors) and
+the return value of the user function is copied into it. The
+caller shall run libmawk_cell_destroy on it.
+
+Argument m is a
+libmawk context previously returned by libmawk_initialize()
+or libmawk_initialize_stage3().
+
+RETURN VALUE
+
+
+
+
+A pointer to
+the cell returned by the user function. The cell
+returnedmust be destroyed by the application using
+libmawk_cell_destroy.
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_initialize(3libmawk),
+libmawk_cell_destroy(3libmawk),
+libmawk_set_cell(3libmawk),
+
+
+
diff --git a/doc/libmawk_cell_destroy.3libmawk.html b/doc/libmawk_cell_destroy.3libmawk.html
new file mode 100644
index 0000000..448912a
--- /dev/null
+++ b/doc/libmawk_cell_destroy.3libmawk.html
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_CELL_DESTROY
+
+
+
+
+LIBMAWK_CELL_DESTROY
+
+NAME
+SYNOPSIS
+DESCRIPTION
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_cell_destroy
+− free all memory associated with a cell
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+void
+libmawk_cell_destroy(mawk_state_t *m, CELL
+*c);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_cell_destroy() function frees all memory
+allocated to store a mawk cell. It is useful with some of
+the libmawk calls that return a newly allocated cell, such
+as the libmawk_call_function() call.
+
+Argument m is a
+libmawk context previously returned by libmawk_initialize()
+or libmawk_initialize_stage3().
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_initialize(3libmawk),
+libmawk_call_function(3libmawk).
+
+
+
diff --git a/doc/libmawk_get_var.3libmawk.html b/doc/libmawk_get_var.3libmawk.html
new file mode 100644
index 0000000..edf6a82
--- /dev/null
+++ b/doc/libmawk_get_var.3libmawk.html
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_GET_VAR
+
+
+
+
+LIBMAWK_GET_VAR
+
+NAME
+SYNOPSIS
+DESCRIPTION
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_get_var
+− returns a pointer to a mawk variable
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+CELL
+*libmawk_get_var(mawk_state_t *m, const char
+*vname);
+int libmawk_get_array_at(mawk_state_t *m,
+const char *arr_name,
+const char *idx, const char
+*res, int alloc);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_get_var() function returns a pointer to a
+mawk cell that represents the global variable with name
+passed in vname in the given context. The returned
+CELL should never be free’d or destroyed. Function
+libmawk_print_cell may be used for converting the cell to
+string. The caller should not change the type of cell but is
+free to change the value.
+
+Function
+libmawk_get_array_at() performs the same operation
+for an element of an array. -1 is returned if
+arr_name is not an array or upon an error. If
+idx is not an existing index in the array it is
+allocated if alloc is non-zero. If res is not
+NULL, it is destroyed (regardless of the return value) and
+if the index exists (or is created by the call), is loaded
+with the value. The caller needs to destroy res after
+use. Since res is destroyed when non-NULL, it must be
+a valid cell with valid type.
+
+Argument m is a
+libmawk context previously returned by libmawk_initialize()
+or libmawk_initialize_stage3().
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_initialize(3libmawk),
+libmawk_call_function(3libmawk),
+libmawk_print_cell(3libmawk).
+
+
+
diff --git a/doc/libmawk_initialize.3libmawk.html b/doc/libmawk_initialize.3libmawk.html
new file mode 100644
index 0000000..ab75f64
--- /dev/null
+++ b/doc/libmawk_initialize.3libmawk.html
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_INITIALIZE
+
+
+
+
+LIBMAWK_INITIALIZE
+
+NAME
+SYNOPSIS
+DESCRIPTION
+RETURN VALUE
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_initialize
+− create a new libmawk context
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+
+mawk_state_t
+*libmawk_initialize(int s, char
+*argv[]);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_initialize() function returns a pointer to a
+newly created libmawk context. Any amount of libmawk
+contexts can live in parallel in an application. Arguments
+are the same as for a command line mawk session. Scripts are
+loaded (either from command line or from files using -f),
+variables are set (with -v), special options are set (with
+-W), etc.
+
+RETURN VALUE
+
+
+
+
+A pointer to a
+new libmawk context or NULL on error.
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_uninitialize(3libmawk),
+
+
+
diff --git a/doc/libmawk_initialize_stage.3libmawk.html b/doc/libmawk_initialize_stage.3libmawk.html
new file mode 100644
index 0000000..5009553
--- /dev/null
+++ b/doc/libmawk_initialize_stage.3libmawk.html
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_INITIALIZE_STAGE
+
+
+
+
+LIBMAWK_INITIALIZE_STAGE
+
+NAME
+SYNOPSIS
+DESCRIPTION
+RETURN VALUE
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_initialize_stage*
+− create a new libmawk context in 3 stages
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+
+mawk_state_t
+*libmawk_initialize_stage1(void);
+
+
+mawk_state_t
+*libmawk_initialize_stage2(mawk_state_t *
+m,int s
+,char*"argv[]);
+
+
+mawk_state_t
+*libmawk_initialize_stage3(mawk_state_t *
+m);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_initialize_stage*() functions together do the
+same as libmawk_initialize() but allows the application to
+take actions between different stages.
+libmawk_initialize_stage1() returns a pointer to a
+newly created libmawk context. Any amount of libmawk
+contexts can live in parallel in an application.
+
+
+libmawk_initialize_stage2()
+can be called after a succesful stage1 call.
+Stage2 is responsible for processing the command line
+arguments and loading any script.
+
+Arguments are
+the same as for a command line mawk session. Scripts are
+loaded (either from command line or from files using -f),
+variables are set (with -v), special options are set (with
+-W), etc. Unlike with libmawk_initialize(), the application
+may decide not to provide any script at this stage. All
+command line arguments are processed.
+
+The most common
+case is that the application calls stage1 with no script,
+then already having a context makes some manipulations on it
+(for example registers some C functions that would be
+already called in the BEGIN part of the script that will be
+later loaded). Optionally before calling stage2 the
+application loads the actual script(s) using
+mawk_append_input_file().
+
+
+libmawk_initialize_stage3()
+is called as a final step of the three-stage initialization
+process. Stage3 is responsible for running all the BEGIN
+parts of all scripts loaded at stage1 or stage2. It is
+useful to have stage3 in a separate call to allow
+applications to manipulate the context right before
+initializing the scripts.
+
+Stage2 gets the
+pointer returned by stage1 and stage3 gets the pointer
+returned by stage2. Subsequent calls to libmawk functions
+should get the pointer returned by stage3.
+
+RETURN VALUE
+
+
+
+
+At stage 1 a
+pointer to a new libmawk context or NULL on error.
+Subsequent stages will return the same pointer or NULL on
+error.
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_uninitialize(3libmawk),
+mawk_append_input_file(3libmawk).
+
+
+
diff --git a/doc/libmawk_register_function.3libmawk.html b/doc/libmawk_register_function.3libmawk.html
new file mode 100644
index 0000000..333beed
--- /dev/null
+++ b/doc/libmawk_register_function.3libmawk.html
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_REGISTER_FUNCTION
+
+
+
+
+LIBMAWK_REGISTER_FUNCTION
+
+NAME
+SYNOPSIS
+DESCRIPTION
+RETURN VALUE
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_register_function
+− registers a C function with a callback
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+typedef CELL
+*libmawk_c_function(mawk_state_t *m, CELL
+*sp, int a_args);
+int libmawk_register_function(mawk_state_t
+*MAWK, const char *fname,
+libmawk_c_function *callback);
+CELL *libmawk_stackret(CELL
+*original_sp);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_register_function() call registers an user
+defined function donated by the host application in a mawk
+context so that it acts exactly like user defined functions
+in written in awk. The name of the new function is given in
+fname and should not match any of the user defined
+function names in the awk script.
+
+When the user
+function is called back, argument sp is the stack
+pointer and a_args holds the number of arguments. The
+user function is responsible for managing the stack: it
+should pop all arguments before returning.
+
+The user
+function should also generate a return value, which is done
+by calling libmawk_set_cell() on the stack slot returned by
+libmawk_stackret. Libmawk_stackret should be called with the
+modified sp after popping all arguments.
+
+Argument m is a
+libmawk context previously returned by libmawk_initialize()
+or libmawk_initialize_stage3().
+
+For more
+information about user function callbacks, especially on
+stack handling, see manual page example(3libmawk).
+
+RETURN VALUE
+
+
+
+
+The user
+function should return the stack pointer after popping all
+arguments.
+
+The
+libmawk_register_function call returns 0 on success.
+
+Call
+libmawk_stackret returns a stack pointer to the slot where
+the user function should store its return value.
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_initialize(3libmawk),
+libmawk_set_cell(3libmawk),
+libmawk_print_cell(3libmawk).
+
+
+
diff --git a/doc/libmawk_run_main.3libmawk.html b/doc/libmawk_run_main.3libmawk.html
new file mode 100644
index 0000000..95f433f
--- /dev/null
+++ b/doc/libmawk_run_main.3libmawk.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_RUN_MAIN
+
+
+
+
+LIBMAWK_RUN_MAIN
+
+NAME
+SYNOPSIS
+DESCRIPTION
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_run_main
+− run main parts of a script
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+void
+libmawk_run_main(mawk_state_t *m);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_run_main() attempts to take and parse the
+next input record and runs all main parts of the script that
+matches. If there are multiple full records in the input
+buffer, the process repeats until the buffer becomes empty
+or contains a partial record. If there is no full record in
+the buffer, the call returns with nothing done. The call
+itself never blocks, but the script may. The input buffer
+may be filled using the libmawk_append_input() call.
+
+Argument m is a
+libmawk context previously returned by libmawk_initialize()
+or libmawk_initialize_stage3().
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_initialize(3libmawk),
+libmawk_append_input(3libmawk),
+
+
+
diff --git a/doc/libmawk_set_cell.3libmawk.html b/doc/libmawk_set_cell.3libmawk.html
new file mode 100644
index 0000000..16aa778
--- /dev/null
+++ b/doc/libmawk_set_cell.3libmawk.html
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_SET_CELL
+
+
+
+
+LIBMAWK_SET_CELL
+
+NAME
+SYNOPSIS
+DESCRIPTION
+RETURN VALUE
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_set_cell
+− set the value of a mawk cell.
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+CELL
+*libmawk_set_cell(mawk_state_t *m, CELL
+*cell, const
+charargtype,...);
+CELL *libmawk_set_cellp(mawk_state_t *m, CELL
+*cell, const charargtype, void
+*argp);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_set_cell() function modifies the value of a
+mawk cell (variable). Argumetn argtype is a format character
+that describes the type of the payload (accessed trough
+vararg).
+
+The
+libmawk_set_cellp() function performs the same action
+but accepts a generic pointer to the payload.
+
+Format
+character is one of the followings:
+’d’ for int payload
+’f’ for double payload
+’s’ for (zero terminated) char * payload.
+
+Argument m is a libmawk context
+previously returned by libmawk_initialize() or
+libmawk_initialize_stage3().
+
+RETURN VALUE
+
+
+
+
+A pointer to
+the cell modified.
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_initialize(3libmawk),
+libmawk_get_var(3libmawk).
+
+
+
diff --git a/doc/libmawk_uninitialize.3libmawk.html b/doc/libmawk_uninitialize.3libmawk.html
new file mode 100644
index 0000000..0c6d251
--- /dev/null
+++ b/doc/libmawk_uninitialize.3libmawk.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+LIBMAWK_UNINITIALIZE
+
+
+
+
+LIBMAWK_UNINITIALIZE
+
+NAME
+SYNOPSIS
+DESCRIPTION
+SEE ALSO
+
+
+
+
+NAME
+
+
+
+
+
+libmawk_uninitialize
+− destroy a libmawk context
+
+SYNOPSIS
+
+
+
+
+#include
+<libmawk.h>
+
+void
+libmawk_uninitialize(mawk_state_t *
+m);
+
+DESCRIPTION
+
+
+
+
+The
+libmawk_uninitialize() function destroys a context
+previously created using libmawk_initialize() or
+libmawk_initialize_stage1() call. It unloads scripts and
+frees all memory of the context.
+
+SEE ALSO
+
+
+
+
+
+libmawk_initialize_stage(3libmawk),
+libmawk_initialize(3libmawk),
+
+
+
diff --git a/doc/lmawk.1.html b/doc/lmawk.1.html
new file mode 100644
index 0000000..e5b7a2b
--- /dev/null
+++ b/doc/lmawk.1.html
@@ -0,0 +1,2998 @@
+
+
+
+
+
+
+
+
+
+LMAWK
+
+
+
+
+LMAWK
+
+NAME
+SYNOPSIS
+DESCRIPTION
+OPTIONS
+THE AWK LANGUAGE
+EXAMPLES
+COMPATIBILITY ISSUES
+SEE ALSO
+BUGS
+AUTHOR
+
+
+
+
+NAME
+
+
+
+
+lmawk −
+pattern scanning and text processing language
+
+SYNOPSIS
+
+
+
+
+lmawk
+[−W option] [−F
+value] [−v var=value]
+[−−] ’program text’ [file ...]
+
+lmawk [−W option] [−F
+value] [−v var=value]
+[−f program-file] [−−] [file
+...]
+
+DESCRIPTION
+
+
+
+
+lmawk is
+an interpreter for the AWK Programming Language derived from
+mawk. The AWK language is useful for manipulation of data
+files, text retrieval and processing, and for prototyping
+and experimenting with algorithms. lmawk is a new
+awk meaning it implements the AWK language as defined in
+Aho, Kernighan and Weinberger, The AWK Programming
+Language, Addison-Wesley Publishing, 1988. (Hereafter
+referred to as the AWK book.) mawk conforms to the
+Posix 1003.2 (draft 11.3) definition of the AWK language
+which contains a few features not described in the AWK book,
+and mawk provides a small number of extensions.
+
+An AWK program
+is a sequence of pattern {action} pairs and function
+definitions. Short programs are entered on the command line
+usually enclosed in ’ ’ to avoid shell
+interpretation. Longer programs can be read in from a file
+with the −f option. Data input is read from the list
+of files on the command line or from standard input when the
+list is empty. The input is broken into records as
+determined by the record separator variable, RS.
+Initially, RS = "\n" and records are
+synonymous with lines. Each record is compared against each
+pattern and if it matches, the program text for
+{action} is executed.
+
+OPTIONS
+
+
+
+
+
+
+ |
+
+
+
+ −F value |
+ |
+
+
+
+ sets the field separator,
+FS, to value. |
+
+ |
+
+
+
+ −f file |
+ |
+
+
+
+ Program text is read from file instead of from
+the command line. Multiple −f options are
+allowed. As a libmawk extension, if file name starts with
+plus (’+’), it is not loaded if the same file
+has been loaded already by a previous -f or include from any
+of the scripts already loaded. |
+
+ |
+
+
+
+ −b file |
+ |
+
+
+
+ Program bytecode is read from file . Multiple
+−b options are allowed. Bytecode can be
+generated using -Wcompile. Libmawk may refuse to load
+bytecode generated on a different system if byte order, type
+sizes or dump version differs. |
+
+ |
+
+
+
+ −v var=value |
+ |
+
+
+
+ assigns value to program variable var. |
+
+ |
+
+
+
+ −− |
+ |
+
+
+
+ indicates the unambiguous end of options. |
+
+
+The above
+options will be available with any Posix compatible
+implementation of AWK, and implementation specific options
+are prefaced with −W. lmawk provides
+six:
+
+
+
+ |
+
+
+
+ −W version |
+ |
+
+
+
+ lmawk writes its version
+and copyright to stdout and compiled limits to stderr and
+exits 0. |
+
+ |
+
+
+
+ −W debug |
+ |
+
+
+
+ include location info in the compiled code; location
+information is visible in the dump and when debugging
+libmawk. |
+
+ |
+
+
+
+ −W dump |
+ |
+
+
+
+ writes an assembler like listing of the internal
+representation of the program to stdout and exits 0 (on
+successful compilation). |
+
+ |
+
+
+
+ −W dumpsym |
+ |
+
+
+
+ writes a list of global symbols to stdout and exits 0
+(on successful compilation). |
+
+ |
+
+
+
+ −W compile |
+ |
+
+
+
+ writes a binary dump of the bytecode to stdout. This
+bytecode can be loaded using the −b switch. |
+
+ |
+
+
+
+ −W interactive |
+ |
+
+
+
+ sets unbuffered writes to stdout and line buffered reads
+from stdin. Records from stdin are lines regardless of the
+value of RS. |
+
+ |
+
+
+
+ −W maxmem=num |
+ |
+
+
+
+ limit dynamic memory allocation during compilation and
+execution to num bytes and exit with
+out-of-the-memory error if more memory is to be allocated.
+Optional suffixes are k for kilobyte and m for megabyte. 0
+means unlimited, which is also the default. |
+
+ |
+
+
+
+ −W exec file |
+ |
+
+
+
+ Program text is read from file and this is the
+last option. Useful on systems that support the #!
+"magic number" convention for executable
+scripts. |
+
+ |
+
+
+
+ −W sprintf=num |
+ |
+
+
+
+ adjusts the size of lmawk’s internal
+sprintf buffer to num bytes. More than rare use of
+this option indicates lmawk should be recompiled. |
+
+ |
+
+
+
+ −W posix_space |
+ |
+
+
+
+ forces lmawk not to consider ’\n’ to
+be space. |
+
+
+The short forms
+−W[vdiesp] are recognized and on some systems
+−We is mandatory to avoid command line length
+limitations.
+
+THE AWK LANGUAGE
+
+
+
+
+1. Program
+structure
+An AWK program is a sequence of pattern {action}
+pairs and user function definitions.
+
+A pattern can
+be:
+
+BEGIN
+END
+expression
+expression , expression
+
+One, but not
+both, of pattern {action} can be omitted. If
+{action} is omitted it is implicitly { print }. If
+pattern is omitted, then it is implicitly matched.
+BEGIN and END patterns require an action.
+
+Statements are
+terminated by newlines, semi-colons or both. Groups of
+statements such as actions or loop bodies are blocked via {
+... } as in C. The last statement in a block doesn’t
+need a terminator. Blank lines have no meaning; an empty
+statement is terminated with a semi-colon. Long statements
+can be continued with a backslash, \. A statement can be
+broken without a backslash after a comma, left brace,
+&&, ||, do, else, the right
+parenthesis of an if, while or for
+statement, and the right parenthesis of a function
+definition. A comment starts with # and extends to, but does
+not include the end of line.
+
+The following
+statements control program flow inside blocks.
+
+if (
+expr ) statement
+
+if (
+expr ) statement else
+statement
+
+while (
+expr ) statement
+
+do
+statement while ( expr )
+
+for (
+opt_expr ; opt_expr ; opt_expr )
+statement
+
+for (
+var in array ) statement
+
+
+continue
+
+
+break
+
+2. Data
+types, conversion and comparison
+There are two basic data types, numeric and string. Numeric
+constants can be integer like −2, decimal like 1.08,
+or in scientific notation like −1.1e4 or .28E−3.
+All numbers are represented internally and all computations
+are done in floating point arithmetic. So for example, the
+expression 0.2e2 == 20 is true and true is represented as
+1.0.
+
+String
+constants are enclosed in double quotes.
+
+"This is a
+string with a newline at the end.\n"
+
+Strings can be continued across
+a line by escaping (\) the newline. The following escape
+sequences are recognized.
+
+
+
+ |
+
+ |
+
+
+
+ \\ |
+
+ |
+
+
+
+ \ |
+
+ |
+
+ |
+
+
+
+ \" |
+
+ |
+
+
+
+ " |
+
+ |
+
+ |
+
+
+
+ \a |
+
+ |
+
+
+
+ alert, ascii 7 |
+
+ |
+
+ |
+
+
+
+ \b |
+
+ |
+
+
+
+ backspace, ascii 8 |
+
+ |
+
+ |
+
+
+
+ \t |
+
+ |
+
+
+
+ tab, ascii 9 |
+
+ |
+
+ |
+
+
+
+ \n |
+
+ |
+
+
+
+ newline, ascii 10 |
+
+ |
+
+ |
+
+
+
+ \v |
+
+ |
+
+
+
+ vertical tab, ascii 11 |
+
+ |
+
+ |
+
+
+
+ \f |
+
+ |
+
+
+
+ formfeed, ascii 12 |
+
+ |
+
+ |
+
+
+
+ \r |
+
+ |
+
+
+
+ carriage return, ascii 13 |
+
+ |
+
+ |
+
+
+
+ \ddd |
+
+ |
+
+
+
+ 1, 2 or 3 octal digits for ascii ddd |
+
+ |
+
+ |
+
+
+
+ \xhh |
+
+ |
+
+
+
+ 1 or 2 hex digits for ascii hh |
+
+
+If you escape
+any other character \c, you get \c, i.e., lmawk
+ignores the escape.
+
+There are
+really three basic data types; the third is number and
+string which has both a numeric value and a string value
+at the same time. User defined variables come into existence
+when first referenced and are initialized to null, a
+number and string value which has numeric value 0 and string
+value "". Non-trivial number and string typed data
+come from input and are typically stored in fields. (See
+section 4).
+
+The type of an
+expression is determined by its context and automatic type
+conversion occurs if needed. For example, to evaluate the
+statements
+
+
+
+ |
+
+
+
+ y = x + 2 ; z = x "hello" |
+
+
+The value
+stored in variable y will be typed numeric. If x is not
+numeric, the value read from x is converted to numeric
+before it is added to 2 and stored in y. The value stored in
+variable z will be typed string, and the value of x will be
+converted to string if necessary and concatenated with
+"hello". (Of course, the value and type stored in
+x is not changed by any conversions.) A string expression is
+converted to numeric using its longest numeric prefix as
+with atof(3). A numeric expression is converted to
+string by replacing expr with sprintf(CONVFMT,
+expr), unless expr can be represented on the
+host machine as an exact integer then it is converted to
+sprintf("%d", expr).
+Sprintf() is an AWK built-in that duplicates the
+functionality of sprintf(3), and CONVFMT is a
+built-in variable used for internal conversion from number
+to string and initialized to "%.6g". Explicit type
+conversions can be forced, expr "" is
+string and expr+0 is numeric.
+
+To evaluate,
+expr1 rel-op expr2, if both operands
+are numeric or number and string then the comparison is
+numeric; if both operands are string the comparison is
+string; if one operand is string, the non-string operand is
+converted and the comparison is string. The result is
+numeric, 1 or 0.
+
+In boolean
+contexts such as, if ( expr )
+statement, a string expression evaluates true if and
+only if it is not the empty string ""; numeric
+values if and only if not numerically zero.
+
+3. Regular
+expressions
+In the AWK language, records, fields and strings are often
+tested for matching a regular expression. Regular
+expressions are enclosed in slashes, and
+
+
+
+is an AWK
+expression that evaluates to 1 if expr
+"matches" r, which means a substring of
+expr is in the set of strings defined by r.
+With no match the expression evaluates to 0; replacing ~
+with the "not match" operator, !~ , reverses the
+meaning. As pattern-action pairs,
+
+
+
+ |
+
+
+
+ /r/ { action } and $0 ~ /r/
+{ action } |
+
+
+are the same,
+and for each input record that matches r,
+action is executed. In fact, /r/ is an AWK
+expression that is equivalent to ($0 ~ /r/)
+anywhere except when on the right side of a match operator
+or passed as an argument to a built-in function that expects
+a regular expression argument.
+
+AWK uses
+extended regular expressions as with egrep(1). The
+regular expression metacharacters, i.e., those with special
+meaning in regular expressions are
+
+
+
+ |
+
+
+
+ ^ $ . [ ] | ( ) * + ? |
+
+
+Regular
+expressions are built up from characters as follows:
+
+
+
+ |
+
+
+
+ c |
+ |
+
+
+
+ matches any non-metacharacter
+c. |
+
+ |
+
+
+
+ \c |
+ |
+
+
+
+ matches a character defined by the same escape sequences
+used in string constants or the literal character c
+if \c is not an escape sequence. |
+
+ |
+
+
+
+ . |
+ |
+
+
+
+ matches any character (including newline). |
+
+ |
+
+
+
+ ^ |
+ |
+
+
+
+ matches the front of a string. |
+
+ |
+
+
+
+ $ |
+ |
+
+
+
+ matches the back of a string. |
+
+ |
+
+
+
+ [c1c2c3...] |
+ |
+
+
+
+ matches any character in the class c1c2c3... . An
+interval of characters is denoted c1−c2 inside a class
+[...]. |
+
+ |
+
+
+
+ [^c1c2c3...] |
+ |
+
+
+
+ matches any character not in the class c1c2c3... |
+
+
+Regular
+expressions are built up from other regular expressions as
+follows:
+
+
+
+ |
+
+
+
+ r1r2 |
+ |
+
+
+
+ matches r1 followed
+immediately by r2 (concatenation). |
+
+ |
+
+
+
+ r1 | r2 |
+ |
+
+
+
+ matches r1 or r2 (alternation). |
+
+ |
+
+
+
+ r* |
+ |
+
+
+
+ matches r repeated zero or more times. |
+
+ |
+
+
+
+ r+ |
+ |
+
+
+
+ matches r repeated one or more times. |
+
+ |
+
+
+
+ r? |
+ |
+
+
+
+ matches r zero or once. |
+
+ |
+
+
+
+ (r) |
+ |
+
+
+
+ matches r, providing grouping. |
+
+
+The increasing
+precedence of operators is alternation, concatenation and
+unary (*, + or ?).
+
+For
+example,
+
+
+/^[_a−zA-Z][_a−zA−Z0−9]*$/
+and
+
+
+
+ |
+
+
+
+
+ /^[−+]?([0−9]+\.?|\.[0−9])[0−9]*([eE][−+]?[0−9]+)?$/ |
+
+
+are matched by
+AWK identifiers and AWK numeric constants respectively. Note
+that . has to be escaped to be recognized as a decimal
+point, and that metacharacters are not special inside
+character classes.
+
+Any expression
+can be used on the right hand side of the ~ or !~ operators
+or passed to a built-in that expects a regular expression.
+If needed, it is converted to string, and then interpreted
+as a regular expression. For example,
+
+
+
+ |
+
+
+
+ BEGIN { identifier =
+"[_a−zA−Z][_a−zA−Z0−9]*"
+} |
+
+ |
+
+
+
+ $0 ~ "^" identifier |
+
+
+prints all
+lines that start with an AWK identifier.
+
+lmawk
+recognizes the empty regular expression, //, which matches
+the empty string and hence is matched by any string at the
+front, back and between every character. For example,
+
+
+
+ |
+
+
+
+ echo abc | lmawk { gsub(//, "X") ; print } |
+
+ |
+
+
+
+ XaXbXcX |
+
+
+4. Records
+and fields
+Records are read in one at a time, and stored in the
+field variable $0. The record is split into
+fields which are stored in $1, $2, ...,
+$NF. The built-in variable NF is set to the
+number of fields, and NR and FNR are
+incremented by 1. Fields above $NF are set to
+"".
+
+Assignment to
+$0 causes the fields and NF to be recomputed.
+Assignment to NF or to a field causes $0 to be
+reconstructed by concatenating the $i’s
+separated by OFS. Assignment to a field with index
+greater than NF, increases NF and causes
+$0 to be reconstructed.
+
+Data input
+stored in fields is string, unless the entire field has
+numeric form and then the type is number and string. For
+example,
+
+
+
+ |
+
+
+
+ echo 24 24E | |
+
+ |
+
+
+
+ lmawk ’{ print($1>100, $1>"100",
+$2>100, $2>"100") }’ |
+
+ |
+
+
+
+ 0 1 1 1 |
+
+
+$0 and
+$2 are string and $1 is number and string. The
+first comparison is numeric, the second is string, the third
+is string (100 is converted to "100"), and the
+last is string.
+
+5.
+Expressions and operators
+The expression syntax is similar to C. Primary expressions
+are numeric constants, string constants, variables, fields,
+arrays and function calls. The identifier for a variable,
+array or function can be a sequence of letters, digits and
+underscores, that does not start with a digit. Variables are
+not declared; they exist when first referenced and are
+initialized to null.
+
+New expressions
+are composed with the following operators in order of
+increasing precedence.
+
+
+
+ |
+ |
+ |
+
+
+
+ assignment |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ = += −= *= /= %= ^= |
+
+ |
+ |
+ |
+
+
+
+ conditional |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ ? : |
+
+ |
+ |
+ |
+
+
+
+ logical or |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ || |
+
+ |
+ |
+ |
+
+
+
+ logical and |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ && |
+
+ |
+ |
+ |
+
+
+
+ array membership |
+ |
+ |
+
+
+
+ in |
+
+ |
+ |
+ |
+
+
+
+ matching |
+ |
+
+ |
+
+
+
+ ~ !~ |
+
+ |
+ |
+ |
+
+
+
+ relational |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ < > <= >= == != |
+
+ |
+ |
+ |
+
+
+
+ concatenation |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ (no explicit operator) |
+
+ |
+ |
+ |
+
+
+
+ add ops |
+ |
+
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ + − |
+
+ |
+ |
+ |
+
+
+
+ mul ops |
+ |
+
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ * / % |
+
+ |
+ |
+ |
+
+
+
+ unary |
+ |
+
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ + − |
+
+ |
+ |
+ |
+
+
+
+ logical not |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ ! |
+
+ |
+ |
+ |
+
+
+
+ exponentiation |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ ^ |
+
+ |
+ |
+ |
+
+
+
+ inc and dec |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ ++ −− (both post and pre) |
+
+ |
+ |
+ |
+
+
+
+ field |
+ |
+
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ $ |
+
+
+Assignment,
+conditional and exponentiation associate right to left; the
+other operators associate left to right. Any expression can
+be parenthesized.
+
+6.
+Arrays
+Awk provides one-dimensional arrays. Array elements are
+expressed as array[expr]. Expr is
+internally converted to string type, so, for example, A[1]
+and A["1"] are the same element and the actual
+index is "1". Arrays indexed by strings are called
+associative arrays. Initially an array is empty; elements
+exist when first accessed. An expression, expr
+in array evaluates to 1 if
+array[expr] exists, else to 0.
+
+There is a form
+of the for statement that loops over each index of an
+array.
+
+
+
+ |
+
+
+
+ for ( var in array )
+statement |
+
+
+sets var
+to each index of array and executes statement.
+The order that var transverses the indices of
+array is not defined.
+
+The statement,
+delete array[expr], causes
+array[expr] not to exist. lmawk
+supports an extension, delete array, which
+deletes all elements of array.
+
+
+Multidimensional
+arrays are synthesized with concatenation using the built-in
+variable SUBSEP.
+array[expr1,expr2] is equivalent to
+array[expr1 SUBSEP expr2].
+Testing for a multidimensional element uses a parenthesized
+index, such as
+
+
+
+ |
+
+
+
+ if ( (i, j) in A ) print A[i, j] |
+
+
+7.
+Builtin-variables
+The following variables are built-in and initialized before
+program execution.
+
+
+
+ |
+
+
+
+ ARGC |
+ |
+
+
+
+ number of command line
+arguments. |
+
+ |
+
+
+
+ ARGV |
+ |
+
+
+
+ array of command line arguments, 0..ARGC-1. |
+
+ |
+
+
+
+ CONVFMT |
+ |
+
+
+
+ format for internal conversion of numbers to string,
+initially = "%.6g". |
+
+ |
+
+
+
+ ENVIRON |
+ |
+
+
+
+ array indexed by environment variables. An environment
+string, var=value is stored as
+ENVIRON[var] = value. |
+
+ |
+
+
+
+ FILENAME |
+ |
+
+
+
+ name of the current input file. |
+
+ |
+
+
+
+ FNR |
+ |
+
+
+
+ current record number in FILENAME. |
+
+ |
+
+
+
+ FS |
+ |
+
+
+
+ splits records into fields as a regular expression. |
+
+ |
+
+
+
+ NF |
+ |
+
+
+
+ number of fields in the current record. |
+
+ |
+
+
+
+ NR |
+ |
+
+
+
+ current record number in the total input stream. |
+
+ |
+
+
+
+ OFMT |
+ |
+
+
+
+ format for printing numbers; initially =
+"%.6g". |
+
+ |
+
+
+
+ OFS |
+ |
+
+
+
+ inserted between fields on output, initially = "
+". |
+
+ |
+
+
+
+ ORS |
+ |
+
+
+
+ terminates each record on output, initially =
+"\n". |
+
+ |
+
+
+
+ RLENGTH |
+ |
+
+
+
+ length set by the last call to the built-in function,
+match(). |
+
+ |
+
+
+
+ RS |
+ |
+
+
+
+ input record separator, initially = "\n". |
+
+ |
+
+
+
+ RSTART |
+ |
+
+
+
+ index set by the last call to match(). |
+
+ |
+
+
+
+ SUBSEP |
+ |
+
+
+
+ used to build multiple array subscripts, initially =
+"\034". |
+
+ |
+
+
+
+ ERRNO |
+ |
+
+
+
+ misc built-in functions (libmawk extensions) use this
+variable to rerport error. All extension calls will set this
+variable before returning, therefor ERRNO holds the result
+of the last call. An empty string value means no error.
+Error messages are formatted in a way that the first word is
+an unique integer, followed by a human readable error
+message from the second word. int(ERRNO) can be used to
+acquire the error code, which then can be used as a
+secondary output from the extension function. For example,
+an awk program can use valueof() to determine if a global
+symbol exists and is a function or a variable or anything
+else. |
+
+ |
+
+
+
+ LIBPATH |
+ |
+
+
+
+ is a semicolon separated list of search paths. When
+loading an awk script by file name (-f command line argument
+or include from another awk script) these paths are inserted
+before the file name, in order, one by one, until the first
+path that allows opening the file. An empty path is
+equivalent to the current working directory. LIBPATH can be
+modified from the command line using -v, as arguments are
+scanned before loading the scripts. Setting LIBPATH to empty
+string results in the original behaviour of mawk. LIBPATH is
+ignored for script file names starting with slash
+(’/’) as those are assumed to be absolute
+paths. |
+
+
+8. Built-in
+functions
+String functions
+
+gsub(r,s,t)
+gsub(r,s)
+
+Global substitution, every
+match of regular expression r in variable t is
+replaced by string s. The number of replacements is
+returned. If t is omitted, $0 is used. An
+& in the replacement string s is replaced by the
+matched substring of t. \& and \\ put literal
+& and \, respectively, in the replacement string.
+
+index(s,t)
+
+If t is a substring of
+s, then the position where t starts is
+returned, else 0 is returned. The first character of
+s is in position 1.
+
+length(s)
+
+Returns the length of string
+s.
+
+match(s,r)
+
+Returns the index of the first
+longest match of regular expression r in string
+s. Returns 0 if no match. As a side effect,
+RSTART is set to the return value. RLENGTH is
+set to the length of the match or −1 if no match. If
+the empty string is matched, RLENGTH is set to 0, and
+1 is returned if the match is at the front, and
+length(s)+1 is returned if the match is at the
+back.
+
+split(s,A,r)
+split(s,A)
+
+String s is split into
+fields by regular expression r and the fields are
+loaded into array A. The number of fields is
+returned. See section 11 below for more detail. If r
+is omitted, FS is used.
+
+
+sprintf(format,expr-list)
+
+Returns a string constructed
+from expr-list according to format. See the
+description of printf() below.
+
+sub(r,s,t)
+sub(r,s)
+
+Single substitution, same as
+gsub() except at most one substitution.
+
+substr(s,i,n)
+substr(s,i)
+
+Returns the substring of string
+s, starting at index i, of length n. If
+n is omitted, the suffix of s, starting at
+i is returned.
+
+tolower(s)
+
+Returns a copy of s with
+all upper case characters converted to lower case.
+
+toupper(s)
+
+Returns a copy of s with
+all lower case characters converted to upper case.
+
+Arithmetic
+functions
+
+
+atan2(y,x)
+Arctan of y/x between -PI and PI.
+
+
+
+ |
+ |
+ |
+
+
+
+ cos(x) |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+ Cosine function, x in radians. |
+
+ |
+ |
+ |
+
+
+
+ exp(x) |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+ Exponential function. |
+
+ |
+ |
+ |
+
+
+
+ int(x) |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+ Returns x truncated towards zero. |
+
+ |
+ |
+ |
+
+
+
+ log(x) |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+ Natural logarithm. |
+
+ |
+ |
+ |
+
+
+
+ rand() |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+ Returns a random number between zero and one. |
+
+ |
+ |
+ |
+
+
+
+ sin(x) |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+ Sine function, x in radians. |
+
+ |
+ |
+ |
+
+
+
+ sqrt(x) |
+ |
+
+ |
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+ Returns square root of x. |
+
+
+srand(expr) srand()
+
+Seeds the random number
+generator, using the clock if expr is omitted, and
+returns the value of the previous seed. lmawk seeds
+the random number generator from the clock at startup so
+there is no real need to call srand(). Srand(expr) is
+useful for repeating pseudo random sequences.
+
+Misc functions
+(libmawk extensions)
+
+
+call(fname,arg1,arg2,...)
+
+Call awk function fname
+with the supplied arguments. If the call fails, empty value,
+else the return value of the callee is returned. Built-in
+variable ERRNO is always set.
+
+
+acall(fname,arrname)
+
+Call awk function fname
+with arguments supplied in array named arrname (both
+arguments are strings naming an existing object). The array
+should be indexed from 1. Number of arguments is determined
+by looking for the first empty (non-existing) index in the
+array. If the call fails, empty value, else the return value
+of the callee is returned. Built-in variable ERRNO is always
+set.
+
+valueof(vname
+[,idx])
+
+Return the value of variable
+fname; if the variable is an array, return the
+element indexed by idx (which must be present in this
+case). If index is not present or is empty (""),
+the variable is expected to be scalar. Built-in variable
+ERRNO is always set. NOTE: valueof() has access to the
+global symbol table only. It will fail to resolve anything
+else than global objects; most notably it will fail on local
+variables, $ arguments and on most of the built-in
+variables.
+
+9. Input and
+output
+There are two output statements, print and
+printf.
+
+
+
+ |
+
+
+
+ print |
+ |
+
+
+
+ writes $0 ORS to standard output. |
+
+ |
+
+
+print expr1,
+expr2, ..., exprn
+
+writes expr1 OFS
+expr2 OFS ... exprn ORS to
+standard output. Numeric expressions are converted to string
+with OFMT.
+
+printf format,
+expr-list
+
+duplicates the printf C library
+function writing to standard output. The complete ANSI C
+format specifications are recognized with conversions %c,
+%d, %e, %E, %f, %g, %G, %i, %o, %s, %u, %x, %X and %%, and
+conversion qualifiers h and l.
+
+The argument
+list to print or printf can optionally be enclosed in
+parentheses. Print formats numbers using OFMT or
+"%d" for exact integers. "%c" with a
+numeric argument prints the corresponding 8 bit character,
+with a string argument it prints the first character of the
+string. The output of print and printf can be redirected to
+a file or command by appending > file, >>
+file or | command to the end of the print
+statement. Redirection opens file or command
+only once, subsequent redirections append to the already
+open stream. By convention, lmawk associates the
+filename "/dev/stderr" with stderr which allows
+print and printf to be redirected to stderr. lmawk
+also associates "−" and
+"/dev/stdout" with stdin and stdout which allows
+these streams to be passed to functions. Opening /dev/fd/N
+will do an fdopen() on file descriptor N, where N is an
+integer - this is a libmawk extension. If any of the /dev
+heuristics needs to be bypassed (i.e. the script wants to
+open the real /dev/stdout or the real /dev/fd/5), the
+leading slash should be doubled (e.g. //dev/fd/5).
+
+The input
+function getline has the following variations.
+
+getline
+
+reads into $0, updates
+the fields, NF, NR and FNR.
+
+getline < file
+
+reads into $0 from
+file, updates the fields and NF.
+
+getline var
+
+reads the next record into
+var, updates NR and FNR.
+
+getline var <
+file
+
+reads the next record of
+file into var.
+
+command | getline
+
+pipes a record from
+command into $0 and updates the fields and
+NF.
+
+command | getline
+var
+
+pipes a record from
+command into var.
+
+Getline returns
+0 on end-of-file, −1 on error, otherwise 1.
+
+Commands on the
+end of pipes are executed by /bin/sh.
+
+The function
+close(expr) closes the file or pipe associated
+with expr. Close returns 0 if expr is an open
+file, the exit status if expr is a piped command, and
+−1 otherwise. Close is used to reread a file or
+command, make sure the other end of an output pipe is
+finished or conserve file resources.
+
+The function
+fflush(expr) flushes the output file or pipe
+associated with expr. Fflush returns 0 if expr
+is an open output stream else −1. Fflush without an
+argument flushes stdout. Fflush with an empty argument
+("") flushes all open output.
+
+The function
+system(expr) uses /bin/sh to execute
+expr and returns the exit status of the command
+expr. Changes made to the ENVIRON array are
+not passed to commands executed with system or
+pipes.
+
+10. User
+defined functions
+The syntax for a user defined function is
+
+
+function
+name( args ) { statements }
+
+The function
+body can contain a return statement
+
+return
+opt_expr
+
+A return
+statement is not required. Function calls may be nested or
+recursive. Functions are passed expressions by value and
+arrays by reference. Extra arguments serve as local
+variables and are initialized to null. For example,
+csplit(s,A) puts each character of s into
+array A and returns the length of s.
+
+function
+csplit(s, A, n, i)
+
+
+
+ |
+ |
+
+
+
+ { |
+
+ |
+ |
+
+
+
+ n = length(s) |
+
+ |
+ |
+
+
+
+ for( i = 1 ; i <= n ; i++ ) A[i] = substr(s, i,
+1) |
+
+ |
+ |
+
+
+
+ return n |
+
+ |
+ |
+
+
+
+ } |
+
+
+Putting extra
+space between passed arguments and local variables is
+conventional. Functions can be referenced before they are
+defined, but the function name and the ’(’ of
+the arguments must touch to avoid confusion with
+concatenation.
+
+11.
+Splitting strings, records and files
+Awk programs use the same algorithm to split strings into
+arrays with split(), and records into fields on FS.
+lmawk uses essentially the same algorithm to split
+files into records on RS.
+
+
+Split(expr,A,sep)
+works as follows:
+
+
+
+ |
+
+
+
+ (1) |
+ |
+
+
+
+ If sep is omitted, it is replaced by FS.
+Sep can be an expression or regular expression. If it
+is an expression of non-string type, it is converted to
+string. |
+
+ |
+
+
+
+ (2) |
+ |
+
+
+
+ If sep = " " (a single space), then
+<SPACE> is trimmed from the front and back of
+expr, and sep becomes <SPACE>.
+lmawk defines <SPACE> as the regular expression
+/[ \t\n]+/. Otherwise sep is treated as a
+regular expression, except that meta-characters are ignored
+for a string of length 1, e.g., split(x, A, "*")
+and split(x, A, /\*/) are the same. |
+
+ |
+
+
+
+ (3) |
+ |
+
+
+
+ If expr is not string, it is converted to string.
+If expr is then the empty string "",
+split() returns 0 and A is set empty. Otherwise, all
+non-overlapping, non-null and longest matches of sep
+in expr, separate expr into fields which are
+loaded into A. The fields are placed in A[1], A[2],
+..., A[n] and split() returns n, the number of fields which
+is the number of matches plus one. Data placed in A
+that looks numeric is typed number and string. |
+
+
+Splitting
+records into fields works the same except the pieces are
+loaded into $1, $2,..., $NF. If
+$0 is empty, NF is set to 0 and all $i
+to "".
+
+lmawk
+splits files into records by the same algorithm, but with
+the slight difference that RS is really a terminator
+instead of a separator. (ORS is really a terminator
+too).
+
+E.g., if
+FS = ":+" and $0 = "a::b:"
+, then NF = 3 and $1 = "a",
+$2 = "b" and $3 = "", but
+if "a::b:" is the contents of an input file and
+RS = ":+", then there are two records
+"a" and "b".
+
+RS =
+" " is not special.
+
+If FS =
+"", then lmawk breaks the record into
+individual characters, and, similarly,
+split(s,A,"") places the individual
+characters of s into A.
+
+12.
+Multi-line records
+Since lmawk interprets RS as a regular
+expression, multi-line records are easy. Setting RS =
+"\n\n+", makes one or more blank lines separate
+records. If FS = " " (the default), then
+single newlines, by the rules for <SPACE> above,
+become space and single newlines are field separators.
+
+For example, if
+a file is "a b\nc\n\n", RS =
+"\n\n+" and FS = " ", then
+there is one record "a b\nc" with three
+fields "a", "b" and "c".
+Changing FS = "\n", gives two fields
+"a b" and "c"; changing FS =
+"", gives one field identical to the record.
+
+If you want
+lines with spaces or tabs to be considered blank, set
+RS = "\n([ \t]*\n)+". For
+compatibility with other awks, setting RS =
+"" has the same effect as if blank lines are
+stripped from the front and back of files and then records
+are determined as if RS = "\n\n+". Posix
+requires that "\n" always separates records when
+RS = "" regardless of the value of
+FS. lmawk does not support this convention,
+because defining "\n" as <SPACE> makes it
+unnecessary.
+
+Most of the
+time when you change RS for multi-line records, you
+will also want to change ORS to "\n\n" so
+the record spacing is preserved on output.
+
+13. Program
+execution
+This section describes the order of program execution. First
+ARGC is set to the total number of command line
+arguments passed to the execution phase of the program.
+ARGV[0] is set the name of the AWK interpreter and
+ARGV[1] ... ARGV[ARGC-1] holds the remaining
+command line arguments exclusive of options and program
+source. For example with
+
+lmawk −f
+prog v=1 A t=hello B
+
+ARGC = 5
+with ARGV[0] = "lmawk", ARGV[1] =
+"v=1", ARGV[2] = "A",
+ARGV[3] = "t=hello" and ARGV[4] =
+"B".
+
+Next, each
+BEGIN block is executed in order. If the program
+consists entirely of BEGIN blocks, then execution
+terminates, else an input stream is opened and execution
+continues. If ARGC equals 1, the input stream is set
+to stdin, else the command line arguments ARGV[1] ...
+ARGV[ARGC-1] are examined for a file argument.
+
+The command
+line arguments divide into three sets: file arguments,
+assignment arguments and empty strings "". An
+assignment has the form var=string. When an
+ARGV[i] is examined as a possible file argument, if
+it is empty it is skipped; if it is an assignment argument,
+the assignment to var takes place and i skips
+to the next argument; else ARGV[i] is opened for
+input. If it fails to open, execution terminates with exit
+code 2. If no command line argument is a file argument, then
+input comes from stdin. Getline in a BEGIN action
+opens input. "−" as a file argument denotes
+stdin.
+
+Once an input
+stream is open, each input record is tested against each
+pattern, and if it matches, the associated
+action is executed. An expression pattern matches if
+it is boolean true (see the end of section 2). A
+BEGIN pattern matches before any input has been read,
+and an END pattern matches after all input has been
+read. A range pattern, expr1,expr2 , matches
+every record between the match of expr1 and the match
+expr2 inclusively.
+
+When end of
+file occurs on the input stream, the remaining command line
+arguments are examined for a file argument, and if there is
+one it is opened, else the END pattern is
+considered matched and all END actions are
+executed.
+
+In the example,
+the assignment v=1 takes place after the BEGIN
+actions are executed, and the data placed in v is
+typed number and string. Input is then read from file A. On
+end of file A, t is set to the string "hello", and
+B is opened for input. On end of file B, the END
+actions are executed.
+
+Program flow at
+the pattern {action} level can be changed with
+the
+
+
+next
+
+
+
+ |
+
+
+
+ exit opt_expr |
+
+
+statements. A
+next statement causes the next input record to be
+read and pattern testing to restart with the first
+pattern {action} pair in the program. An exit
+statement causes immediate execution of the END
+actions or program termination if there are none or if the
+exit occurs in an END action. The
+opt_expr sets the exit value of the program unless
+overridden by a later exit or subsequent error.
+
+14.
+include
+libmawk introduces source inclusion feature. Syntax is:
+
+
+
+ |
+
+
+
+ include "filename" |
+
+
+Include
+statements must be on top level (outside of blocks). If file
+name
+starts with a plus sign (’+’), the script file
+is not loaded if it has
+been already loaded (by another include or -f command line
+argument).
+
+EXAMPLES
+
+
+
+
+1. emulate
+cat.
+
+
+
+2. emulate
+wc.
+
+
+
+ |
+
+
+
+ { chars += length($0) + 1 # add one for the \n |
+
+ |
+
+
+
+ words += NF |
+
+ |
+
+
+
+ } |
+
+ |
+
+
+
+ END{ print NR, words, chars } |
+
+
+3. count the
+number of unique "real words".
+
+
+
+ |
+
+
+
+ BEGIN { FS = "[^A-Za-z]+" } |
+
+ |
+
+
+
+ { for(i = 1 ; i <= NF ; i++) word[$i] = ""
+} |
+
+ |
+
+
+
+ END { delete word[""] |
+
+ |
+
+
+
+ for ( i in word ) cnt++ |
+
+ |
+
+
+
+ print cnt |
+
+ |
+
+
+
+ } |
+
+
+4. sum the
+second field of every record based on the first field.
+
+
+
+ |
+
+
+
+ $1 ~ /credit|gain/ { sum += $2 } |
+
+ |
+
+
+
+ $1 ~ /debit|loss/ { sum −= $2 } |
+
+ |
+
+
+
+ END { print sum } |
+
+
+5. sort a file,
+comparing as string
+
+
+
+ |
+ |
+
+
+
+ { line[NR] = $0 "" } # make sure of comparison
+type |
+ |
+ |
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+ |
+
+
+
+ # in case some lines look numeric |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ END { isort(line, NR) |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ for(i = 1 ; i <= NR ; i++) print line[i] |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ } |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ #insertion sort of A[1..n] |
+ |
+ |
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ function isort( A, n, |
+ |
+ |
+ |
+
+
+
+ i, j, hold) |
+
+ |
+ |
+
+
+
+ { |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ for( i = 2 ; i <= n ; i++) |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ { |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ hold = A[j = i] |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ while ( A[j−1] > hold ) |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ { j−− ; A[j+1] = A[j] } |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ A[j] = hold |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ } |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ # sentinel A[0] = "" will be created if
+needed |
+ |
+ |
+ |
+
+ |
+
+ |
+ |
+
+
+
+ } |
+ |
+ |
+ |
+
+ |
+
+
+COMPATIBILITY ISSUES
+
+
+
+
+The Posix
+1003.2(draft 11.3) definition of the AWK language is AWK as
+described in the AWK book with a few extensions that
+appeared in SystemVR4 nawk. The extensions are:
+
+New functions:
+toupper() and tolower(); libmawk extensions: call(),
+acall(), valueof().
+
+New variables:
+ENVIRON[] and CONVFMT; libmawk extension: ERRNO, LIBPATH. As
+a libmawk extension, ENVIRON affects the environment of
+children processes.
+
+As a libmawk
+extension, new built-in variable LIBPATH is used as a list
+of search paths while loading scripts from the command line
+or from include.
+
+If a script
+name starts with plus (’+’), the file is not
+loaded if it has been loaded earlier (to avoid double
+loading libs trough -f and/or include). This is a libmawk
+extension.
+
+It is possible
+to include a script from another script using keyword
+include "scriptname.awk" (libmawk extension).
+
+ANSI C
+conversion specifications for printf() and sprintf().
+
+New command
+options: −v var=value, multiple -f options and
+implementation options as arguments to −W.
+
+Posix AWK is
+oriented to operate on files a line at a time. RS can
+be changed from "\n" to another single character,
+but it is hard to find any use for this — there are no
+examples in the AWK book. By convention, RS =
+"", makes one or more blank lines separate
+records, allowing multi-line records. When RS =
+"", "\n" is always a field separator
+regardless of the value in FS.
+
+lmawk,
+on the other hand, allows RS to be a regular
+expression. When "\n" appears in records, it is
+treated as space, and FS always determines
+fields.
+
+Removing the
+line at a time paradigm can make some programs simpler and
+can often improve performance. For example, redoing example
+3 from above,
+
+
+
+ |
+
+
+
+ BEGIN { RS = "[^A-Za-z]+" } |
+
+ |
+
+
+
+ { word[ $0 ] = "" } |
+
+ |
+
+
+
+ END { delete word[ "" ] |
+
+ |
+
+
+
+ for( i in word ) cnt++ |
+
+ |
+
+
+
+ print cnt |
+
+ |
+
+
+
+ } |
+
+
+counts the
+number of unique words by making each word a record. On
+moderate size files, lmawk executes twice as fast,
+because of the simplified inner loop.
+
+The following
+program replaces each comment by a single space in a C
+program file,
+
+
+
+ |
+ |
+
+
+
+ BEGIN { |
+
+ |
+
+ |
+ |
+
+
+
+ RS = "/\*([^*]|\*+[^/*])*\*+/" |
+
+ |
+
+ |
+
+ |
+
+ |
+
+
+
+ # comment is record separator |
+
+ |
+ |
+
+
+
+ ORS = " " |
+
+ |
+
+ |
+ |
+
+
+
+ getline hold |
+
+ |
+
+
+}
+
+{ print hold ;
+hold = $0 }
+
+END { printf
+"%s" , hold }
+
+Buffering one
+record is needed to avoid terminating the last record with a
+space.
+
+With
+lmawk, the following are all equivalent,
+
+
+
+ |
+
+
+
+ x ~ /a\+b/ x ~ "a\+b" x ~
+"a\\+b" |
+
+
+The strings get
+scanned twice, once as string and once as regular
+expression. On the string scan, lmawk ignores the
+escape on non-escape characters while the AWK book advocates
+\c be recognized as c which necessitates the
+double escaping of meta-characters in strings. Posix
+explicitly declines to define the behavior which passively
+forces programs that must run under a variety of awks to use
+the more portable but less readable, double escape.
+
+Posix AWK does
+not recognize "/dev/std{out,err}" or \x hex escape
+sequences in strings. Unlike ANSI C, lmawk limits the
+number of digits that follows \x to two as the current
+implementation only supports 8 bit characters. The built-in
+fflush first appeared in a recent (1993) AT&T awk
+released to netlib, and is not part of the posix standard.
+Aggregate deletion with delete array is not
+part of the posix standard.
+
+Posix
+explicitly leaves the behavior of FS = ""
+undefined, and mentions splitting the record into characters
+as a possible interpretation, but currently this use is not
+portable across implementations.
+
+Finally, here
+is how lmawk handles exceptional cases not discussed
+in the AWK book or the Posix draft. It is unsafe to assume
+consistency across awks and safe to skip to the next
+section.
+
+substr(s, i, n)
+returns the characters of s in the intersection of the
+closed interval [1, length(s)] and the half-open interval
+[i, i+n). When this intersection is empty, the empty string
+is returned; so substr("ABC", 1, 0) = ""
+and substr("ABC", −4, 6) =
+"A".
+
+Every string,
+including the empty string, matches the empty string at the
+front so, s ~ // and s ~ "", are always 1 as is
+match(s, //) and match(s, ""). The last two set
+RLENGTH to 0.
+
+index(s, t) is
+always the same as match(s, t1) where t1 is the same as t
+with metacharacters escaped. Hence consistency with match
+requires that index(s, "") always returns 1. Also
+the condition, index(s,t) != 0 if and only t is a substring
+of s, requires index("","") = 1.
+
+If getline
+encounters end of file, getline var, leaves var unchanged.
+Similarly, on entry to the END actions, $0,
+the fields and NF have their value unaltered from the
+last record.
+
+SEE ALSO
+
+
+
+
+
+egrep(1),
+mawk(1)
+
+Aho, Kernighan
+and Weinberger, The AWK Programming Language,
+Addison-Wesley Publishing, 1988, (the AWK book), defines the
+language, opening with a tutorial and advancing to many
+interesting programs that delve into issues of software
+design and analysis relevant to programming in any
+language.
+
+The GAWK
+Manual, The Free Software Foundation, 1991, is a
+tutorial and language reference that does not attempt the
+depth of the AWK book and assumes the reader may be a novice
+programmer. The section on AWK arrays is excellent. It also
+discusses Posix requirements for AWK.
+
+BUGS
+
+
+
+
+lmawk
+cannot handle ascii NUL \0 in the source or data files. You
+can output NUL using printf with %c, and any other 8 bit
+character is acceptable input.
+
+lmawk
+implements printf() and sprintf() using the C library
+functions, printf and sprintf, so full ANSI compatibility
+requires an ANSI C library. In practice this means the h
+conversion qualifier may not be available. Also lmawk
+inherits any bugs or limitations of the library
+functions.
+
+Implementors of
+the AWK language have shown a consistent lack of imagination
+when naming their programs.
+
+AUTHOR
+
+
+
+
+mawk:
+Mike Brennan (brennan@whidbey.com).
+
+libmawk
+extensions: Tibor Palinkas (libmawk@igor2.repo.hu).
+
+
+
diff --git a/doc/numeric.html b/doc/numeric.html
new file mode 100644
index 0000000..2fb66ac
--- /dev/null
+++ b/doc/numeric.html
@@ -0,0 +1,59 @@
+
+
+ libmawk numerics
+ Mawk implemented all numeric calculations using double precision floating
+ point numbers. Libmawk has a compile-time option for using different
+ types for numerics, with the following choices available:
+
+ - double (default)
+
- int
+
+
+ handling exceptions and NaNs
+ Mawk was geared towards catching floating point errors (such as
+ division by zero or log(-1)) and report a runtime error as soon as
+ possible. Libmawk, targeting embedded script application, should minimize
+ runtime errors by providing means for the script to check for them and
+ recover.
+
+ There are multiple approaches for handling suc errors. A design decision
+ has been made for exclusively using NaN (Not a Number) - on platforms where it is not
+ implemented by the FPU or by libc, it's emulated. NaN support shall work
+ the same way for all available numeric types.
+
+ Rules about NaN are few and simple:
+
+ - 1. NaN should be a sepcial value that can not be mistaken for a valid number by the script
+
- 2. math library calls (e.g. log()) should return NaN for invalid input; library calls shall accept NaN as input and return NaN as output
+
- 3. other library functions should handle NaN properly (i.e. printf %f or print shall write "nan" and a string-to-number conversion shall be able to understand "nan")
+
- 4. if any input of a calculation is NaN, the result shall be NaN (e.g. NaN+1 is NaN); such calculations never cause runtime error
+
- 5. 1/0, 0/0 and similar corner cases are all NaN - there is no inf
+
- 6. the only runtime error that may be caused by NaNs is when a conditional jump depends on a NaN (e.g. if (nan) {})
+
- 7. isnan(x) returns 1 if x is NaN.
+
+
+ In practice this means a block of numeric calculations can be done safely,
+ without checking any input or intermediate results. At the end of the
+ block the result(s) shall be checked with isnan(). As long as the results
+ (even indirectly) depend on an input or intermediate result that is NaN,
+ the result is guaranteed to be NaN too, without the risk of a runtime error.
+
+
+
Implementation
+ type: double
+ If the system has FPE, it's disabled. If the system does not have native
+ NaN, a special NaN value is defined (using HUGE_VAL) and
+ before each operation all inputs are checked for NaN to make sure the
+ output is NaN too. On systems with NaN
+ ### TODO: inf? ###
+
+ type: int
+ ### TODO: ###
+
+ division by zero
+ Before divisions input is always checked and division by zero is
+ replaced by a NaN. The same mechanism is in place for all numeric types.
+
+
+
+
diff --git a/doc/portability.html b/doc/portability.html
new file mode 100644
index 0000000..339be4f
--- /dev/null
+++ b/doc/portability.html
@@ -0,0 +1,76 @@
+
+
+ Portability with scconfig
+
+ Libmawk is configured using an embedded copy of scconfig. In a distribution
+ tarball, it's a snapshot of scconfig; in an svn repository it is an
+ svn external, which guarantees libmawk is using the latest version
+ of scconfig.
+
+ Scconfig is a self-contained project configuration tool which depends
+ only on Make and an ANSI (C89) C compiler. Scconfig can test different
+ things on a system, often by compiling and running test programs. Besides
+ saving test results in a text file, it can also generate text files like
+ Makefiles or include files with content depending on those test results.
+
+ There are artificial limits on portability tho. While in theory it is
+ possible to scretch the system so that it really works on all operating
+ systems ever existed, or at least on all UNIX-like systems, it is not the
+ goal. This decision is a trade-off between portability and maintainability.
+ Also between portability and code size (or bloat). The artificial limits are:
+
+ - a C compiler that fully supports ANSI (C89) C is required
+
- make supporting traditional Makefiles is required
+
- no efforts for systems older than 80s UNIX systems; for example libmawk depends on pipe(2), and if it is missing, some of the functionality will be missing
+
- mawk had support for DOS - this is removed, because
+
+ - it was already marked as somewhat obsolete or at least untested in version 1.3.3, which is the base for libmawk; documentation suggested users should use an older version of mawk on DOS
+
- since DOS poses drastic limitation on memory, and mawk seemed to be a memory hog, it is hard to believe someone would actually write an application that would share memory with libmawk and still do something useful - and after all, the purpose of libmawk is exactly embedding awk in applications
+
- too many #ifdefs scattered the code for supporting DOS
+
+
+
+ Libmawk 0.9.7 is reported to configure and compile on the
+ following systems:
+
+ system | compiles
+ out of
+ the box | FPE problems | awklib test | binary save/load save/load
+ |
[i386] GNU/Linux | yes | no | ok | ok
+ |
[amd64] GNU/Linux | yes | no | ok | ok
+ |
[i386] Minix | | | |
+ |
[i386] NetBSD 4.0 | yes | no | ok | ok
+ |
[i386] open
+ solaris 5.11 | | | |
+ |
[i386] OpenBSD 5.0 | yes | no | ok | ok
+ |
[i386] FreeBSD 9.0 | yes | no | ok | ok
+ |
[IP22] IRIX 5.3 | no | minor | | doesn't work
+ |
[i386] Dragonfly
+ BSD 2.10 | yes | no | ok | ok
+
+ |
+
+
+
+ Libmawk 0.9.6 is reported to configure and compile on the
+ following systems:
+
+ system | compiles
+ out of
+ the box | FPE problems | awklib test | binary save/load save/load
+ |
i386 GNU/Linux | yes | no | ok | ok
+ |
amd64 GNU/Linux | yes | no | ok | ok
+ |
i386 Minix | yes | yes | ok | ok
+ |
i386 NetBSD 4.0 | yes | yes | ok | ok
+ |
i386 open
+ solaris 5.11 | yes | no | ok | ok
+ |
i386 OpenBSD 5.0 | yes | yes | ok | ok
+ |
i386 FreeBSD 9.0 | yes | yes | ok | ok
+ |
IP22 IRIX 5.3 | yes | no | ok | doesn't work
+ |
i386 Dragonfly
+ BSD 2.10 | yes | yes | ok | ok
+
+ |
+
+
+
diff --git a/doc/semi-gnu.html b/doc/semi-gnu.html
new file mode 100644
index 0000000..989c147
--- /dev/null
+++ b/doc/semi-gnu.html
@@ -0,0 +1,60 @@
+
+
+ Semi-dependency on GNU utils
+Some of the features depend on GNU-specific implementation of standard
+software. These are all semi-dependencies, which means if an user doesn't
+have GNU software on a system, it is still possible for him/her to compile
+libmawk. Unfortunately editing some parts of the source on such a system
+would not result in the desired updates. Obviously a main goal is to
+minimize this sort of dependency, but sometimes it is very hard or impossible
+to avoid. This document describes these dependencies explaining the tradeoff
+and the ways around.
+ 1. Bison
+ 1.1. Rationale
+ There are four GNU-specific features in use:
+
+ - %pure-parser for getting a reentrant parser
+
- %parse-param to pass mawk state around to avoid global variables
+
- %lex-param for the same reason as %parse-param
+
- --name-prefix to avoid namespace pollution
+
+ The first three features are critical for having a reentrant/thread safe
+ libmawk. Without those three, there could be only one mawk context in
+ an application, or at least parsing more than one script in the same
+ time would cause both context mangled.
+
+ The fourth feature, --name-prefix helps avoiding namespace pollution -
+ the application may have its own parsers with or without name prefixing,
+ and libmawk shouldn't collide with those.
+
+
1.2. effects, restrictions
+ Scconfig detects presence of bison; in case bison is not installed,
+ Makefile.conf is generated in a way that bison is never run. Output
+ of bison is included in the source tree (parse.c/parse.h).
+
+ Not having bison on a systam means editing parse.y will not
+ update the actual parser code in parse.c.
+
+
1.3. how to bypass
+ It is possible to use traditional yacc for compiling parse.y. Besides
+ editing Makefile.conf and parse.y for removing those 4 features,
+ scan.c/scan.h should be edited too, because arguments for the scanner
+ depends on these settings. A global variable for the mawk context for
+ the script currently being parsed should be introduced. Actions should
+ be made to ensure no concurrent loading of scripts is possible.
+
+ 2. Makefile.dep needs gcc
+ 2.1. Rationale
+ Source file dependencies are generated using gcc -MM and are
+ stored in Makefile.dep shipped with the source package.
+
+ 2.2. effects, restrictions
+ After changing #include lines in the source, running make depend
+ will not update Makefile.dep but will zap it.
+
+ 2.3. how to bypass
+ It is easily possible to keep Makefile.dep in sync by hand. Another option
+ is to ignore Makefile.dep and run make clean before compilation.
+
+
+
diff --git a/scconfig/Makefile b/scconfig/Makefile
new file mode 100644
index 0000000..a59c677
--- /dev/null
+++ b/scconfig/Makefile
@@ -0,0 +1,55 @@
+# --- configuration part --
+SRC=src/
+BIN=src/
+
+# - generic configuration -
+# what cflags to use to compile scconfig
+USER_CFLAGS = -g -DGENCALL
+
+# what ldflags to use to link scconfig
+USER_LDFLAGS =
+
+# in case hooks.c needs to link to something local
+USER_OBJS =
+
+# what to build - a ./configure
+all: configure
+
+# This line imports scconfig core and default tests
+include src/default/Makefile.plugin
+
+#
+# - PLUGINS -
+#
+# Comment this line if you are not interested in c99 features
+#include src/c99/Makefile.plugin
+
+# Comment this line if you do not need script libs to be detected
+#include src/scripts/Makefile.plugin
+
+# Comment this line if you do not need parser libs to be detected
+#include src/parser/Makefile.plugin
+
+# Comment this line if you do not need to detect parser generators
+include src/parsgen/Makefile.plugin
+
+# Comment this line if you do not need math related libs
+include src/math/Makefile.plugin
+
+# Comment this line if you do not need socket/networking
+#include src/socket/Makefile.plugin
+
+# Comment this line if you do not need tmpasm (templating)
+include src/tmpasm/Makefile.plugin
+
+# --- you shouldn't edit the lines below ---
+OBJS = $(USER_OBJS) hooks.o $(DEFAULT_OBJS) $(SCRIPT_OBJS) $(PARSER_OBJS) $(TMPASM_OBJS) $(C99_OBJS) $(PARSGEN_OBJS) $(MATH_OBJS) $(SOCKET_OBJS)
+CFLAGS = $(USER_CFLAGS) $(DEFAULT_CFLAGS) $(SCRIPT_CFLAGS) $(PARSER_CFLAGS) $(TMPASM_CFLAGS) $(C99_CFLAGS) $(PARSGEN_CFLAGS) $(MATH_CFLAGS) $(SOCKET_CFLAGS) -Isrc/default
+LDFLAGS = $(USER_LDFLAGS) $(DEFAULT_LDFLAGS) $(SCRIPT_LDFLAGS) $(PARSER_LDFLAGS) $(TMPASM_LDFLAGS) $(C99_LDFLAGS) $(PARSGEN_LDFLAGS) $(MATH_LDFLAGS) $(SOCKET_LDFLAGS)
+
+configure: $(OBJS)
+ $(CC) -o configure $(OBJS)
+
+clean:
+ -rm $(OBJS) configure
+
diff --git a/scconfig/hooks.c b/scconfig/hooks.c
new file mode 100644
index 0000000..9e24d00
--- /dev/null
+++ b/scconfig/hooks.c
@@ -0,0 +1,204 @@
+#include
+#include "arg.h"
+#include "log.h"
+#include "dep.h"
+#include "db.h"
+#include "tmpasm_scconfig.h"
+
+#define VER1 "1"
+#define VER2 "0"
+#define VER3 "0"
+
+static void help(void)
+{
+ printf("./configure: configure libmawk.\n");
+ printf("\n");
+ printf("Usage: ./configure [options]\n");
+ printf("\n");
+ printf("options are:\n");
+ printf(" --prefix=path change installation prefix from /usr/local to path\n");
+ printf(" --debug build full debug version (-g -O0, extra asserts)\n");
+ printf(" --profile build profiling version if available (-pg)\n");
+ printf(" --symbols include symbols (add -g, but no -O0 or extra asserts)\n");
+ printf(" --numeric=int change the internal numeric type (default is double)\n");
+ printf("\n");
+}
+
+/* Runs when a custom command line argument is found
+ returns true if no furhter argument processing should be done */
+int hook_custom_arg(const char *key, const char *value)
+{
+ if (strcmp(key, "prefix") == 0) {
+ put("/local/prefix", value);
+ return 1;
+ }
+ if (strcmp(key, "debug") == 0) {
+ put("/local/debug", strue);
+ return 1;
+ }
+ if (strcmp(key, "symbols") == 0) {
+ put("/local/symbols", strue);
+ return 1;
+ }
+ if (strcmp(key, "profile") == 0) {
+ put("/local/profile", strue);
+ return 1;
+ }
+ if (strcmp(key, "numeric") == 0) {
+ if ((strcmp(value, "int") == 0) || (strcmp(value, "double") == 0))
+ put("/local/numeric", value);
+ else {
+ fprintf(stderr, "Error: invalid numeric format. Must be int or double\n");
+ exit(1);
+ }
+ return 1;
+ }
+ if (strcmp(key, "help") == 0) {
+ help();
+ exit(0);
+ }
+
+ return 0;
+}
+
+
+/* Runs before anything else */
+int hook_preinit()
+{
+ return 0;
+}
+
+/* Runs after initialization */
+int hook_postinit()
+{
+ db_mkdir("/local");
+
+ /* defaults */
+ put("/local/prefix", "/usr/local");
+ put("/local/debug", sfalse);
+ put("/local/symbols", sfalse);
+ put("/local/profile", sfalse);
+
+ report("Configuring libmawk.\n");
+ logprintf(0, "Configuring libmawk.\n");
+ return 0;
+}
+
+/* Runs after all arguments are read and parsed */
+int hook_postarg()
+{
+ if (get("/local/numeric") == NULL)
+ put("/local/numeric", "double");
+ return 0;
+}
+
+/* Runs when things should be detected for the host system */
+int hook_detect_host()
+{
+ return 0;
+}
+
+/* Runs when things should be detected for the target system */
+int hook_detect_target()
+{
+ put("/local/version", VER1 "." VER2 "." VER3);
+ put("/local/version/1", VER1);
+ put("/local/version/2", VER2);
+ put("/local/version/3", VER3);
+
+ /* if there was no custom requirement from the command line, run all requirements in non-fatal mode */
+ if (num_custom_reqs < 1) {
+ if (istrue(get("/local/debug"))) {
+ require("cc/argstd/pedantic", 0, 0);
+ require("cc/argstd/ansi", 0, 0);
+ require("cc/argstd/Wall", 0, 0);
+ append("/target/cc/cflags", " -O0 ");
+ append("/target/cc/cflags", get("cc/argstd/ansi"));
+ append("/target/cc/cflags", " ");
+ append("/target/cc/cflags", get("cc/argstd/pedantic"));
+ append("/target/cc/cflags", " ");
+ append("/target/cc/cflags", get("cc/argstd/Wall"));
+ }
+ else
+ append("/target/cc/cflags", " -O3 ");
+ if (istrue(get("/local/debug")) || istrue(get("/local/symbols")))
+ append("/target/cc/cflags", " -g ");
+ if (istrue(get("/local/profile"))) {
+ require("cc/argstd/pg", 0, 0);
+ require("cc/argstd/no-pie", 0, 0);
+ append("/target/cc/cflags", " ");
+ append("/target/cc/cflags", get("cc/argstd/pg"));
+ append("/target/cc/cflags", " ");
+ append("/target/cc/cflags", get("cc/argstd/no-pie"));
+ }
+
+
+ require("cc/cc", 0, 1);
+ require("cc/fpic", 0, 1);
+ require("cc/soname", 0, 0);
+ require("cc/rdynamic", 0, 0);
+ require("fstools/chmodx", 0, 1);
+ require("fstools/cp", 0, 1);
+ require("fstools/rm", 0, 1);
+ require("fstools/ln", 0, 1);
+ require("fstools/mkdir", 0, 1);
+ require("sys/types/size_t/includes", 0, 0);
+ require("libs/fs/realpath/presents", 0, 0);
+ require("libs/env/putenv", 0, 1);
+ require("libs/io/pipe/presents", 0, 0);
+ require("libs/math/cc/log/*", 0, 1);
+ require("libs/math/nan/*", 0, 0);
+ require("libs/math/isnan/*", 0, 0);
+ require("libs/math/nanop/*", 0, 0);
+
+ require("parsgen/bison", 0, 0);
+ printf("Numeric format: %s\n", get("/local/numeric"));
+ }
+ return 0;
+}
+
+/* Runs after detection hooks, should generate the output (Makefiles, etc.) */
+int hook_generate()
+{
+ printf("Generating libmawk/Makefile... ");
+ fflush(stdout);
+ if (tmpasm("../src/libmawk", "Makefile.conf.in", "Makefile.conf") == 0)
+ printf("OK\n");
+ else
+ printf("failed\n");
+
+ printf("Generating libmawk/conf.h... ");
+ fflush(stdout);
+ if (tmpasm("../src/libmawk", "conf.h.in", "conf.h") == 0)
+ printf("OK\n");
+ else
+ printf("failed\n");
+
+ printf("Generating awklib/Makefile... ");
+ fflush(stdout);
+ if (tmpasm("../src/awklib", "Makefile.in", "Makefile") == 0)
+ printf("OK\n");
+ else
+ printf("failed\n");
+
+ printf("Generating awklib/regression/Makefile... ");
+ fflush(stdout);
+ db_mkdir("/local");
+ if (tmpasm("../src/awklib/regression", "Makefile.in", "Makefile") == 0)
+ printf("OK\n");
+ else
+ printf("failed\n");
+
+ return 0;
+}
+
+/* Runs before everything is uninitialized */
+void hook_preuninit()
+{
+}
+
+/* Runs at the very end, when everything is already uninitialized */
+void hook_postuninit()
+{
+}
+
diff --git a/scconfig/src/default/Makefile.plugin b/scconfig/src/default/Makefile.plugin
new file mode 100644
index 0000000..3b33700
--- /dev/null
+++ b/scconfig/src/default/Makefile.plugin
@@ -0,0 +1,141 @@
+DEFAULT_NOMAIN_OBJS = \
+ $(BIN)/default/find_cc.o \
+ $(BIN)/default/lib_compile.o \
+ $(BIN)/default/lib_uniqinc.o \
+ $(BIN)/default/lib_file.o \
+ $(BIN)/default/lib_try.o \
+ $(BIN)/default/str.o \
+ $(BIN)/default/ht.o \
+ $(BIN)/default/log.o \
+ $(BIN)/default/arg.o \
+ $(BIN)/default/db.o \
+ $(BIN)/default/dep.o \
+ $(BIN)/default/deps_default.o \
+ $(BIN)/default/find_libs.o \
+ $(BIN)/default/find_fscalls.o \
+ $(BIN)/default/find_printf.o \
+ $(BIN)/default/find_proc.o \
+ $(BIN)/default/find_fstools.o \
+ $(BIN)/default/find_uname.o \
+ $(BIN)/default/find_target.o \
+ $(BIN)/default/find_thread.o \
+ $(BIN)/default/find_io.o \
+ $(BIN)/default/find_time.o \
+ $(BIN)/default/find_types.o \
+ $(BIN)/default/find_signal.o \
+ $(BIN)/default/find_environ.o \
+ $(BIN)/default/regex.o \
+ $(BIN)/default/lib_filelist.o \
+ $(BIN)/default/lib_srctree.o \
+ $(BIN)/default/lib_pkg_config.o \
+ $(BIN)/default/find_str.o \
+ $(BIN)/default/find_sys.o
+
+DEFAULT_MAIN_OBJS = \
+ $(BIN)/default/main.o \
+ $(BIN)/default/main_custom_args.o \
+ $(BIN)/default/main_lib.o
+
+DEFAULT_OBJS = $(DEFAULT_NOMAIN_OBJS) $(DEFAULT_MAIN_OBJS)
+
+$(BIN)/default/lib_compile.o: $(SRC)/default/lib_compile.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/lib_compile.c -o $(BIN)/default/lib_compile.o
+
+$(BIN)/default/lib_file.o: $(SRC)/default/lib_file.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/lib_file.c -o $(BIN)/default/lib_file.o
+
+$(BIN)/default/lib_try.o: $(SRC)/default/lib_try.c $(SRC)/default/log.h $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/lib_try.c -o $(BIN)/default/lib_try.o
+
+$(BIN)/default/str.o: $(SRC)/default/str.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/str.c -o $(BIN)/default/str.o
+
+$(BIN)/default/ht.o: $(SRC)/default/ht.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/ht.c -o $(BIN)/default/ht.o
+
+$(BIN)/default/log.o: $(SRC)/default/log.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/log.c -o $(BIN)/default/log.o
+
+$(BIN)/default/arg.o: $(SRC)/default/arg.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/arg.c -o $(BIN)/default/arg.o
+
+$(BIN)/default/db.o: $(SRC)/default/db.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/db.c -o $(BIN)/default/db.o
+
+$(BIN)/default/dep.o: $(SRC)/default/dep.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/dep.c -o $(BIN)/default/dep.o
+
+$(BIN)/default/deps_default.o: $(SRC)/default/deps_default.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/deps_default.c -o $(BIN)/default/deps_default.o
+
+$(BIN)/default/find_libs.o: $(SRC)/default/find_libs.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_libs.c -o $(BIN)/default/find_libs.o
+
+$(BIN)/default/find_fscalls.o: $(SRC)/default/find_fscalls.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_fscalls.c -o $(BIN)/default/find_fscalls.o
+
+$(BIN)/default/find_signal.o: $(SRC)/default/find_signal.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_signal.c -o $(BIN)/default/find_signal.o
+
+$(BIN)/default/find_printf.o: $(SRC)/default/find_printf.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_printf.c -o $(BIN)/default/find_printf.o
+
+$(BIN)/default/find_proc.o: $(SRC)/default/find_proc.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_proc.c -o $(BIN)/default/find_proc.o
+
+$(BIN)/default/find_fstools.o: $(SRC)/default/find_fstools.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_fstools.c -o $(BIN)/default/find_fstools.o
+
+$(BIN)/default/find_uname.o: $(SRC)/default/find_uname.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_uname.c -o $(BIN)/default/find_uname.o
+
+$(BIN)/default/find_target.o: $(SRC)/default/find_target.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_target.c -o $(BIN)/default/find_target.o
+
+$(BIN)/default/regex.o: $(SRC)/default/regex.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/regex.c -o $(BIN)/default/regex.o
+
+$(BIN)/default/lib_filelist.o: $(SRC)/default/lib_filelist.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/lib_filelist.c -o $(BIN)/default/lib_filelist.o
+
+$(BIN)/default/lib_srctree.o: $(SRC)/default/lib_srctree.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/lib_srctree.c -o $(BIN)/default/lib_srctree.o
+
+$(BIN)/default/lib_pkg_config.o: $(SRC)/default/lib_pkg_config.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/lib_pkg_config.c -o $(BIN)/default/lib_pkg_config.o
+
+$(BIN)/default/lib_uniqinc.o: $(SRC)/default/lib_uniqinc.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/lib_uniqinc.c -o $(BIN)/default/lib_uniqinc.o
+
+$(BIN)/default/find_sys.o: $(SRC)/default/find_sys.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_sys.c -o $(BIN)/default/find_sys.o
+
+$(BIN)/default/find_str.o: $(SRC)/default/find_str.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_str.c -o $(BIN)/default/find_str.o
+
+$(BIN)/default/find_cc.o: $(SRC)/default/find_cc.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_cc.c -o $(BIN)/default/find_cc.o
+
+$(BIN)/default/find_environ.o: $(SRC)/default/find_environ.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_environ.c -o $(BIN)/default/find_environ.o
+
+$(BIN)/default/find_io.o: $(SRC)/default/find_io.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_io.c -o $(BIN)/default/find_io.o
+
+$(BIN)/default/find_time.o: $(SRC)/default/find_time.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_time.c -o $(BIN)/default/find_time.o
+
+$(BIN)/default/find_types.o: $(SRC)/default/find_types.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_types.c -o $(BIN)/default/find_types.o
+
+$(BIN)/default/main.o: $(SRC)/default/main.c $(SRC)/default/dep.h $(SRC)/default/libs.h Makefile
+ $(CC) $(CFLAGS) -c $(SRC)/default/main.c -o $(BIN)/default/main.o
+
+$(BIN)/default/main_custom_args.o: $(SRC)/default/main_custom_args.c
+ $(CC) $(CFLAGS) -c $(SRC)/default/main_custom_args.c -o $(BIN)/default/main_custom_args.o
+
+$(BIN)/default/main_lib.o: $(SRC)/default/main_lib.c
+ $(CC) $(CFLAGS) -c $(SRC)/default/main_lib.c -o $(BIN)/default/main_lib.o
+
+$(BIN)/default/find_thread.o: $(SRC)/default/find_thread.c $(SRC)/default/dep.h $(SRC)/default/libs.h
+ $(CC) $(CFLAGS) -c $(SRC)/default/find_thread.c -o $(BIN)/default/find_thread.o
diff --git a/scconfig/src/default/arg.c b/scconfig/src/default/arg.c
new file mode 100644
index 0000000..a50f48a
--- /dev/null
+++ b/scconfig/src/default/arg.c
@@ -0,0 +1,106 @@
+/*
+ scconfig - command line argument processing
+ Copyright (C) 2009..2012 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include "db.h"
+#include "arg.h"
+#include "dep.h"
+#include "log.h"
+#include "libs.h"
+
+argtbl_t main_argument_table[] = {
+ {"import", NULL, import_args, "Import saved config (sub)tree"},
+ {"target", "/arg/sys/target", NULL, "set cross compilation target (prefix)"},
+ {"target-name", "/arg/sys/target-name", NULL, "set cross compilation target (system name)"},
+ {"target-shell","/arg/sys/target-shell",NULL, "set the shell on cross compilation target"},
+ {"emu", "/arg/sys/emu", NULL, "emulator for testing cross compiled executables with"},
+ {"pkg-config", "/arg/sys/pkg-config", NULL, "path to pkg-config to use"},
+ {"pkg-config-zap","/arg/sys/pkg-config-zap",NULL, "ignore pkg-config results by this regex pattern"},
+
+/* wildcard rules for icl() control */
+ {"^ldflags/", NULL, import_icl, NULL},
+ {"^cflags/", NULL, import_icl, NULL},
+ {"^includes/", NULL, import_icl, NULL},
+ {"^prefix/", NULL, import_icl, NULL},
+
+/* the followings are autoconf compatibility translations */
+ {"CC", "/arg/cc/cc", NULL, "Force using a C compiler (command line)"},
+ {"CFLAGS", "/arg/cc/cflags", NULL, "Force using a CFLAGS for C compilation"},
+ {"LDFLAGS", "/arg/cc/ldflags", NULL, "Force using a LDFLAGS for linking"},
+ {"LDL", "/arg/libs/ldl", NULL, "Force using -ldl string"},
+
+ {"gpmi-prefix", "/arg/gpmi/prefix", NULL, NULL},
+ {NULL, NULL, NULL, NULL}
+};
+
+void process_args(int argc, char *argv[])
+{
+ int n;
+ char *key, *value;
+ argtbl_t *a;
+ int found, tainted = 0;
+
+ db_mkdir("/arg");
+
+ logprintf(0, "CLI arg 0: '%s'\n", argv[0]);
+ for(n = 1; n < argc; n++) {
+ key = argv[n];
+ logprintf(0, "CLI arg %d: '%s'\n", n, key);
+ while(*key == '-') key++;
+ value = str_chr(key, '=');
+ found = 0;
+ if (value != NULL) {
+ *value = '\0';
+ value++;
+ /* Look in the argument translate table */
+ for(a = main_argument_table; a->arg != NULL; a++) {
+ if (((a->arg[0] == '^') && (strncmp(a->arg+1, key, strlen(a->arg+1)) == 0)) || (strcmp(a->arg, key) == 0)) {
+ found = 1;
+ if (a->callback != NULL) {
+ if (a->callback(key, value) != 0) {
+ error("Processing argument '%s' failed in the callback\n", argv[n]);
+ abort();
+ }
+ }
+ if (a->path != NULL)
+ put(a->path, value);
+ }
+ }
+ /* Look in known deps table or /arg */
+ if (found == 0) {
+ if ((is_dep_known(key)) || (strncmp(key, "/arg/", 5) == 0)) {
+ put(key, value);
+ found = 1;
+ }
+ }
+ }
+ if (found == 0) {
+ if (custom_arg(key, value) == 0) {
+ error("Unknown argument '%s'\n", key);
+ tainted++;
+ }
+ }
+ }
+ if (tainted)
+ exit(1);
+}
diff --git a/scconfig/src/default/arg.h b/scconfig/src/default/arg.h
new file mode 100644
index 0000000..f94eb31
--- /dev/null
+++ b/scconfig/src/default/arg.h
@@ -0,0 +1,22 @@
+#ifndef SCC_ARG_H
+#define SCC_ARG_H
+
+typedef struct {
+ char *arg;
+ char *path;
+ int (*callback)(const char *key, const char *value);
+ char *help;
+} argtbl_t;
+
+extern argtbl_t main_argument_table[];
+
+
+
+void process_args(int argc, char *argv[]);
+
+
+/* main.c: */
+extern int custom_arg(const char *key, const char *value);
+extern int num_custom_reqs;
+
+#endif
diff --git a/scconfig/src/default/db.c b/scconfig/src/default/db.c
new file mode 100644
index 0000000..5d012a5
--- /dev/null
+++ b/scconfig/src/default/db.c
@@ -0,0 +1,443 @@
+/*
+ scconfig - database
+ Copyright (C) 2009..2012 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include "db.h"
+#include "log.h"
+#include "libs.h"
+
+ht_t *DBs = NULL;
+char *db_cwd = NULL;
+
+void append(const char *key, const char *value)
+{
+ const char *orig;
+ char *new;
+ int l1, l2;
+
+ assert(key != NULL);
+ assert(value != NULL);
+ if (*value == '\0')
+ return;
+
+ orig = get(key);
+ if (orig == NULL) {
+ put(key, value);
+ return;
+ }
+
+ l1 = strlen(orig);
+ l2 = strlen(value);
+ new = malloc(l1 + l2 + 1);
+ memcpy(new, orig, l1);
+ memcpy(new + l1, value, l2);
+ new[l1+l2] = '\0';
+ put(key, new);
+}
+
+
+static const char *db_split_path(const char *key, ht_t **ht, char **fld)
+{
+ size_t fld_len;
+ const char *path;
+ char first_level_dir[32];
+
+ path = str_chr((char *)(key+1), '/');
+ assert(path != NULL);
+ fld_len = path - key;
+ path++;
+ if (*path == '\0') {
+ *ht = NULL;
+ if (fld != NULL)
+ *fld = NULL;
+ return NULL;
+ }
+ assert(fld_len < sizeof(first_level_dir));
+ strncpy(first_level_dir, key, fld_len);
+ first_level_dir[fld_len] = '\0';
+ *ht = ht_get(DBs, first_level_dir+1);
+ if (fld != NULL)
+ *fld = first_level_dir;
+ return path;
+}
+
+static void export_qs(FILE *f, const char *s)
+{
+ fputc('"', f);
+ for(;*s != '\0';s++) {
+ switch(*s) {
+ case '"': fputc('\\', f); fputc('"', f); break;
+ case '\n': fputc('\\', f); fputc('n', f); break;
+ case '\r': fputc('\\', f); fputc('r', f); break;
+ case '\t': fputc('\\', f); fputc('t', f); break;
+ default: fputc(*s, f);
+ }
+ }
+
+ fputc('"', f);
+ fputc('\n', f);
+}
+
+static int needs_quote(const char *s) {
+ for(; *s != '\0'; s++)
+ if ((*s < 32) || (*s > 126) || (*s == '"')) return 1;
+ return 0;
+}
+
+int export_(FILE *f, int export_empty, ht_t *table, const char *fld)
+{
+ ht_entry_t *h;
+
+ for(h = ht_first(table); h != NULL; h = ht_next(table, h))
+ if (export_empty || ((h->value != NULL) && (*(char *)h->value != '\0'))) {
+ fprintf(f, "/%s/%s=", fld, h->key);
+ if (h->value != NULL) {
+ if (needs_quote((char *)h->value))
+ export_qs(f, (const char *)h->value);
+ else
+ fprintf(f, "%s\n", (char *)h->value);
+ }
+ else
+ fprintf(f, "\n");
+ }
+ return 0;
+}
+
+int export(const char *fn, int export_empty, const char *root)
+{
+ FILE *f;
+ int ret;
+/* ht_t *table; */
+ ht_entry_t *h;
+
+ if (fn != NULL) {
+ f = fopen(fn, "w");
+ if (f == NULL)
+ return -1;
+ }
+ else
+ f = stdout;
+
+ if ((root == NULL) || ((root[0] == '/') && (root[1] == '\0'))) {
+ /* export all directories */
+ for(h = ht_first(DBs); h != NULL; h = ht_next(DBs, h))
+ ret += export_(f, export_empty, h->value, h->key);
+ }
+ else {
+ error("not yet implemented\n");
+ abort();
+ /* db_split_path(); */
+ }
+
+ if (f != stdout)
+ fclose(f);
+
+ return ret;
+}
+
+/* append a single character, grow the buffer as needed */
+#define qappend(chr) \
+do { \
+ if (used >= alloced) { \
+ alloced += 256; \
+ res = realloc(res, alloced); \
+ } \
+ res[used] = chr; \
+ used++; \
+} while(0)
+
+/* read until end of quote and interpret backslash sequences if do_esc is non-zero */
+static char *readq(FILE *f, char *str, long strmax, int quote, int do_esc, int *num_lines, const char *fn)
+{
+ int bs = 0;
+ long used = 0, alloced = 0;
+ char *res = NULL, *s;
+
+ for(;;) {
+ for(s = str; *s != '\0'; s++) {
+ if (*s == '\n') (*num_lines)++;
+
+ if (bs) { /* character escaped by backslash */
+ switch(*s) {
+ case '\\': qappend('\\'); break;
+ case 'n': qappend('\n'); break;
+ case 'r': qappend('\r'); break;
+ case 't': qappend('\t'); break;
+ default: qappend(*s); break;
+ }
+ bs = 0;
+ }
+ else if (*s == quote) { /* end */
+ qappend('\0');
+ if ((s[1] != '\r') && (s[1] != '\n') && (s[1] != '\0'))
+ fprintf(stderr, "Warning: trailing text after quote ignored in %s:%d\n", fn, (*num_lines)+1);
+ return res;
+ }
+ else if (do_esc && (*s == '\\')) bs = 1; /* backslash start */
+ else qappend(*s); /* plain character */
+ }
+
+ /* get the next chunk */
+ fgets(str, strmax, f);
+ }
+
+ return NULL; /* can't get here */
+}
+
+int import(const char *fn)
+{
+ char line[1024];
+ char *key, *value, *nl, *slash;
+ int num_records, num_lines;
+ FILE *f;
+
+ f = fopen(fn, "r");
+ if (f == NULL)
+ return -1;
+
+ for(num_records = 0, num_lines = 0; !feof(f); num_lines++) {
+ *line = '\0';
+ fgets(line, sizeof(line) - 1, f);
+ if ((*line != '#') && (*line != '\n') && (*line != '\r') && (*line != '\0')) {
+ int quote, do_esc=0;
+ key = line;
+ value = str_chr(key, '=');
+ if (value == NULL) {
+ error("Error importing: missing '=' in line %d in file %s.\n", num_lines, fn);
+ abort();
+ }
+ num_records++;
+ *value = '\0';
+ value++;
+ if (*value == '"') {
+ quote=*value;
+ value++;
+ do_esc=1;
+ }
+ else if (*value == '\'') {
+ quote=*value;
+ value++;
+ }
+ else
+ quote=0;
+
+ if (!quote) {
+ nl = str_chr(value, '\n');
+ if (nl != NULL)
+ *nl = '\0';
+ }
+ else
+ value = readq(f, value, sizeof(line) - (value - line) - 4, quote, do_esc, &num_lines, fn);
+
+ slash = str_chr(key+1, '/');
+ if (slash == NULL) {
+ error("Error importing: no directory name for %s.\n", key);
+ abort();
+ }
+ *slash = '\0';
+ db_mkdir(key);
+ *slash = '/';
+ put(key, value);
+ logprintf(0, "(Import from '%s': '%s'='%s')\n", fn, key, value);
+ if (quote)
+ free(value);
+ }
+ }
+
+ fclose(f);
+ return num_records;
+}
+
+int import_args(const char *key, const char *fn)
+{
+ (void) key; /* suppress compiler warnings for unused key; needed because function pointers to this function from arg.c */
+ db_mkdir("/target");
+ db_mkdir("/host");
+
+ return import(fn) < 0;
+}
+
+
+static const char *db_get(const char *key)
+{
+ const char *path;
+ ht_t *ht;
+
+ path = db_split_path(key, &ht, NULL);
+ if (ht == NULL)
+ return NULL;
+ return ht_get(ht, path);
+}
+
+static const char *db_put(const char *key, const char *value)
+{
+ const char *path;
+ ht_t *ht;
+
+ path = db_split_path(key, &ht, NULL);
+ if (ht == NULL) {
+ error("db_put: can't find top level hash for '%s'\n", key);
+ abort();
+ }
+ return ht_set(ht, path, (void *)value);
+}
+
+#define assamble_path \
+ assert(strlen(key) + strlen(db_cwd) < sizeof(tmp)-1); \
+ sprintf(tmp, "%s/%s", db_cwd, key);
+
+const char *get(const char *key)
+{
+ char tmp[256];
+
+ if (*key == '/')
+ return db_get(key);
+ assamble_path;
+ return db_get(tmp);
+}
+
+const char *put(const char *key, const char *value)
+{
+ char tmp[256];
+
+ if (*key == '/')
+ return db_put(key, value);
+ assamble_path;
+ return db_put(tmp, value);
+}
+
+void db_init(void)
+{
+ DBs = ht_resize(ht_alloc(0), 16);
+}
+
+void db_uninit(void)
+{
+ ht_entry_t *h;
+ ht_t *dir;
+
+ for(h = ht_first(DBs); h != NULL; h = ht_next(DBs, h)) {
+ dir = h->value;
+ dir->refcount--;
+ if (dir->refcount == 0)
+ ht_free(dir);
+ }
+ ht_free(DBs);
+ if (db_cwd != NULL)
+ free(db_cwd);
+/* Just in case someone calls db_init again... */
+ db_cwd = NULL;
+ DBs = NULL;
+}
+
+void db_cd(const char *path)
+{
+ assert(*path == '/');
+ if (db_cwd != NULL)
+ free(db_cwd);
+ db_cwd = strclone(path);
+}
+
+void db_mkdir(const char *path)
+{
+ ht_t *ht, *target;
+ assert(*path == '/');
+ target = ht_get(DBs, path+1);
+ if (target == NULL) {
+ ht = ht_resize(ht_alloc(1), 256);
+ ht_set(DBs, path+1, ht);
+ }
+}
+
+void db_rmdir(const char *path)
+{
+ ht_t *ht;
+ assert(*path == '/');
+ ht = ht_get(DBs, path+1);
+ if (ht == NULL)
+ return;
+ ht_del(DBs, path+1);
+/* ht_free(ht); */
+}
+
+void db_link(const char *existing, const char *new)
+{
+ ht_t *ht;
+
+ assert(*new == '/');
+ ht = ht_get(DBs, existing+1);
+ assert(ht != NULL);
+ ht_set(DBs, new+1, ht);
+ ht->refcount++;
+}
+
+char *concat_nodes(const char *prefix, ...)
+{
+ char *buff;
+ const char *node, *value;
+ int allocated = 256, len, totallen;
+
+ va_list ap;
+ va_start(ap, prefix);
+ buff = malloc(allocated);
+ if (prefix != NULL) {
+ strcpy(buff, prefix);
+ totallen = strlen(prefix);
+ buff[totallen] = ' ';
+ totallen++;
+ }
+ else
+ totallen = 0;
+
+ while((node = va_arg(ap, const char *)) != NULL) {
+ value = get(node);
+ if (value != NULL) {
+ len = strlen(value);
+ if (totallen + len >= allocated) {
+ allocated = totallen + len + 256;
+ buff = realloc(buff, allocated);
+ }
+ memcpy(buff + totallen, value, len);
+ totallen += len;
+ buff[totallen] = ' ';
+ totallen++;
+
+ buff[totallen] = '\0';
+ }
+ }
+
+ buff[totallen - 1] = '\0';
+ va_end(ap);
+ return buff;
+}
+
+int node_istrue(const char *key)
+{
+ const char *s = get(key);
+ if (s == NULL)
+ return 0;
+ return istrue(s);
+}
diff --git a/scconfig/src/default/db.h b/scconfig/src/default/db.h
new file mode 100644
index 0000000..a0e1677
--- /dev/null
+++ b/scconfig/src/default/db.h
@@ -0,0 +1,37 @@
+#include "ht.h"
+
+
+#define strue "true"
+#define sfalse "false"
+#define istrue(s) ((s != NULL) && (*s == 't'))
+#define isfalse(s) ((s != NULL) && (*s == 'f'))
+/* the 3rd option is "unknown" */
+
+/* accessors */
+const char *get(const char *key);
+const char *put(const char *key, const char *data);
+void append(const char *key, const char *value);
+char *concat_nodes(const char *prefix, ...);
+int node_istrue(const char *key);
+
+
+/* init/uninit */
+void db_init(void);
+void db_uninit(void);
+
+/* export/import */
+int export(const char *fn, int export_empty, const char *root);
+int import(const char *fn);
+int import_args(const char *key, const char *fn);
+
+/* file system features */
+extern char *db_cwd;
+void db_cd(const char *path);
+void db_mkdir(const char *path);
+void db_link(const char *existing, const char *new);
+void db_rmdir(const char *path);
+
+extern ht_t *DBs;
+#define iscross (ht_get(DBs, "target") != ht_get(DBs, "host"))
+#define in_cross_target (iscross && (strcmp(db_cwd, "/target") == 0))
+#define in_cross_host (iscross && (strcmp(db_cwd, "/host") == 0))
diff --git a/scconfig/src/default/dep.c b/scconfig/src/default/dep.c
new file mode 100644
index 0000000..f1028a8
--- /dev/null
+++ b/scconfig/src/default/dep.c
@@ -0,0 +1,227 @@
+/*
+ scconfig - dependencies
+ Copyright (C) 2009 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include "dep.h"
+#include "db.h"
+#include "log.h"
+#include "libs.h"
+
+typedef struct {
+ int (*fn)(const char *name, int logdepth, int fatal);
+} fn_wrap_t;
+
+
+static ht_t *deps = NULL;
+
+
+/* find name_ and decide if it was a wildcard request;
+ NOTE: there are requests and servers, both can be wildcard independently.
+ - if a request ends with a / *, it is an explicit wildcard request (*wild=1)
+ - if a request names a "directory" that is wildcard-server, that's a wildcard request (*wild=1)
+ - else the request is a normal request (*wild=0).
+ For normal requests, a required node was explicitly named; if that node is
+ not created by the detection function, that's a failure. For wildcard
+ requests we don't look for any specific node to be created.
+ TODO: we may still check if at least the directory is created
+ */
+fn_wrap_t *get_wrap(const char *name_, int *wild, int *missing)
+{
+ fn_wrap_t *w;
+ char *name, *sep;
+ int len;
+
+ len = strlen(name_);
+ *wild = name_[len-1] == '*';
+
+ if (*wild) {
+ char *pres;
+ pres = malloc(len+16);
+ memcpy(pres, name_, len-1);
+ strcpy(pres+len-1, "presents");
+ *missing = get(pres) == NULL;
+ if (*missing) { /* if there's no /presents, it may be a non-directory node with an actual non-empty string value */
+ const char *val;
+ pres[len-2] = '\0';
+ val = get(pres);
+ if (val != NULL)
+ *missing = !strlen(val);
+ }
+ free(pres);
+ if (!(*missing)) /* already detected, won't be detected again */
+ return NULL;
+ }
+ *missing = 1;
+
+ /* try full match first */
+ w = ht_get(deps, name_);
+ if (w != NULL)
+ return w;
+
+
+ /* try substituting the last part of the path with * for wildcard matches */
+ name = malloc(len+3); /* worst case: ends in a / and we need to append *\0; allocate a bit more */
+ memcpy(name, name_, len+1); /* make a copy we can modify */
+ if (name[len-1] != '/') {
+ name[len] = '/'; /* append a / - if name_ was a "directory", this will result in name/ * */
+ name[len+1] = '\0';
+ }
+
+ *wild = 1; /* if we append a / *, then it's a wildcard request */
+ for(;;) {
+ sep = str_rchr(name, '/');
+ if (sep == NULL)
+ goto error;
+ sep[1] = '*';
+ sep[2] = '\0';
+ w = ht_get(deps, name);
+ if (w != NULL) {
+ free(name);
+ return w;
+ }
+ *sep = '\0';
+ *wild = 0; /* cutting back the second layer - not wildcard request anymore, but a request to a specific node served by a wildcard */
+ }
+
+ /* no match, exit with error */
+ error:;
+ *wild = 0;
+ free(name);
+ return NULL;
+}
+
+
+int require(const char *name, int logdepth, int fatal)
+{
+ fn_wrap_t *w;
+ int wild, missing;
+
+ if (get(name) == NULL) {
+ w = get_wrap(name, &wild, &missing);
+ if (!missing)
+ return 0;
+ if ((w == NULL) || (w->fn == NULL)) {
+ error("Node %s is required but I don't know how to detect it.\n", name);
+ abort();
+ }
+
+ logprintf(logdepth, "(Required node: '%s')\n", name);
+ if (w->fn(name, logdepth+1, fatal) != 0) {
+ if (fatal) {
+ error("Node %s is required but provided detection callback fails to find that feature on that system.\n", name);
+ abort();
+ }
+ else {
+ logprintf(logdepth, "(Feature not found, but it is not fatal)");
+ return 1;
+ }
+ }
+ if ((!wild) && (get(name) == NULL)) {
+ error("Node %s is required but provided detection callback didn't create it (looks like an internal error in scconfig). (db_cwd='%s')\n", name, db_cwd);
+ abort();
+ }
+ }
+ return 0;
+}
+
+const char *dep_add(const char *name, int (*finder)(const char *name, int logdepth, int fatal))
+{
+ fn_wrap_t *w;
+ w = malloc(sizeof(fn_wrap_t));
+ w->fn = finder;
+ return ht_set(deps, name, w);
+}
+
+int asked_for(const char *cando, const char *needtodo)
+{
+ int len;
+
+ /* foo/bar/baz matches /foo/bar/baz */
+ if (strcmp(cando, needtodo) == 0)
+ goto yes;
+
+ len = strlen(needtodo);
+ if (len == 0)
+ return 0;
+
+ /* foo/bar/baz matches /foo/bar/ * */
+ if ((needtodo[len-1] == '*') && (strncmp(cando, needtodo, len-1) == 0))
+ goto yes;
+
+ return 0;
+
+
+ yes:; /* asked for it, but have to see if it's already detected */
+ if (get(cando) != NULL)
+ return 0;
+
+ return 1;
+}
+
+int is_dep_wild(const char *path)
+{
+ int len = strlen(path);
+ if (len == 0)
+ return 0;
+ return (path[len-1] == '*');
+}
+
+const char *det_list_target(const char *path)
+{
+ const char *res;
+
+ if (path == NULL)
+ goto unk;
+
+ res = strrchr(path, '/');
+ if (res == NULL)
+ goto unk;
+
+ return res + 1;
+unk:;
+ return "";
+}
+
+
+void dep_init(void)
+{
+ deps = ht_resize(ht_alloc(0), 128);
+}
+
+void dep_uninit(void)
+{
+ ht_free(deps);
+}
+
+int is_dep_known(const char *name)
+{
+ return (ht_get(deps, name) != NULL);
+}
+
+void require_all(int fatal)
+{
+ ht_entry_t *h;
+
+ for(h = ht_first(deps); h != NULL; h = ht_next(deps, h))
+ require(h->key, 0, fatal);
+}
diff --git a/scconfig/src/default/dep.h b/scconfig/src/default/dep.h
new file mode 100644
index 0000000..4df9063
--- /dev/null
+++ b/scconfig/src/default/dep.h
@@ -0,0 +1,24 @@
+#include "ht.h"
+
+int is_dep_known(const char *name);
+int require(const char *name, int logdepth, int fatal);
+const char *dep_add(const char *name, int (*finder)(const char *name, int logdepth, int fatal));
+void require_all(int fatal);
+
+/* Returns if dependency is a wildcard one (ending in / *) */
+int is_dep_wild(const char *path);
+
+/* Almost 'basename': returns the last portion of the path, which may
+ be '*'. Returns "" on error. */
+const char *det_list_target(const char *path);
+
+/* Returns 1 if the user asked for detecting a feature; needtodo is
+ the first argument passed to the detection function (the target the caller
+ wants to get detected), cando is the output path of the test that the
+ detector could do next. */
+int asked_for(const char *cando, const char *needtodo);
+
+/* for internal use */
+void dep_init(void);
+void dep_uninit(void);
+
diff --git a/scconfig/src/default/deps_default.c b/scconfig/src/default/deps_default.c
new file mode 100644
index 0000000..da8c503
--- /dev/null
+++ b/scconfig/src/default/deps_default.c
@@ -0,0 +1,167 @@
+/*
+ scconfig - dependency list of default tests
+ Copyright (C) 2009..2012 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include "dep.h"
+#include "find.h"
+
+void deps_default_init(void)
+{
+ dep_add("cc/cc", find_cc);
+ dep_add("cc/argstd/*", find_cc_argstd);
+ dep_add("cc/cflags", find_cc);
+ dep_add("cc/ldflags", find_cc);
+ dep_add("cc/inline", find_inline);
+ dep_add("cc/varargmacro", find_varargmacro);
+ dep_add("cc/funcmacro", find_funcmacro);
+ dep_add("cc/constructor", find_constructor);
+ dep_add("cc/destructor", find_destructor);
+ dep_add("cc/rdynamic", find_rdynamic);
+ dep_add("cc/soname", find_soname);
+ dep_add("cc/wlrpath", find_wlrpath);
+ dep_add("cc/wloutimplib", find_cc_wloutimplib);
+ dep_add("cc/wloutputdef", find_cc_wloutputdef);
+ dep_add("cc/fpic", find_fpic);
+ dep_add("cc/fpie/*", find_cc_fpie);
+ dep_add("cc/fnopie/*", find_cc_fnopie);
+ dep_add("cc/fnopic/*", find_cc_fnopic);
+ dep_add("cc/alloca/*", find_alloca);
+ dep_add("cc/_exit/*", find__exit);
+ dep_add("cc/ldflags_dynlib", find_ldflags_dynlib);
+ dep_add("cc/ldflags_dll", find_ldflags_dll);
+ dep_add("cc/ldflags_so", find_ldflags_so);
+ dep_add("cc/func_attr/unused/*", find_fattr_unused);
+ dep_add("cc/declspec/dllimport/*", find_declspec_dllimport);
+ dep_add("cc/declspec/dllexport/*", find_declspec_dllexport);
+ dep_add("cc/argmachine/*", find_cc_argmachine);
+ dep_add("libs/ldl", find_lib_ldl);
+ dep_add("libs/LoadLibrary/*", find_lib_LoadLibrary);
+ dep_add("libs/lpthread", find_lib_lpthread);
+ dep_add("libs/lpthread-recursive", find_lib_lpthread);
+ dep_add("thread/semget/*", find_thread_semget);
+ dep_add("thread/pthread_create/*", find_thread_pthread_create);
+ dep_add("thread/CreateSemaphore/*", find_thread_CreateSemaphore);
+ dep_add("thread/CreateThread/*", find_thread_CreateThread);
+ dep_add("libs/errno/*", find_lib_errno);
+ dep_add("libs/printf_x", find_printf_x);
+ dep_add("libs/printf_ptrcast", find_printf_ptrcast);
+ dep_add("libs/snprintf", find_snprintf);
+ dep_add("libs/snprintf_safe", find_snprintf);
+ dep_add("libs/dprintf", find_dprintf);
+ dep_add("libs/vdprintf", find_vdprintf);
+ dep_add("libs/vsnprintf", find_vsnprintf);
+ dep_add("libs/proc/_spawnvp/*", find_proc__spawnvp);
+ dep_add("libs/proc/fork/*", find_proc_fork);
+ dep_add("libs/proc/wait/*", find_proc_wait);
+ dep_add("libs/fs/realpath/*", find_fs_realpath);
+ dep_add("libs/fs/_fullpath/*", find_fs__fullpath);
+ dep_add("libs/fs/readdir/*", find_fs_readdir);
+ dep_add("libs/fs/findnextfile/*", find_fs_findnextfile);
+ dep_add("libs/fs/stat/macros/*", find_fs_stat_macros);
+ dep_add("libs/fs/stat/fields/*", find_fs_stat_fields);
+ dep_add("libs/fs/access/*", find_fs_access);
+ dep_add("libs/fs/access/macros/*", find_fs_access_macros);
+ dep_add("libs/fs/lstat/*", find_fs_lstat);
+ dep_add("libs/fs/statlstat/*", find_fs_statlstat);
+ dep_add("libs/fs/getcwd/*", find_fs_getcwd);
+ dep_add("libs/fs/_getcwd/*", find_fs__getcwd);
+ dep_add("libs/fs/getwd/*", find_fs_getwd);
+ dep_add("libs/fs/mkdir/*", find_fs_mkdir);
+ dep_add("libs/fs/_mkdir/*", find_fs__mkdir);
+ dep_add("libs/fs/mkdtemp/*", find_fs_mkdtemp);
+ dep_add("libs/fs/mmap/*", find_fs_mmap);
+ dep_add("libs/fsmount/next_dev/*", find_fsmount_next_dev);
+ dep_add("libs/fsmount/struct_fsstat/*",find_fsmount_fsstat_fields);
+ dep_add("libs/fsmount/struct_statfs/*",find_fsmount_statfs_fields);
+ dep_add("libs/fsmount/struct_statvfs/*",find_fsmount_statvfs_fields);
+ dep_add("libs/fs/ustat/*", find_fs_ustat);
+ dep_add("libs/fs/statfs/*", find_fs_statfs);
+ dep_add("libs/fs/statvfs/*", find_fs_statvfs);
+ dep_add("libs/fs/flock/*", find_fs_flock);
+
+ dep_add("libs/io/pipe/*", find_io_pipe);
+ dep_add("libs/io/dup2/*", find_io_dup2);
+ dep_add("libs/io/fileno/*", find_io_fileno);
+ dep_add("libs/io/lseek/*", find_io_lseek);
+ dep_add("libs/io/popen/*", find_io_popen);
+ dep_add("libs/time/usleep/*", find_time_usleep);
+ dep_add("libs/types/stdint/*", find_types_stdint);
+ dep_add("sys/types/size/*", find_types_sizes);
+ dep_add("libs/time/Sleep/*", find_time_Sleep);
+ dep_add("libs/time/gettimeofday/*", find_time_gettimeofday);
+ dep_add("libs/time/ftime/*", find_time_ftime);
+ dep_add("libs/time/timegm/*", find_time_timegm);
+ dep_add("libs/time/_mkgmtime/*", find_time_mkgmtime);
+ dep_add("libs/time/gmtime_r/*", find_time_gmtime_r);
+ dep_add("libs/time/gmtime_s/*", find_time_gmtime_s);
+ dep_add("libs/env/main_3arg/*", find_main_arg3);
+ dep_add("libs/env/putenv/*", find_putenv);
+ dep_add("libs/env/setenv/*", find_setenv);
+ dep_add("libs/env/environ/*", find_environ);
+ dep_add("signal/raise/*", find_signal_raise);
+ dep_add("signal/names/*", find_signal_names);
+ dep_add("fstools/cp", find_fstools_cp);
+ dep_add("fstools/ln", find_fstools_ln);
+ dep_add("fstools/mv", find_fstools_mv);
+ dep_add("fstools/rm", find_fstools_rm);
+ dep_add("fstools/mkdir", find_fstools_mkdir);
+ dep_add("fstools/ar", find_fstools_ar);
+ dep_add("fstools/ranlib", find_fstools_ranlib);
+ dep_add("fstools/awk", find_fstools_awk);
+ dep_add("fstools/cat", find_fstools_cat);
+ dep_add("fstools/sed", find_fstools_sed);
+ dep_add("fstools/file_l", find_fstools_file_l);
+ dep_add("fstools/file", find_fstools_file);
+ dep_add("fstools/chmodx", find_fstools_chmodx);
+ dep_add("sys/name", find_uname);
+ dep_add("sys/uname", find_uname);
+ dep_add("sys/triplet", find_triplet);
+ dep_add("sys/sysid", find_sysid);
+ dep_add("sys/shell", find_shell);
+ dep_add("sys/shell_needs_quote", find_shell);
+ dep_add("sys/tmp", find_tmp);
+ dep_add("sys/shell_eats_backslash", find_tmp);
+ dep_add("sys/ext_exe", find_uname);
+ dep_add("sys/ext_dynlib", find_uname);
+ dep_add("sys/ext_dynlib_native", find_uname);
+ dep_add("sys/ext_stalib", find_uname);
+ dep_add("sys/class", find_uname);
+ dep_add("sys/path_sep", find_uname);
+ dep_add("sys/ptrwidth", find_sys_ptrwidth);
+ dep_add("sys/byte_order", find_sys_byte_order);
+ dep_add("sys/types/size_t/*", find_types_size_t);
+ dep_add("sys/types/off_t/*", find_types_off_t);
+ dep_add("sys/types/off64_t/*", find_types_off64_t);
+ dep_add("sys/types/gid_t/*", find_types_gid_t);
+ dep_add("sys/types/uid_t/*", find_types_uid_t);
+ dep_add("sys/types/pid_t/*", find_types_pid_t);
+ dep_add("sys/types/mode_t/*", find_types_mode_t);
+ dep_add("sys/types/nlink_t/*", find_types_nlink_t);
+ dep_add("sys/types/ptrdiff_t/*", find_types_ptrdiff_t);
+ dep_add("sys/types/dev_t/*", find_types_dev_t);
+ dep_add("sys/types/ino_t/*", find_types_ino_t);
+ dep_add("sys/types/void_ptr/*", find_types_void_ptr);
+ dep_add("str/strcasecmp/*", find_strcasecmp);
+ dep_add("str/strncasecmp/*", find_strncasecmp);
+
+ dep_add("/internal/filelist/cmd", find_filelist);
+ dep_add("/internal/filelist/method", find_filelist);
+}
diff --git a/scconfig/src/default/deps_default.h b/scconfig/src/default/deps_default.h
new file mode 100644
index 0000000..77ebf1f
--- /dev/null
+++ b/scconfig/src/default/deps_default.h
@@ -0,0 +1 @@
+void deps_default_init(void);
diff --git a/scconfig/src/default/find.h b/scconfig/src/default/find.h
new file mode 100644
index 0000000..4314e81
--- /dev/null
+++ b/scconfig/src/default/find.h
@@ -0,0 +1,159 @@
+/* cc */
+int find_cc(const char *name, int logdepth, int fatal);
+int find_cc_argstd(const char *name, int logdepth, int fatal);
+int find_cc_argmachine(const char *name, int logdepth, int fatal);
+int find_cc_fpie(const char *name, int logdepth, int fatal);
+int find_cc_fnopie(const char *name, int logdepth, int fatal);
+int find_cc_fnopic(const char *name, int logdepth, int fatal);
+int find_inline(const char *name, int logdepth, int fatal);
+int find_varargmacro(const char *name, int logdepth, int fatal);
+int find_funcmacro(const char *name, int logdepth, int fatal);
+int find_constructor(const char *name, int logdepth, int fatal);
+int find_destructor(const char *name, int logdepth, int fatal);
+int find_fattr_unused(const char *name, int logdepth, int fatal);
+int find_declspec_dllimport(const char *name, int logdepth, int fatal);
+int find_declspec_dllexport(const char *name, int logdepth, int fatal);
+int find_rdynamic(const char *name, int logdepth, int fatal);
+int find_soname(const char *name, int logdepth, int fatal);
+int find_wlrpath(const char *name, int logdepth, int fatal);
+int find_cc_wloutimplib(const char *name, int logdepth, int fatal);
+int find_cc_wloutputdef(const char *name, int logdepth, int fatal);
+int find_fpic(const char *name, int logdepth, int fatal);
+int find_ldflags_dynlib(const char *name, int logdepth, int fatal);
+int find_ldflags_dll(const char *name, int logdepth, int fatal);
+int find_ldflags_so(const char *name, int logdepth, int fatal);
+int find_alloca(const char *name, int logdepth, int fatal);
+int find__exit(const char *name, int logdepth, int fatal);
+
+/* libs */
+int find_lib_ldl(const char *name, int logdepth, int fatal);
+int find_lib_LoadLibrary(const char *name, int logdepth, int fatal);
+int find_lib_errno(const char *name, int logdepth, int fatal);
+
+/* thread */
+int find_lib_lpthread(const char *name, int logdepth, int fatal);
+int find_thread_semget(const char *name, int logdepth, int fatal);
+int find_thread_pthread_create(const char *name, int logdepth, int fatal);
+int find_thread_CreateSemaphore(const char *name, int logdepth, int fatal);
+int find_thread_CreateThread(const char *name, int logdepth, int fatal);
+
+/* fscalls */
+int find_fs_realpath(const char *name, int logdepth, int fatal);
+int find_fs__fullpath(const char *name, int logdepth, int fatal);
+int find_fs_readdir(const char *name, int logdepth, int fatal);
+int find_fs_findnextfile(const char *name, int logdepth, int fatal);
+int find_fs_access(const char *name, int logdepth, int fatal);
+int find_fs_access_macros(const char *name, int logdepth, int fatal);
+int find_fs_stat_macros(const char *name, int logdepth, int fatal);
+int find_fs_stat_fields(const char *name, int logdepth, int fatal);
+int find_fs_lstat(const char *name, int logdepth, int fatal);
+int find_fs_statlstat(const char *name, int logdepth, int fatal);
+int find_fs_getcwd(const char *name, int logdepth, int fatal);
+int find_fs__getcwd(const char *name, int logdepth, int fatal);
+int find_fs_getwd(const char *name, int logdepth, int fatal);
+int find_fs_mkdir(const char *name, int logdepth, int fatal);
+int find_fs__mkdir(const char *name, int logdepth, int fatal);
+int find_fs_mkdtemp(const char *name, int logdepth, int fatal);
+int find_fs_mmap(const char *name, int logdepth, int fatal);
+int find_fsmount_next_dev(const char *name, int logdepth, int fatal);
+int find_fsmount_fsstat_fields(const char *name, int logdepth, int fatal);
+int find_fsmount_statfs_fields(const char *name, int logdepth, int fatal);
+int find_fsmount_statvfs_fields(const char *name, int logdepth, int fatal);
+int find_fs_ustat(const char *name, int logdepth, int fatal);
+int find_fs_statfs(const char *name, int logdepth, int fatal);
+int find_fs_statvfs(const char *name, int logdepth, int fatal);
+int find_fs_flock(const char *name, int logdepth, int fatal);
+
+/* printf */
+int find_printf_x(const char *name, int logdepth, int fatal);
+int find_printf_ptrcast(const char *name, int logdepth, int fatal);
+int find_snprintf(const char *name, int logdepth, int fatal);
+int find_dprintf(const char *name, int logdepth, int fatal);
+int find_vdprintf(const char *name, int logdepth, int fatal);
+int find_vsnprintf(const char *name, int logdepth, int fatal);
+
+/* proc */
+int find_proc__spawnvp(const char *name, int logdepth, int fatal);
+int find_proc_fork(const char *name, int logdepth, int fatal);
+int find_proc_wait(const char *name, int logdepth, int fatal);
+
+/* fstools */
+int find_fstools_cp(const char *name, int logdepth, int fatal);
+int find_fstools_ln(const char *name, int logdepth, int fatal);
+int find_fstools_mv(const char *name, int logdepth, int fatal);
+int find_fstools_rm(const char *name, int logdepth, int fatal);
+int find_fstools_mkdir(const char *name, int logdepth, int fatal);
+int find_fstools_ar(const char *name, int logdepth, int fatal);
+int find_fstools_ranlib(const char *name, int logdepth, int fatal);
+int find_fstools_awk(const char *name, int logdepth, int fatal);
+int find_fstools_cat(const char *name, int logdepth, int fatal);
+int find_fstools_sed(const char *name, int logdepth, int fatal);
+int find_fstools_file(const char *name, int logdepth, int fatal);
+int find_fstools_file_l(const char *name, int logdepth, int fatal);
+int find_fstools_chmodx(const char *name, int logdepth, int fatal);
+
+/* uname */
+int find_uname(const char *name, int logdepth, int fatal);
+int find_triplet(const char *name, int logdepth, int fatal);
+int find_sysid(const char *name, int logdepth, int fatal);
+
+/* find_target */
+int find_target(const char *name, int logdepth, int fatal);
+
+/* filelist */
+int find_filelist(const char *name, int logdepth, int fatal);
+
+/* find_str.c */
+int find_strcasecmp(const char *name, int logdepth, int fatal);
+int find_strncasecmp(const char *name, int logdepth, int fatal);
+
+/* find_sys.c */
+int find_sys_ptrwidth(const char *name, int logdepth, int fatal);
+int find_sys_byte_order(const char *name, int logdepth, int fatal);
+int find_tmp(const char *name, int logdepth, int fatal);
+int find_shell(const char *name, int logdepth, int fatal);
+
+/* find_io.c */
+int find_io_pipe(const char *name, int logdepth, int fatal);
+int find_io_dup2(const char *name, int logdepth, int fatal);
+int find_io_fileno(const char *name, int logdepth, int fatal);
+int find_io_lseek(const char *name, int logdepth, int fatal);
+int find_io_popen(const char *name, int logdepth, int fatal);
+
+/* find_time.c */
+int find_time_usleep(const char *name, int logdepth, int fatal);
+int find_time_Sleep(const char *name, int logdepth, int fatal);
+int find_time_gettimeofday(const char *name, int logdepth, int fatal);
+int find_time_ftime(const char *name, int logdepth, int fatal);
+int find_time_timegm(const char *name, int logdepth, int fatal);
+int find_time_mkgmtime(const char *name, int logdepth, int fatal);
+int find_time_gmtime_s(const char *name, int logdepth, int fatal);
+int find_time_gmtime_r(const char *name, int logdepth, int fatal);
+
+/* find_types.c */
+int find_types_stdint(const char *name, int logdepth, int fatal);
+int find_types_sizes(const char *name, int logdepth, int fatal);
+int find_types_size_t(const char *name, int logdepth, int fatal);
+int find_types_off_t(const char *name, int logdepth, int fatal);
+int find_types_off64_t(const char *name, int logdepth, int fatal);
+int find_types_gid_t(const char *name, int logdepth, int fatal);
+int find_types_uid_t(const char *name, int logdepth, int fatal);
+int find_types_pid_t(const char *name, int logdepth, int fatal);
+int find_types_dev_t(const char *name, int logdepth, int fatal);
+int find_types_dev_t(const char *name, int logdepth, int fatal);
+int find_types_mode_t(const char *name, int logdepth, int fatal);
+int find_types_nlink_t(const char *name, int logdepth, int fatal);
+int find_types_ptrdiff_t(const char *name, int logdepth, int fatal);
+int find_types_dev_t(const char *name, int logdepth, int fatal);
+int find_types_ino_t(const char *name, int logdepth, int fatal);
+int find_types_void_ptr(const char *name, int logdepth, int fatal);
+
+/* find_signal.c */
+int find_signal_names(const char *name, int logdepth, int fatal);
+int find_signal_raise(const char *name, int logdepth, int fatal);
+
+/* environ.c */
+int find_main_arg3(const char *name, int logdepth, int fatal);
+int find_putenv(const char *name, int logdepth, int fatal);
+int find_setenv(const char *name, int logdepth, int fatal);
+int find_environ(const char *name, int logdepth, int fatal);
diff --git a/scconfig/src/default/find_cc.c b/scconfig/src/default/find_cc.c
new file mode 100644
index 0000000..6403459
--- /dev/null
+++ b/scconfig/src/default/find_cc.c
@@ -0,0 +1,1037 @@
+/*
+ scconfig - detection of cc and compiler features
+ Copyright (C) 2009..2012 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+
+
+static int try_flags(int logdepth, const char *cc, const char *test_c, const char *cflags, const char *ldflags, const char *expected)
+{
+ char *out;
+
+ logprintf(logdepth, "trying cc:try_flags with cc='%s' cflags='%s' ldflags='%s'\n", (cc == NULL ? get("cc/cc") : cc), cflags == NULL ? "" : cflags, ldflags == NULL ? "" : ldflags);
+
+ if (compile_run(logdepth+1, test_c, cc, cflags, ldflags, &out) == 0) {
+ if (((out == NULL) && (iscross)) || (strncmp(out, expected, strlen(expected)) == 0)) {
+ free(out);
+ return 1;
+ }
+ free(out);
+ }
+ return 0;
+}
+
+static int try_flags_inv(int logdepth, const char *cc, const char *test_c, const char *cflags, const char *ldflags, const char *expected_bad)
+{
+ char *out;
+
+ logprintf(logdepth, "trying cc:try_flags with cc='%s' cflags='%s' ldflags='%s'\n", (cc == NULL ? get("cc/cc") : cc), cflags == NULL ? "" : cflags, ldflags == NULL ? "" : ldflags);
+
+ if (compile_run(logdepth+1, test_c, cc, cflags, ldflags, &out) == 0) {
+ if (((out == NULL) && (iscross)) || (strncmp(out, expected_bad, strlen(expected_bad)) != 0)) {
+ free(out);
+ return 1;
+ }
+ free(out);
+ }
+ return 0;
+}
+
+static int try(int logdepth, const char *cc, const char *test_c, const char *expected)
+{
+ return try_flags(logdepth, cc, test_c, NULL, NULL, expected);
+}
+
+
+static int trycc(int logdepth, const char *cc, const char *test_c)
+{
+ int ret;
+
+ if (cc == NULL)
+ return 0;
+
+ ret = try(logdepth, cc, test_c, "OK");
+ if (ret)
+ put("cc/cc", cc);
+ return ret;
+}
+
+int find_cc(const char *name, int logdepth, int fatal)
+{
+ char *test_c = "#include \nint main() { printf(\"OK\\n\");\nreturn 0;}\n";
+ char *out = NULL, *targetcc;
+ const char *cc, *cflags, *ldflags, *target, *sys;
+ int len;
+
+ require("sys/name", logdepth, fatal);
+
+ sys = istarget(db_cwd) ? "target" : "host";
+ report("Checking for cc (%s)... ", sys);
+ logprintf(logdepth, "find_cc: trying to find cc (%s)...\n", sys);
+ logdepth++;
+
+ /* cflags */
+ cflags = get("/arg/cc/cflags");
+ if (cflags != NULL) {
+ logprintf(logdepth+1, "using user supplied cflags '%s'\n", cflags);
+ put("cc/cflags", cflags);
+ }
+
+ /* ldflags */
+ ldflags = get("/arg/cc/ldflags");
+ if (ldflags != NULL) {
+ logprintf(logdepth+1, "using user supplied ldflags '%s'\n", ldflags);
+ put("cc/ldflags", ldflags);
+ }
+
+ cc = get("/arg/cc/cc");
+ if (cc == NULL) {
+ target = get("sys/target");
+ if (target != NULL) {
+ logprintf(logdepth+1, "find_cc: crosscompiling for '%s', looking for target cc\n", target);
+ len = strlen(target);
+ targetcc = malloc(len + 8);
+ memcpy(targetcc, target, len);
+ strcpy(targetcc + len, "-gcc");
+ if (!trycc(logdepth+1, targetcc, test_c)) {
+ strcpy(targetcc + len, "-cc");
+ if (!trycc(logdepth+1, targetcc, test_c)) {
+ report("FAILED: failed to find crosscompiler for target '%s'\n", target);
+ logprintf(logdepth, "find_cc: FAILED to find a crosscompiler for target '%s'\n", target);
+ return 1;
+ }
+ }
+ put("cc/cc", targetcc);
+ }
+ else {
+ cc = getenv("CC");
+ logprintf(logdepth, "find_cc: Detecting cc (host)\n");
+ /* Find a working cc (no arguments) */
+ if (!(((cc != NULL) && (trycc(logdepth+1, cc, test_c))) || trycc(logdepth+1, "gcc", test_c) || trycc(logdepth+1, "cc", test_c))) {
+ report("FAILED to find a compiler\n");
+ logprintf(logdepth, "find_cc: FAILED to find a compiler\n");
+ return 1;
+ }
+ }
+ }
+ else {
+ put("cc/cc", cc);
+ logprintf(logdepth+1, "using user supplied '%s' (will test later)\n", cc);
+ }
+
+ /* cflags (again) */
+ if (cflags == NULL) {
+ logprintf(logdepth, "find_cc: Detecting -pipe\n");
+
+ if (compile_run(logdepth+1, test_c, NULL, "-pipe", "", &out) == 0) {
+ if (target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) {
+ append("cc/cflags", " -pipe");
+ }
+ free(out);
+ }
+ }
+ if (get("cc/cflags") == NULL)
+ put("cc/cflags", "");
+
+ /* ldflags (again) */
+ if (get("cc/ldflags") == NULL)
+ put("cc/ldflags", "");
+
+ /* Final test of all arguments together */
+ logprintf(logdepth, "find_cc: final test on cc and all flags \n");
+ if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) != 0) {
+ report("FAILED to get the compiler and all flags to work together\n");
+ logprintf(logdepth, "find_cc: the compiler and all the flags don't work well together, aborting\n");
+ if (out != NULL)
+ free(out);
+ return 1;
+ }
+
+ report("OK ('%s', '%s', '%s')\n", get("cc/cc"), get("cc/cflags"), get("cc/ldflags"));
+ logprintf(logdepth, "find_cc: conclusion: cc='%s' cflags='%s' ldflags='%s'\n", get("cc/cc"), get("cc/cflags"), get("cc/ldflags"));
+ if (out != NULL)
+ free(out);
+ return 0;
+}
+
+int find_cc_argstd(const char *det_name, int logdepth, int fatal)
+{
+ char *test_c = "#include \nint main() { printf(\"OK\\n\");\nreturn 0;}\n";
+ char *out = NULL;
+ char **flg, *flags[] = {"-ansi", "-pedantic", "-Wall", "-std=c89", "-std=c99", "-Werror", "-Wextra", "-W", "-pg", "-no-pie", "-static-pie", NULL};
+ const char *det_target = det_list_target(det_name);
+
+ require("cc/cc", logdepth, fatal);
+
+ logprintf(logdepth, "find_cc: Detecting CC args %s\n", det_target);
+ report("Checking for cc args for std %s... ", det_target);
+
+ for(flg = flags; *flg != NULL; flg++) {
+ char name[128], *end;
+ const char *found = "";
+ sprintf(name, "cc/argstd/%s", (*flg)+1);
+ end = strchr(name, '=');
+ if (end != NULL)
+ *end = '_';
+ if (!asked_for(name, det_name))
+ continue;
+ if (compile_run(logdepth+1, test_c, NULL, *flg, "", &out) == 0) {
+ if (target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) {
+ found = *flg;
+ report(" ");
+ report(found);
+ }
+ free(out);
+ }
+ put(name, found);
+ }
+
+ if (is_dep_wild(det_name))
+ put("cc/argstd/presents", strue); /* to avoid re-detection*/
+
+ report("\n");
+ return 0;
+}
+
+int find_cc_argmachine(const char *name, int logdepth, int fatal)
+{
+#define ARGM(flag) "-m" #flag , "-mno-" #flag
+ const char *test_c = "#include \nint main() { printf(\"OK\\n\");\nreturn 0;}\n";
+ char *out = NULL;
+ const char **flg, *flags[] = { ARGM(mmx), ARGM(sse), ARGM(sse2), ARGM(sse3), ARGM(ssse3), ARGM(sse4), ARGM(sse4.1), ARGM(sse4.2), ARGM(avx), ARGM(avx2), NULL};
+
+ require("cc/cc", logdepth, fatal);
+
+ logprintf(logdepth, "find_cc: Detecting CC machine args\n");
+ report("Checking for cc args for machine... ");
+
+ for(flg = flags; *flg != NULL; flg++) {
+ char name[128], *end;
+ const char *found = "";
+ {
+ const char* ptr = (*flg) + 1;
+ strcpy(name, "cc/argmachine/");
+ end = name + strlen(name);
+ while(*ptr) {
+ if('.'!=*ptr && '-'!=*ptr) *end++ = *ptr;
+ ++ptr;
+ }
+ *end = '\0';
+ }
+ end = strchr(name, '=');
+ if (end != NULL)
+ *end = '_';
+ if (compile_run(logdepth+1, test_c, NULL, *flg, "", &out) == 0) {
+ if (target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) {
+ found = *flg;
+ report(" ");
+ report(found);
+ }
+ free(out);
+ }
+ put(name, found);
+ }
+
+ report("\n");
+ return 0;
+#undef ARGM
+}
+
+int find_inline(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "static inline void test_inl()"
+ NL "{"
+ NL " puts(\"OK\");"
+ NL "}"
+ NL "int main() {"
+ NL " test_inl();"
+ NL " return 0;"
+ NL "}"
+ NL ;
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for inline... ");
+ logprintf(logdepth, "find_inline: trying to find inline...\n");
+ logdepth++;
+ if (try(logdepth, NULL, test_c, "OK")) {
+ put("cc/inline", strue);
+ report("Found.\n");
+ return 0;
+ }
+ put("cc/inline", sfalse);
+ report("Not found.\n");
+ return 1;
+}
+
+int find_varargmacro(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "#define pr(fmt, x...) {printf(\"PR \"); printf(fmt, x); }"
+ NL "int main() {"
+ NL " pr(\"%d %d %s\", 42, 8192, \"test\");"
+ NL " puts(\"\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for vararg macro... ");
+ logprintf(logdepth, "find_varargmacro: trying to find vararg macro...\n");
+ logdepth++;
+ if (try(logdepth, NULL, test_c, "PR 42 8192 test")) {
+ put("cc/varargmacro", strue);
+ report("Found.\n");
+ return 0;
+ }
+ put("cc/varargmacro", sfalse);
+ report("Not found.\n");
+ return 1;
+}
+
+int find_funcmacro(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " printf(\"%s\\n\", __func__);"
+ NL " return 0;"
+ NL "}"
+ NL ;
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for __func__ macro... ");
+ logprintf(logdepth, "find_funcmacro: trying to find __func__ macro...\n");
+ logdepth++;
+ if (try(logdepth, NULL, test_c, "main")) {
+ put("cc/funcmacro", strue);
+ report("Found.\n");
+ return 0;
+ }
+ put("cc/funcmacro", sfalse);
+ report("Not found.\n");
+ return 1;
+}
+
+int find_constructor(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "void startup() __attribute__ ((constructor));"
+ NL "void startup()"
+ NL "{"
+ NL " puts(\"OK\");"
+ NL "}"
+ NL "int main() {"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for constructor... ");
+ logprintf(logdepth, "find_constructor: trying to find constructor...\n");
+ logdepth++;
+ if (try(logdepth, NULL, test_c, "OK")) {
+ put("cc/constructor", strue);
+ report("Found.\n");
+ return 0;
+ }
+ put("cc/constructor", sfalse);
+ report("Not found.\n");
+ return 1;
+}
+
+int find_destructor(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "void startup() __attribute__ ((destructor));"
+ NL "void startup()"
+ NL "{"
+ NL " puts(\"OK\");"
+ NL "}"
+ NL "int main() {"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for destructor... ");
+ logprintf(logdepth, "find_destructor: trying to find destructor...\n");
+ logdepth++;
+ if (try(logdepth, NULL, test_c, "OK")) {
+ put("cc/destructor", strue);
+ report("Found.\n");
+ return 0;
+ }
+ put("cc/destructor", sfalse);
+ report("Not found.\n");
+ return 1;
+}
+
+static int test_fattr(const char *name, int logdepth, int fatal, const char *fattr)
+{
+ char path[64];
+ char test_c[256];
+ const char *test_c_tmp =
+ NL "#include "
+ NL "static void test1() __attribute__ ((%s));"
+ NL "static void test1()"
+ NL "{"
+ NL " puts(\"OK\");"
+ NL "}"
+ NL "int main() {"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ require("cc/cc", logdepth, fatal);
+
+ sprintf(test_c, test_c_tmp, fattr);
+ sprintf(path, "cc/func_attr/%s/presents", fattr);
+
+ report("Checking for function attribute %s... ", fattr);
+ logprintf(logdepth, "test_fattr: trying to find %s...\n", fattr);
+ logdepth++;
+ if (try(logdepth, NULL, test_c, "OK")) {
+ put(path, strue);
+ report("Found.\n");
+ return 0;
+ }
+ put(path, sfalse);
+ report("Not found.\n");
+ return 1;
+}
+
+int find_fattr_unused(const char *name, int logdepth, int fatal)
+{
+ return test_fattr(name, logdepth, fatal, "unused");
+}
+
+static int test_declspec(const char *name, int logdepth, int fatal, const char *dspec)
+{
+ char path[64];
+ char test_c[256];
+ const char *test_c_tmp =
+ NL "#include "
+ NL "void __declspec (%s) test1();"
+ NL "void test1()"
+ NL "{"
+ NL " puts(\"OK\");"
+ NL "}"
+ NL "int main() {"
+ NL " test1();"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ require("cc/cc", logdepth, fatal);
+
+ sprintf(test_c, test_c_tmp, dspec);
+ sprintf(path, "cc/declspec/%s/presents", dspec);
+
+ report("Checking for declspec %s... ", dspec);
+ logprintf(logdepth, "test_declspec: trying to find %s...\n", dspec);
+ logdepth++;
+ if (try(logdepth, NULL, test_c, "OK")) {
+ put(path, strue);
+ report("Found.\n");
+ return 0;
+ }
+ put(path, sfalse);
+ report("Not found.\n");
+ return 1;
+}
+
+int find_declspec_dllimport(const char *name, int logdepth, int fatal)
+{
+ return test_declspec(name, logdepth, fatal, "dllimport");
+}
+
+int find_declspec_dllexport(const char *name, int logdepth, int fatal)
+{
+ return test_declspec(name, logdepth, fatal, "dllexport");
+}
+
+static int test_dll_auxfile(const char *name, int logdepth, int fatal, const char *path, const char *ldflag, const char *filename)
+{
+ char *ldflags;
+ char test_c[256];
+ const char *test_c_template =
+ NL "#include "
+ NL "void %s test1();"
+ NL "void test1()"
+ NL "{"
+ NL " puts(\"OK\");"
+ NL "}"
+ NL "int main() {"
+ NL " test1();"
+ NL " return 0;"
+ NL "}"
+ NL ;
+ const char *dspec;
+
+ require("cc/cc", logdepth, fatal);
+ require("cc/declspec/dllexport/*", logdepth, 0);
+
+ if (istrue("cc/declspec/dllexport/presents"))
+ dspec = " __declspec(dllexport) ";
+ else
+ dspec = "";
+
+ sprintf(test_c, test_c_template, dspec);
+
+ report("Checking for DLL flag %s... ", ldflag);
+ logprintf(logdepth, "test_dll_auxfile: trying to find %s...\n", ldflag);
+ logdepth++;
+ ldflags = str_concat("", ldflag, ",", filename, " ", get("cc/ldflags"), NULL);
+ if (try_flags(logdepth, NULL, test_c, NULL, ldflags, "OK")) {
+ unlink(filename);
+ put(path, ldflag);
+ free(ldflags);
+ report("Found.\n");
+ return 0;
+ }
+ unlink(filename);
+ free(ldflags);
+ report("Not found.\n");
+ return 1;
+}
+
+int find_cc_wloutimplib(const char *name, int logdepth, int fatal)
+{
+ return test_dll_auxfile(name, logdepth, fatal, "cc/wloutimplib", "-Wl,--out-implib", "libscconfig_0.a");
+}
+
+int find_cc_wloutputdef(const char *name, int logdepth, int fatal)
+{
+ return test_dll_auxfile(name, logdepth, fatal, "cc/wloutputdef", "-Wl,--output-def", "libscconfig_0.def");
+}
+
+/* Hello world program to test compiler flags */
+static const char *test_hello_world =
+ NL "#include "
+ NL "int main() {"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+static int try_hello(int logdepth, const char *cflags, const char *ldflags, const char *name, const char *value)
+{
+ if (try_flags(logdepth, NULL, test_hello_world, cflags, ldflags, "OK")) {
+ put(name, value);
+ report("OK (%s)\n", value);
+ return 1;
+ }
+ return 0;
+}
+
+int find_rdynamic(const char *name, int logdepth, int fatal)
+{
+ const char *node = "cc/rdynamic";
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for rdynamic... ");
+ logprintf(logdepth, "find_rdynamic: trying to find rdynamic...\n");
+ logdepth++;
+
+ if (try_hello(logdepth, NULL, "-rdynamic", node, "-rdynamic")) return 0;
+ if (try_hello(logdepth, NULL, "-Wl,-export-dynamic", node, "-Wl,-export-dynamic")) return 0;
+ if (try_hello(logdepth, NULL, NULL, node, "")) return 0;
+
+ report("Not found.\n");
+ return 1;
+}
+
+int find_cc_fpie(const char *name, int logdepth, int fatal)
+{
+ const char *test_c = test_hello_world;
+
+ require("cc/cc", logdepth, fatal);
+ /* TODO: what about -fpic? */
+
+ report("Checking for -fpie... ");
+ logprintf(logdepth, "find_cc_fpie: trying to find -fpie...\n");
+ logdepth++;
+
+ /* NOTE: some gcc configuration might not pass the -pie flag to the linker, so */
+ /* try to detect whether we can force it to the linker */
+ if (try_icl(logdepth, "cc/fpie", test_c, NULL, "-fpie", "-pie -Wl,-pie")) return 0;
+ if (try_icl(logdepth, "cc/fpie", test_c, NULL, "-fPIE", "-pie -Wl,-pie")) return 0;
+ if (try_icl(logdepth, "cc/fpie", test_c, NULL, "-fpie", "-pie")) return 0;
+ if (try_icl(logdepth, "cc/fpie", test_c, NULL, "-fPIE", "-pie")) return 0;
+ if (try_icl(logdepth, "cc/fpie", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "cc/fpie");
+}
+
+int find_cc_fnopie(const char *name, int logdepth, int fatal)
+{
+ const char *test_c = test_hello_world;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for -fno-pie... ");
+ logprintf(logdepth, "find_cc_fnopie: trying to find -fno-pie...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "cc/fnopie", test_c, NULL, "-fno-pie", NULL)) return 0;
+ if (try_icl(logdepth, "cc/fnopie", test_c, NULL, "-fno-pie", "-static")) return 0;
+ if (try_icl(logdepth, "cc/fnopie", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "cc/fnopie");
+}
+
+int find_cc_fnopic(const char *name, int logdepth, int fatal)
+{
+ const char *test_c = test_hello_world;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for -fno-pic... ");
+ logprintf(logdepth, "find_cc_fnopic: trying to find -fno-pic...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "cc/fnopic", test_c, NULL, "-fno-pic", NULL)) return 0;
+ if (try_icl(logdepth, "cc/fnopic", test_c, NULL, "-fno-pic", "-static")) return 0;
+ if (try_icl(logdepth, "cc/fnopic", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "cc/fnopic");
+}
+
+int find_soname(const char *name, int logdepth, int fatal)
+{
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for soname... ");
+ logprintf(logdepth, "find_soname: trying to find soname...\n");
+ logdepth++;
+
+ if (try_hello(logdepth, NULL, "-Wl,-soname,libscconfig.0", "cc/soname", "-Wl,-soname,")) return 0;
+ if (try_hello(logdepth, NULL, NULL, "cc/soname", "")) return 0;
+
+ report("Not found.\n");
+ return 1;
+}
+
+
+int find_wlrpath(const char *name, int logdepth, int fatal)
+{
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for rpath... ");
+ logprintf(logdepth, "find_wlrpath: trying to find rpath...\n");
+ logdepth++;
+
+ if (try_hello(logdepth, NULL, "-Wl,-rpath=.", "cc/wlrpath", "-Wl,-rpath=")) return 0;
+
+ report("Not found.\n");
+ return 1;
+}
+
+int find_fpic(const char *name, int logdepth, int fatal)
+{
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for -fpic... ");
+ logprintf(logdepth, "find_fpic: trying to find -fpic...\n");
+ logdepth++;
+
+ if (try_hello(logdepth, NULL, "-fPIC", "cc/fpic", "-fPIC")) return 0;
+ if (try_hello(logdepth, NULL, "-fpic", "cc/fpic", "-fpic")) return 0;
+ if (try_hello(logdepth, NULL, NULL, "cc/fpic", "")) return 0;
+
+ report("Not found.\n");
+ return 1;
+}
+
+/* Hello world lib... */
+static const char *test_lib =
+ NL "#include "
+ NL "int hello() {"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+/* ...and the corresponding host application */
+static const char *test_host =
+ NL "#include "
+ NL "#include "
+ NL "#include %s"
+ NL "int main() {"
+ NL " void *handle = NULL;"
+ NL " void (*func)() = NULL;"
+ NL " char *error;"
+ NL
+ NL " handle = dlopen(\"%s\", RTLD_NOW);"
+ NL " if (handle == NULL) {"
+ NL " printf(\"dlopen fails: \", dlerror());"
+ NL " return 1;"
+ NL " }"
+ NL " func = dlsym(handle, \"hello\");"
+ NL " if (func == NULL) {"
+ NL " printf(\"dlsym fails: \", dlerror());"
+ NL " return 1;"
+ NL " }"
+ NL " func();"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+static int try_dynlib(int logdepth, const char *cflags, char *concated_ldflags, const char *name, const char *value, const char *host_app_cflags, const char *host_app_ldflags)
+{
+ char test_host_app[1024];
+ const char *fpic;
+ const char *ld_include;
+ const char *dlc;
+ char *libname, *libname_dyn;
+ char *cflags_c;
+ char *oname = ".o";
+ int ret = 0;
+
+
+ dlc = get("libs/dl-compat");
+ if ((dlc != NULL) && (strcmp(dlc, strue) == 0))
+ ld_include = "";
+ else
+ ld_include = "";
+
+ fpic = get("cc/fpic");
+ if (fpic == NULL) fpic = "";
+
+ if (cflags == NULL)
+ cflags="";
+
+ cflags_c = malloc(strlen(cflags) + 8 + strlen(fpic));
+ sprintf(cflags_c, "%s -c %s", cflags, fpic);
+
+
+ libname_dyn = libname = (char *)get("sys/ext_dynlib");
+ if ((compile_code(logdepth, test_lib, &oname, NULL, cflags_c, NULL) != 0) ||
+ (compile_file(logdepth, oname, &libname_dyn, NULL, NULL, concated_ldflags) != 0)) {
+ report("FAILED (compiling dynlib)\n");
+ }
+ else {
+ sprintf(test_host_app, test_host, ld_include, libname_dyn);
+ if (try_flags(logdepth, NULL, test_host_app, host_app_cflags, host_app_ldflags, "OK")) {
+ put(name, value);
+ report("OK (%s)\n", value);
+ ret = 1;
+ }
+ }
+ unlink(libname_dyn);
+ unlink(oname);
+ if (libname != libname_dyn)
+ free(libname_dyn);
+ free(oname);
+ free(concated_ldflags);
+ free(cflags_c);
+ return ret;
+}
+
+
+int find_ldflags_dynlib(const char *name, int logdepth, int fatal)
+{
+
+ require("cc/cc", logdepth, fatal);
+ require("cc/rdynamic", logdepth, fatal);
+ require("cc/fpic", logdepth, fatal);
+ require("libs/ldl", logdepth, fatal);
+
+ report("Checking for dynamic library ldflags... ");
+ logprintf(logdepth, "find_ldflags_dynlib: trying to find dynamic library ldflags...\n");
+ logdepth++;
+
+ if (try_dynlib(logdepth, NULL, concat_nodes("-dynamic -shared", "cc/rdynamic", "libs/ldl", NULL), "cc/ldflags_dynlib", "-dynamic -shared", NULL, get("libs/ldl"))) return 0;
+ if (try_dynlib(logdepth, NULL, concat_nodes("-shared", "cc/rdynamic", "libs/ldl", NULL), "cc/ldflags_dynlib", "-shared", NULL, get("libs/ldl"))) return 0;
+ report("Not found.\n");
+ return 1;
+}
+
+static int try_dll_or_so(int logdepth, int is_dll, const char *lib_ldflags, const char *name, const char *value,
+ const char *dspec_dllexport, const char *dspec_dllimport,
+ const char *app_cflags, const char *app_ldflags)
+{
+ static const char *test_lib_template =
+ NL "#include "
+ NL "%s void hello();"
+ NL "void hello() {"
+ NL " puts(\"OK\");"
+ NL "}"
+ NL ;
+ static const char *test_app_template =
+ NL "%s void hello();"
+ NL "int main() {"
+ NL " hello();"
+ NL " return 0;"
+ NL "}"
+ NL ;
+ char test_lib[1024];
+ char test_app[1024];
+ const char *fpic;
+ char *cflags_c;
+ char *oname, *oname_ext;
+ char *libname, *libname_ext;
+ char *appname = NULL, *appname_ext = NULL;
+ char *lib_filename = NULL, *lib_dirname = NULL;
+ char *lib_ldflags_new = NULL;
+ char *app_ldflags_new = NULL;
+ size_t len, ii;
+ int ret = 0;
+
+ ++logdepth;
+
+ require("cc/cc", logdepth, 0);
+ require("cc/cflags", logdepth, 0);
+ require("cc/ldflags", logdepth, 0);
+ require("cc/fpic", logdepth, 0);
+ require("sys/ext_exe", logdepth, 0);
+ require("sys/ext_dynlib_native", logdepth, 0);
+
+ fpic = get("cc/fpic");
+ if (fpic == NULL) fpic = "";
+
+ if (app_cflags == NULL)
+ app_cflags = "";
+
+ if (app_ldflags == NULL)
+ app_ldflags = "";
+
+ cflags_c = str_concat(" ", get("cc/cflags"), "-c", fpic, NULL);
+
+ oname = oname_ext = ".o";
+ libname = libname_ext = (char *)get("sys/ext_dynlib_native");
+ sprintf(test_lib, test_lib_template, dspec_dllexport);
+ lib_ldflags_new = str_concat(" ", get("cc/ldflags"), lib_ldflags, NULL);
+ if ((compile_code(logdepth, test_lib, &oname, NULL, cflags_c, NULL) != 0) ||
+ (compile_file(logdepth, oname, &libname, NULL, NULL, lib_ldflags_new) != 0)) {
+ report("FAILED (compiling %s)\n", (is_dll?"DLL":"SO"));
+ }
+ else {
+ lib_filename = file_name(libname);
+ lib_dirname = dir_name(libname);
+
+ if (!is_dll) {
+ len = strlen(lib_filename) - strlen(libname_ext);
+ for (ii=3; ii"
+ NL "int main() {"
+ NL " char *s;"
+ NL " s = alloca(128);"
+ NL " if (s != NULL)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+static int try_alloca(int logdepth, const char *cflags, const char *ldflags, const char *name, const char *value)
+{
+ if (try_flags(logdepth, NULL, test_alloca, cflags, ldflags, "OK")) {
+ put(name, value);
+ report("OK (%s)\n", value);
+ return 1;
+ }
+ return 0;
+}
+
+int find_alloca(const char *name, int logdepth, int fatal)
+{
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for alloca()... ");
+ logprintf(logdepth, "find_alloca: trying to find alloca()...\n");
+ logdepth++;
+
+ if (try_alloca(logdepth, NULL, NULL, "cc/alloca/presents", "true")) return 0;
+
+ put("cc/alloca/presents", "false");
+ report("Not found.\n");
+ return 1;
+}
+
+
+int find__exit(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " _exit(0);"
+ NL " puts(\"BAD\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for _exit()... ");
+ logprintf(logdepth, "find__exit: trying to find _exit()...\n");
+ logdepth++;
+
+ if (try_flags_inv(logdepth, NULL, test_c, NULL, NULL, "BAD")) {
+ put("cc/_exit/presents", strue);
+ report("found\n");
+ return 0;
+ }
+
+ put("cc/_exit/presents", sfalse);
+ report("Not found.\n");
+ return 1;
+}
+
diff --git a/scconfig/src/default/find_environ.c b/scconfig/src/default/find_environ.c
new file mode 100644
index 0000000..4004e4d
--- /dev/null
+++ b/scconfig/src/default/find_environ.c
@@ -0,0 +1,166 @@
+/*
+ scconfig - detection of environmental variable access features
+ Copyright (C) 2014 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_main_arg3(const char *name, int logdepth, int fatal)
+{
+ char *out;
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main(int argc, char *argv[], char *env[])"
+ NL "{"
+ NL " char **e;"
+ NL " int cnt;"
+ NL " for(e = env, cnt = 0; *e != NULL; e++, cnt++) ;"
+ NL " printf(\"%d\\n\", cnt);"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for main() with 3 arguments... ");
+ logprintf(logdepth, "find_main_3args: checking for main() with 3 arguments\n");
+ if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) {
+ if (atoi(out) > 1) {
+ put("libs/env/main_3arg", strue);
+ report("OK\n");
+ free(out);
+ return 0;
+ }
+ free(out);
+ report("not found (broken output).\n");
+ }
+ else {
+ report("not found (no output).\n");
+ }
+ put("libs/env/main_3arg", sfalse);
+ return 1;
+}
+
+int find_environ(const char *name, int logdepth, int fatal)
+{
+ char *out;
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "extern char **environ;"
+ NL "int main(int argc, char *argv[])"
+ NL "{"
+ NL " char **e;"
+ NL " int cnt;"
+ NL " for(e = environ, cnt = 0; *e != NULL; e++, cnt++) ;"
+ NL " printf(\"%d\\n\", cnt);"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for extern environ... ");
+ logprintf(logdepth, "find_environ: checking for extern environ\n");
+ if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) {
+ if (atoi(out) > 1) {
+ put("libs/env/environ", strue);
+ report("OK\n");
+ free(out);
+ return 0;
+ }
+ free(out);
+ report("not found (broken output).\n");
+ }
+ else {
+ report("not found (no output).\n");
+ }
+ put("libs/env/environ", sfalse);
+ return 1;
+}
+
+int find_putenv(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main(int argc, char *argv[])"
+ NL "{"
+ NL " putenv(\"SCCONFIG_TEST=bad\");"
+ NL " putenv(\"SCCONFIG_TEST=OK\");"
+ NL " printf(\"%s\\n\", getenv(\"SCCONFIG_TEST\"));"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for putenv()... ");
+ logprintf(logdepth, "find_putenv: trying to find putenv...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/env/putenv", test_c, "", NULL, NULL))
+ return 0;
+ if (try_icl(logdepth, "libs/env/putenv", test_c, "#define _XOPEN_SOURCE", NULL, NULL))
+ return 0;
+ if (try_icl(logdepth, "libs/env/putenv", test_c, "#define _SVID_SOURCE", NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "libs/env/putenv");
+}
+
+
+int find_setenv(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main(int argc, char *argv[])"
+ NL "{"
+ NL " setenv(\"SCCONFIG_TEST\", \"bad\", 1);"
+ NL " setenv(\"SCCONFIG_TEST\", \"OK\", 1);"
+ NL " printf(\"%s\\n\", getenv(\"SCCONFIG_TEST\"));"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for setenv()... ");
+ logprintf(logdepth, "find_setenv: trying to find setenv...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/env/setenv", test_c, "", NULL, NULL))
+ return 0;
+ if (try_icl(logdepth, "libs/env/setenv", test_c, "#define _BSD_SOURCE", NULL, NULL))
+ return 0;
+ if (try_icl(logdepth, "libs/env/setenv", test_c, "#define _POSIX_C_SOURCE 200112L", NULL, NULL))
+ return 0;
+ if (try_icl(logdepth, "libs/env/setenv", test_c, "#define _XOPEN_SOURCE 600", NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "libs/env/setenv");
+}
+
diff --git a/scconfig/src/default/find_fscalls.c b/scconfig/src/default/find_fscalls.c
new file mode 100644
index 0000000..4aa2320
--- /dev/null
+++ b/scconfig/src/default/find_fscalls.c
@@ -0,0 +1,806 @@
+/*
+ scconfig - detection of standard library features: file system specific calls
+ Copyright (C) 2010 Tibor Palinkas
+ Copyright (C) 2018 Aron Barath
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_fs_realpath(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "#ifdef PATH_MAX"
+ NL "char out_buf[PATH_MAX];"
+ NL "#else"
+ NL "char out_buf[32768];"
+ NL "#endif"
+ NL "int main() {"
+ NL " if (realpath(\".\", out_buf) == out_buf)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for realpath()... ");
+ logprintf(logdepth, "find_fs_realpath: trying to find realpath...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/fs/realpath", test_c, NULL, NULL, NULL)) return 0;
+ if (try_icl(logdepth, "libs/fs/realpath", test_c, "#define _DEFAULT_SOURCE", NULL, NULL)) return 0;
+ if (try_icl(logdepth, "libs/fs/realpath", test_c, "#define _BSD_SOURCE", NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/fs/realpath");
+}
+
+
+int find_fs__fullpath(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " char full[_MAX_PATH];"
+ NL " if (_fullpath(full, \".\", _MAX_PATH) != NULL)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for _fullpath()... ");
+ logprintf(logdepth, "find_fs__fullpath: trying to find _fullpath...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/fs/_fullpath", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/fs/_fullpath");
+}
+
+
+int find_fs_readdir(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " DIR *dirp;"
+ NL " struct dirent *dp;"
+ NL " int found = 0;"
+ NL " if ((dirp = opendir(\".\")) == 0)"
+ NL " return -1;"
+ NL " while ((dp = readdir(dirp)) != 0)"
+ NL " if (strcmp(dp->d_name, \"configure\") == 0)"
+ NL " found++;"
+ NL " closedir(dirp);"
+ NL " if (found == 1)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}";
+ char *includes[] = {
+ "#include ",
+ "#include ", /* 4.2BSD */
+ NULL
+ };
+ char **i;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for readdir()... ");
+ logprintf(logdepth, "find_fs_readdir: trying to find readdir...\n");
+ logdepth++;
+
+ for (i = includes; *i != NULL; i++)
+ if (try_icl(logdepth, "libs/fs/readdir", test_c, *i, NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "libs/fs/readdir");
+}
+
+
+int find_fs_findnextfile(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "int main(int argc, char *argv[]) {"
+ NL " WIN32_FIND_DATA fd;"
+ NL " HANDLE h;"
+ NL " int found=0;"
+ NL " h = FindFirstFile(argv[0], &fd);"
+ NL " if (h == INVALID_HANDLE_VALUE)"
+ NL " return -1;"
+ NL " while (FindNextFile(h, &fd) != 0);"
+ NL " found++;"
+ NL " FindClose(h);"
+ NL " if (found > 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}";
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for FindNextFile()... ");
+ logprintf(logdepth, "find_fs_findnextfile: trying to find FindNextFile...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/fs/findnextfile", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/fs/findnextfile");
+}
+
+int find_fs_access(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "int my_test() { return access(\".\", 0); }"
+ NL "#include "
+ NL "int main() {"
+ NL " if (my_test() == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ const char* includes[] = { "#include ", "#include \n#include ", "#include ", NULL };
+ const char** inc;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for access()... ");
+ logprintf(logdepth, "find_fs_access: trying to find access...\n");
+ logdepth++;
+
+ for (inc=includes; *inc; ++inc)
+ if (try_icl(logdepth, "libs/fs/access", test_c, *inc, NULL, NULL)) return 0;
+
+ return try_fail(logdepth, "libs/fs/access");
+}
+
+int find_fs_access_macros(const char *rname, int logdepth, int fatal)
+{
+ char *test_c_templ =
+ NL "%s"
+ NL "void my_test() { int a = %s; }"
+ NL "#include "
+ NL "int main() {"
+ NL " my_test();"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ char test_c[256];
+
+ char *names[][3] = {
+ {"F_OK", "F_OK", NULL},
+ {"R_OK", "R_OK", NULL},
+ {"W_OK", "W_OK", NULL},
+ {"X_OK", "X_OK", NULL},
+ {NULL, NULL, NULL}
+ };
+ char **n;
+ const char* access_includes;
+ int name, pr;
+ char nodename[128];
+
+ require("cc/cc", logdepth, fatal);
+ if (require("libs/fs/access/*", logdepth, fatal)!=0 ||
+ !istrue(get("libs/fs/access/presents"))) {
+ put("libs/fs/access/macros/presents", sfalse);
+ return 1;
+ }
+ access_includes = get("libs/fs/access/includes");
+
+ report("Checking for access macros:\n");
+ logprintf(logdepth, "find_fs_access_macros: trying to find access macros...\n");
+ logdepth++;
+
+ pr = 0;
+ for(name = 0; *names[name] != NULL; name++) {
+ report(" %s...\t", names[name][0]);
+ for(n = &names[name][0]; *n != NULL; n++) {
+ sprintf(test_c, test_c_templ, access_includes, *n);
+ if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) {
+ sprintf(nodename, "libs/fs/access/macros/%s", names[name][0]);
+ put(nodename, *n);
+ report("found as %s\n", *n);
+ pr++;
+ goto found;
+ }
+ }
+ report("not found\n");
+ found:;
+ }
+
+ put("libs/fs/access/macros/presents", ((pr > 0) ? (strue) : (sfalse)));
+ return (pr == 0);
+}
+
+int find_fs_stat_macros(const char *rname, int logdepth, int fatal)
+{
+ char *test_c_templ =
+ NL "#include "
+ NL "#include "
+ NL "void my_test() { int a = %s(0); }"
+ NL "#include "
+ NL "int main() {"
+ NL " my_test();"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ char test_c[256];
+
+ char *names[][3] = {
+ {"S_ISREG", "S_IFREG", NULL},
+ {"S_ISDIR", "S_IFDIR", NULL},
+ {"S_ISCHR", "S_IFCHR", NULL},
+ {"S_ISBLK", "S_IFBLK", NULL},
+ {"S_ISFIFO", "S_IFFIFO", NULL},
+ {"S_ISLNK", "S_IFLNK", NULL},
+ {"S_ISCHR", "S_IFCHR", NULL},
+ {"S_ISSOCK", "S_IFSOCK", NULL},
+ {NULL, NULL, NULL}
+ };
+ char **n;
+ int name, pr;
+ char nodename[128];
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for stat macros:\n");
+ logprintf(logdepth, "find_fs_stat_macros: trying to find stat macros...\n");
+ logdepth++;
+
+ pr = 0;
+ for(name = 0; *names[name] != NULL; name++) {
+ report(" %s...\t", names[name][0]);
+ for(n = &names[name][0]; *n != NULL; n++) {
+ sprintf(test_c, test_c_templ, *n);
+ if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) {
+ sprintf(nodename, "libs/fs/stat/macros/%s", names[name][0]);
+ put(nodename, *n);
+ report("found as %s\n", *n);
+ pr++;
+ goto found;
+ }
+ }
+ report("not found\n");
+ found:;
+ }
+
+ put("libs/fs/stat/macros/presents", ((pr > 0) ? (strue) : (sfalse)));
+ return (pr == 0);
+}
+
+int find_fs_stat_fields(const char *rname, int logdepth, int fatal)
+{
+ char *test_c_templ =
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " struct stat st;"
+ NL " (void)st.%s;"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ char test_c[256];
+
+ char *names[] = {"st_blksize", "st_blocks", "st_rdev", "st_mtim", "st_mtime", "st_birthtim", "st_birthtime", NULL };
+ int name, pr;
+ char nodename[128];
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for stat macros:\n");
+ logprintf(logdepth, "find_fs_stat_fields: trying to find stat macros...\n");
+ logdepth++;
+
+ pr = 0;
+ for(name = 0; names[name] != NULL; name++) {
+ report(" %s...\t", names[name]);
+ sprintf(test_c, test_c_templ, names[name]);
+ sprintf(nodename, "libs/fs/stat/fields/%s/presents", names[name]);
+ if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) {
+ put(nodename, strue);
+ report("found\n");
+ pr++;
+ }
+ else {
+ report("not found\n");
+ put(nodename, sfalse);
+ }
+ }
+ return (pr == 0);
+}
+
+static int find_fs_any_lstat(const char *name, int logdepth, int fatal, char *fn)
+{
+ /* make sure does not affect our lstat() detection */
+ const char *test_c_in =
+ NL "void my_puts(const char *s);"
+ NL "int main() {"
+ NL " struct stat buf;"
+ NL " if (%s(\".\", &buf) == 0)"
+ NL " my_puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL "#include "
+ NL "void my_puts(const char *s)"
+ NL "{"
+ NL " puts(s);"
+ NL "}"
+ NL;
+ char test_c[384], node[64];
+ const char *incs[] = {"#include ", "#include ", "#include \n#include \n#include ", NULL};
+ const char **inc;
+
+ require("cc/cc", logdepth, fatal);
+
+ sprintf(node, "libs/fs/%s", fn);
+ sprintf(test_c, test_c_in, fn);
+
+ report("Checking for %s... ", fn);
+ logprintf(logdepth, "find_fs_%s: trying to find lstat()...\n", fn);
+ logdepth++;
+
+ for (inc = incs; *inc; ++inc) {
+ if (try_icl(logdepth, node, test_c, *inc, NULL, NULL))
+ return 0;
+ }
+
+ return try_fail(logdepth, node);
+}
+
+int find_fs_lstat(const char *name, int logdepth, int fatal)
+{
+ return find_fs_any_lstat(name, logdepth, fatal, "lstat");
+}
+
+int find_fs_statlstat(const char *name, int logdepth, int fatal)
+{
+ return find_fs_any_lstat(name, logdepth, fatal, "statlstat");
+}
+
+
+int find_fs_getcwd(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " char b[1024];"
+ NL " if (getcwd(b, sizeof(b)) != NULL)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for getcwd... ");
+ logprintf(logdepth, "find_fs_getcwd: trying to find getcwd()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/fs/getcwd", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/fs/getcwd");
+}
+
+int find_fs__getcwd(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " char b[1024];"
+ NL " if (_getcwd(b, sizeof(b)) != NULL)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for _getcwd... ");
+ logprintf(logdepth, "find_fs__getcwd: trying to find _getcwd()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/fs/_getcwd", test_c, "#include ", NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/fs/_getcwd");
+}
+
+
+int find_fs_getwd(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " char b[8192];"
+ NL " if (getwd(b) != NULL)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for getwd... ");
+ logprintf(logdepth, "find_fs_getwd: trying to find getwd()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/fs/getwd", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/fs/getwd");
+}
+
+int find_fs_mkdir(const char *name, int logdepth, int fatal)
+{
+ char *dir;
+ char test_c[1024];
+ char *test_c_in =
+ NL "#include "
+ NL "int main() {"
+ NL no_implicit(int, "mkdir", "mkdir")
+ NL " if (mkdir(\"%s\"%s) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ dir = tempfile_new("");
+ unlink(dir);
+
+ report("Checking for mkdir... ");
+ logprintf(logdepth, "find_fs_mkdir: trying to find mkdir()...\n");
+ logdepth++;
+
+ /* POSIX, 2 arguments, standard includes */
+ sprintf(test_c, test_c_in, dir, ", 0755");
+ if (try_icl(logdepth, "libs/fs/mkdir", test_c, "#include \n#include \n", NULL, NULL)) {
+ if (!is_dir(dir))
+ goto oops1;
+ put("libs/fs/mkdir/num_args", "2");
+ rmdir(dir);
+ return 0;
+ }
+
+ /* POSIX, 2 arguments, no includes */
+ oops1:;
+ sprintf(test_c, test_c_in, dir, ", 0755");
+ if (try_icl(logdepth, "libs/fs/mkdir", test_c, NULL, NULL, NULL)) {
+ if (!is_dir(dir))
+ goto oops2;
+ put("libs/fs/mkdir/num_args", "2");
+ rmdir(dir);
+ return 0;
+ }
+
+ /* win32, 1 argument, with */
+ oops2:;
+ sprintf(test_c, test_c_in, dir, "");
+ if (try_icl(logdepth, "libs/fs/mkdir", test_c, "#include \n", NULL, NULL)) {
+ if (!is_dir(dir))
+ goto oops3;
+ put("libs/fs/mkdir/num_args", "1");
+ rmdir(dir);
+ return 0;
+ }
+
+ oops3:;
+ put("libs/fs/mkdir/includes", "");
+ put("libs/fs/mkdir/ldflags", "");
+ put("libs/fs/mkdir/cdflags", "");
+
+ rmdir(dir);
+ return try_fail(logdepth, "libs/fs/mkdir");
+}
+
+int find_fs__mkdir(const char *name, int logdepth, int fatal)
+{
+ char *dir;
+ char test_c[1024];
+ char *test_c_in =
+ NL "#include "
+ NL "int main() {"
+ NL " if (_mkdir(\"%s\"%s) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ dir = tempfile_new("");
+ unlink(dir);
+
+ report("Checking for _mkdir... ");
+ logprintf(logdepth, "find_fs__mkdir: trying to find _mkdir()...\n");
+ logdepth++;
+
+ /* win32, 2 arguments, standard includes */
+ sprintf(test_c, test_c_in, dir, ", 0755");
+ if (try_icl(logdepth, "libs/fs/_mkdir", test_c, "#include \n", NULL, NULL)) {
+ if (!is_dir(dir))
+ goto oops1;
+ put("libs/fs/_mkdir/num_args", "2");
+ rmdir(dir);
+ return 0;
+ }
+
+ oops1:;
+ /* win32, 1 argument, standard includes */
+ sprintf(test_c, test_c_in, dir, "");
+ if (try_icl(logdepth, "libs/fs/_mkdir", test_c, "#include \n", NULL, NULL)) {
+ if (!is_dir(dir))
+ goto oops2;
+ put("libs/fs/_mkdir/num_args", "1");
+ rmdir(dir);
+ return 0;
+ }
+
+ oops2:;
+ put("libs/fs/_mkdir/includes", "");
+ put("libs/fs/_mkdir/ldflags", "");
+ put("libs/fs/_mkdir/cdflags", "");
+
+ rmdir(dir);
+ return try_fail(logdepth, "libs/fs/_mkdir");
+}
+
+int find_fs_mkdtemp(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " char fn[32], *o;"
+ NL " strcpy(fn, \"scc.XXXXXX\");"
+ NL " o = mkdtemp(fn);"
+ NL " if ((o != NULL) && (strstr(o, \"scc.\") != NULL)) {"
+ NL " remove(o);"
+ NL " puts(\"OK\");"
+ NL " }"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for mkdtemp... ");
+ logprintf(logdepth, "find_fs_mkdtemp: trying to find mkdtemp()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/fs/mkdtemp", test_c, "#include \n", NULL, NULL)) return 0;
+ if (try_icl(logdepth, "libs/fs/mkdtemp", test_c, "#define _BSD_SOURCE\n#include \n", NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/fs/mkdtemp");
+}
+
+int find_fs_mmap(const char *name, int logdepth, int fatal)
+{
+ char test_c[1024];
+ char *tmp;
+ FILE *f;
+ char *test_c_in =
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " int fd, size = 11;"
+ NL " void *p;"
+ NL " fd = open(\"%s\", O_RDONLY);"
+ NL " p = mmap(0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);"
+ NL " if (p == NULL) {"
+ NL " puts(\"mmap fail\");"
+ NL " return 0;"
+ NL " }"
+ NL " if (strcmp(p, \"hello world\") != 0) {"
+ NL " puts(\"strcmp fail\");"
+ NL " return 0;"
+ NL " }"
+ NL " if (munmap(p, size) != 0) {"
+ NL " puts(\"munmap fail\");"
+ NL " return 0;"
+ NL " }"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ tmp = tempfile_new("");
+ f = fopen(tmp, "w");
+ fprintf(f, "hello world");
+ fclose(f);
+ sprintf(test_c, test_c_in, tmp);
+
+ report("Checking for mmap... ");
+ logprintf(logdepth, "find_fs_mmap: trying to find mmap()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/fs/mmap", test_c, "#include \n", NULL, NULL)) {
+ unlink(tmp);
+ free(tmp);
+ return 0;
+ }
+
+ unlink(tmp);
+ free(tmp);
+ return try_fail(logdepth, "libs/fs/mmap");
+}
+
+/* Haiku/BeOS next_dev */
+int find_fsmount_next_dev(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main()"
+ NL "{"
+ NL " int32 pos = 0;"
+ NL " dev_t res = next_dev(&pos);"
+ NL " if (res >= 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for next_dev... ");
+ logprintf(logdepth, "find_fsmount_next_dev: trying to find next_dev()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/fsmount/next_dev", test_c, "#include \n", NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/fsmount/next_dev");
+}
+
+int find_fsmount_fsstat_fields(const char *name, int logdepth, int fatal)
+{
+ const char *fields[] = {"f_fstypename", NULL};
+ return try_icl_sfields(logdepth, "libs/fsmount/struct_fsstat", "struct fsstat", fields, "#include ", NULL, NULL, 0);
+}
+
+int find_fsmount_statfs_fields(const char *name, int logdepth, int fatal)
+{
+ const char *fields[] = {"f_fstypename", "f_type", NULL};
+ return try_icl_sfields(logdepth, "libs/fsmount/struct_statfs", "struct statfs", fields, "#include ", NULL, NULL, 0);
+}
+
+int find_fsmount_statvfs_fields(const char *name, int logdepth, int fatal)
+{
+ const char *fields[] = {"f_fstypename", "f_type", "f_basetype", NULL};
+ return try_icl_sfields(logdepth, "libs/fsmount/struct_statvfs", "struct statvfs", fields, "#include ", NULL, NULL, 0);
+}
+
+int find_fs_ustat(const char *name, int logdepth, int fatal)
+{
+ const char *key = "libs/fs/ustat";
+ const char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main()"
+ NL "{"
+ NL " struct stat stat_buf;"
+ NL " struct ustat ustat_buf;"
+ NL " if (stat(\".\", &stat_buf) == 0 &&"
+ NL " ustat(stat_buf.st_dev, &ustat_buf) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for ustat... ");
+ logprintf(logdepth, "find_fs_ustat: trying to find ustat()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0;
+ if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0;
+ if (try_icl(logdepth, key, test_c, "#include \n#include ", NULL, NULL)) return 0;
+ if (try_icl(logdepth, key, test_c, "#include \n#include \n#include ", NULL, NULL)) return 0;
+ return try_fail(logdepth, key);
+}
+
+int find_fs_statfs(const char *name, int logdepth, int fatal)
+{
+ const char *key = "libs/fs/statfs";
+ const char *test_c =
+ NL "#include "
+ NL "int main()"
+ NL "{"
+ NL " struct statfs statfs_buf;"
+ NL " if (statfs(\".\", &statfs_buf) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for statfs... ");
+ logprintf(logdepth, "find_fs_statfs: trying to find statfs()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0;
+ if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0;
+ return try_fail(logdepth, key);
+}
+
+int find_fs_statvfs(const char *name, int logdepth, int fatal)
+{
+ const char *key = "libs/fs/statvfs";
+ const char *test_c =
+ NL "#include "
+ NL "int main()"
+ NL "{"
+ NL " struct statvfs statvfs_buf;"
+ NL " if (statvfs(\".\", &statvfs_buf) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for statvfs... ");
+ logprintf(logdepth, "find_fs_statvfs: trying to find statvfs()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0;
+ return try_fail(logdepth, key);
+}
+
+int find_fs_flock(const char *name, int logdepth, int fatal)
+{
+ const char *key = "libs/fs/flock";
+ const char *test_c =
+ NL "#include "
+ NL "int main()"
+ NL "{"
+ NL " if (flock(1, LOCK_UN) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for flock... ");
+ logprintf(logdepth, "find_fs_flock: trying to find flock()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0;
+ return try_fail(logdepth, key);
+}
diff --git a/scconfig/src/default/find_fstools.c b/scconfig/src/default/find_fstools.c
new file mode 100644
index 0000000..5dc894d
--- /dev/null
+++ b/scconfig/src/default/find_fstools.c
@@ -0,0 +1,833 @@
+/*
+ scconfig - detection of file system tools
+ Copyright (C) 2009..2012 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+#include "dep.h"
+
+static int test_cp_ln(int logdepth, const char *command, int link)
+{
+ char *src, *dst, *src_esc, *dst_esc;
+ char *cmd, *result;
+ char *test_string = "works.";
+ int ret;
+
+ logprintf(logdepth, "trying '%s'\n", command);
+
+ src = tempfile_dump(test_string, "");
+ dst = tempfile_new("");
+ if (link)
+ unlink(dst);
+
+ src_esc = shell_escape_dup(src);
+ dst_esc = shell_escape_dup(dst);
+ cmd = malloc(strlen(command) + strlen(src_esc) + strlen(dst_esc) + 32);
+ sprintf(cmd, "%s %s %s", command, src_esc, dst_esc);
+ run_shell(logdepth, cmd, NULL);
+ free(cmd);
+ free(src_esc);
+ free(dst_esc);
+
+ result = load_file(dst);
+ ret = !strcmp(result, test_string);
+ logprintf(logdepth+1, "result: '%s' == '%s' (%d)\n", result, test_string, ret);
+ free(result);
+
+ unlink(src);
+ free(src);
+
+ result = load_file(dst);
+ if (link) {
+ if (strcmp(result, test_string) == 0) {
+ report("Warning: link is copy (or hard link). ");
+ logprintf(logdepth+1, "Warning: link is copy (or hard link).\n");
+ }
+ }
+ else {
+ if (strcmp(result, test_string) != 0) {
+ report("Warning: copy is symlink. ");
+ logprintf(logdepth+1, "Warning: copy is symlink.\n");
+ }
+ }
+ free(result);
+
+ if (ret) {
+ if (link)
+ put("fstools/ln", command);
+ else
+ put("fstools/cp", command);
+
+ report("OK (%s)\n", command);
+ }
+
+ unlink(dst);
+ free(dst);
+ return ret;
+}
+
+static int test_mv(int logdepth, const char *command)
+{
+ char *src, *dst, *src_esc, *dst_esc;
+ char *cmd, *result;
+ char *test_string = "works.";
+ int ret;
+
+ logprintf(logdepth, "trying '%s'\n", command);
+
+ src = tempfile_dump(test_string, "");
+ dst = tempfile_new("");
+ unlink(dst);
+
+ src_esc = shell_escape_dup(src);
+ dst_esc = shell_escape_dup(dst);
+ cmd = malloc(strlen(command) + strlen(src_esc) + strlen(dst_esc) + 32);
+ sprintf(cmd, "%s %s %s", command, src_esc, dst_esc);
+ run_shell(logdepth, cmd, NULL);
+ free(cmd);
+ free(src_esc);
+ free(dst_esc);
+
+ result = load_file(dst);
+ ret = !strcmp(result, test_string);
+ logprintf(logdepth+1, "result: '%s' == '%s' (%d)\n", result, test_string, ret);
+ free(result);
+
+ if (file_size(src) > 0) {
+ report("Warning: mv is copy. ");
+ logprintf(logdepth+1, "Warning: mv is copy.\n");
+ }
+
+ if (ret) {
+ put("fstools/mv", command);
+ report("OK (%s)\n", command);
+ }
+
+ unlink(dst);
+ unlink(src);
+ free(dst);
+ free(src);
+ return ret;
+}
+
+static int test_mkdir(int logdepth, const char *command)
+{
+ char *dir, *file;
+ char *dir_esc;
+ char *cmd, *result;
+ char *test_string = "works.";
+ int ret = 0, had_p;
+ FILE *f;
+
+ logprintf(logdepth, "trying '%s'\n", command);
+ dir = tempfile_new("");
+ dir_esc = shell_escape_dup(dir);
+ unlink(dir);
+
+ had_p = is_dir("-p");
+
+ cmd = malloc(strlen(command) + strlen(dir_esc) + 32);
+ sprintf(cmd, "%s %s", command, dir_esc);
+ run_shell(logdepth, cmd, NULL);
+ free(cmd);
+
+ file = malloc(strlen(dir) + 32);
+ sprintf(file, "%s/test", dir);
+ f = fopen(file, "w");
+ if (f != NULL) {
+ fputs(test_string, f);
+ fclose(f);
+ result = load_file(file);
+ if (strcmp(result, test_string) == 0)
+ ret = 1;
+ free(result);
+ }
+
+ unlink(file);
+ unlink(dir);
+
+ cmd = malloc(strlen(dir) + 32);
+ sprintf(cmd, "rmdir %s", dir_esc);
+ run_shell(logdepth, cmd, NULL);
+ free(cmd);
+
+ free(file);
+ free(dir);
+ free(dir_esc);
+
+ /* This is a bit ugly, but on win32 or other systems where mkdir works
+ but -p doesn't have an effect, a directory called -p may be left over... */
+ if ((!had_p) && (is_dir("-p"))) {
+ unlink("-p");
+ return 0;
+ }
+
+ if (ret != 0) {
+ put("fstools/mkdir", command);
+ report("OK (%s)\n", command);
+ }
+
+ return ret;
+}
+
+static int test_rm(int logdepth, const char *command)
+{
+ char *src, *src_esc, *cmd, *test_string = "works.";
+ int ret;
+
+ logprintf(logdepth, "trying '%s'\n", command);
+
+ src = tempfile_dump(test_string, "");
+
+ if (file_size(src) < 0) {
+ report("error: can't create temp file\n");
+ free(src);
+ return 0;
+ }
+
+
+ src_esc = shell_escape_dup(src);
+ cmd = malloc(strlen(command) + strlen(src_esc) + 32);
+ sprintf(cmd, "%s %s", command, src_esc);
+ run_shell(logdepth, cmd, NULL);
+ free(cmd);
+ free(src_esc);
+
+ ret = file_size(src) < 0;
+
+ if (ret) {
+ put("fstools/rm", command);
+ report("OK (%s)\n", command);
+ }
+ else
+ unlink(src);
+
+ free(src);
+ return ret;
+}
+
+static int test_ar(int logdepth, const char *command)
+{
+ char *src, *dst, *src_esc, *dst_esc;
+ char *cmd, *result, *expected;
+ char *test_string = "works.";
+ const char *path_sep;
+ int ret = 0;
+
+ logprintf(logdepth, "trying '%s'\n", command);
+ path_sep = get("sys/path_sep");
+
+ src = tempfile_dump(test_string, "");
+ dst = tempfile_new("");
+ unlink(dst);
+
+ src_esc = shell_escape_dup(src);
+ dst_esc = shell_escape_dup(dst);
+ cmd = malloc(strlen(command) + strlen(src_esc) + strlen(dst_esc) + 32);
+ sprintf(cmd, "%s ru %s %s", command, dst_esc, src_esc);
+ run_shell(logdepth, cmd, NULL);
+ sprintf(cmd, "%s t %s", command, dst_esc);
+ run_shell(logdepth, cmd, &result);
+ free(cmd);
+ free(dst_esc);
+ free(src_esc);
+
+ if (result != NULL) {
+ expected = str_rchr(src, *path_sep);
+ if (expected == NULL)
+ expected = src;
+ else
+ expected++;
+
+ ret = strncmp(expected, result, strlen(expected)) == 0;
+ if (ret) {
+ put("fstools/ar", command);
+ report("OK (%s)\n", command);
+ }
+ free(result);
+ }
+
+ unlink(src);
+ unlink(dst);
+ free(src);
+ free(dst);
+ return ret;
+}
+
+static int test_ranlib(int logdepth, const char *command, const char *obj)
+{
+ char *cmd, *archive, *archive_esc, *obj_esc;
+ const char *ar;
+ int ret;
+ ar = get("fstools/ar");
+ logprintf(logdepth, "trying '%s'\n", command);
+
+ archive = tempfile_new(".a");
+ archive_esc = shell_escape_dup(archive);
+ obj_esc = shell_escape_dup(obj);
+ cmd = malloc(strlen(command) + strlen(obj_esc) + strlen(archive_esc) + 64);
+
+ sprintf(cmd, "%s r %s %s", ar, archive_esc, obj_esc);
+ unlink(archive);
+ ret = run_shell(logdepth, cmd, NULL) == 0;
+ if (!ret)
+ goto fin;
+
+ sprintf(cmd, "%s %s", command, archive_esc);
+ ret = run_shell(logdepth, cmd, NULL) == 0;
+
+ if (ret) {
+ put("fstools/ranlib", command);
+ report("OK (%s)\n", command);
+ }
+
+fin:;
+ unlink(archive);
+ free(archive);
+ free(cmd);
+ free(archive_esc);
+ free(obj_esc);
+ return ret;
+}
+
+static int test_awk(int logdepth, const char *command)
+{
+ char cmd[1024];
+ char *out;
+ int ret = 0;
+ char *script, *script_esc;
+
+ /* For some reason windows awk doesn't like the code with NLs */
+ char *test_awk =
+ "BEGIN {"
+ " gsub(\"b\", \"B\", t);"
+ " print t;"
+ "}";
+
+ logprintf(logdepth, "trying '%s'\n", command);
+ script = tempfile_dump(test_awk, ".awk");
+ script_esc = shell_escape_dup(script);
+ sprintf(cmd, "%s -v \"t=blobb\" -f %s", command, script_esc);
+ free(script_esc);
+ run_shell(logdepth, cmd, &out);
+ unlink(script);
+ free(script);
+
+ if ((out != NULL) && (strncmp(out, "BloBB", 5) == 0)) {
+ put("fstools/awk", command);
+ report("OK (%s)\n", command);
+ ret = 1;
+ }
+
+ free(out);
+ return ret;
+}
+
+static int test_cat(int logdepth, const char *command)
+{
+ char cmd[1024];
+ char *out;
+ int ret = 0;
+ char *fn, *fn_esc;
+ const char *test_str = "hello world";
+
+ logprintf(logdepth, "trying '%s'\n", command);
+ fn = tempfile_dump(test_str, ".txt");
+ fn_esc = shell_escape_dup(fn);
+ sprintf(cmd, "%s %s", command, fn_esc);
+ run_shell(logdepth, cmd, &out);
+ unlink(fn);
+ free(fn);
+ free(fn_esc);
+
+ if ((out != NULL) && (strncmp(out, test_str, strlen(test_str)) == 0)) {
+ put("fstools/cat", command);
+ report("OK (%s)\n", command);
+ ret = 1;
+ }
+
+ free(out);
+ return ret;
+}
+
+static int test_sed(int logdepth, const char *command)
+{
+ char cmd[1024];
+ char *out;
+ int ret = 0;
+ char *fn, *fn_esc;
+ const char *test_str_in = "hello world";
+ const char *test_str_out = "he11o wor1d";
+
+ logprintf(logdepth, "trying '%s'\n", command);
+ fn = tempfile_dump(test_str_in, ".txt");
+ fn_esc = shell_escape_dup(fn);
+ sprintf(cmd, "%s \"s/l/1/g\" < %s", command, fn_esc);
+ run_shell(logdepth, cmd, &out);
+ unlink(fn);
+ free(fn);
+ free(fn_esc);
+
+ if ((out != NULL) && (strncmp(out, test_str_out, strlen(test_str_out)) == 0)) {
+ put("fstools/sed", command);
+ report("OK (%s)\n", command);
+ ret = 1;
+ }
+
+ free(out);
+ return ret;
+}
+
+static int test_chmodx(int logdepth, const char *command)
+{
+ char *cmd, *tmp, *tmp_esc, *out, *s;
+ int ret;
+
+ logprintf(logdepth, "trying '%s'\n", command);
+ tmp = tempfile_dump("#!/bin/sh\necho OK\n", ".bat");
+
+ tmp_esc = shell_escape_dup(tmp);
+ cmd = malloc(strlen(command) + strlen(tmp_esc) + 16);
+ sprintf(cmd, "%s %s", command, tmp_esc);
+ ret = run_shell(logdepth, cmd, NULL) == 0;
+ free(cmd);
+ if (!ret) {
+ free(tmp_esc);
+ return ret;
+ }
+
+ ret = run(logdepth+1, tmp_esc, &out);
+ free(tmp_esc);
+
+ if (ret == 0) {
+ for(s = out; s != NULL; s = str_chr(s, '\n')) {
+ logprintf(logdepth+1, "chmod line to test: '%s'\n", s);
+ if ((s[0] == 'O') && (s[1] == 'K')) {
+ logprintf(logdepth+2, "(OK)\n");
+ ret = 1;
+ break;
+ }
+ s++;
+ }
+ }
+ else
+ ret = 0;
+
+ free(out);
+ if (ret) {
+ put("fstools/chmodx", command);
+ logprintf(logdepth, "chmodx command validated: '%s'\n", command);
+ report("OK (%s)\n", command);
+ }
+ unlink(tmp);
+ return ret;
+}
+
+static int test_file(int logdepth, const char *node, const char *command)
+{
+ char cmd[1024];
+ char *out;
+ int ret = 0;
+ char *fn, *fn_esc;
+
+ logprintf(logdepth, "trying '%s'\n", command);
+ fn = tempfile_dump("plain text file\r\n", ".txt");
+ fn_esc = shell_escape_dup(fn);
+ sprintf(cmd, "%s %s", command, fn_esc);
+ run_shell(logdepth, cmd, &out);
+ unlink(fn);
+ free(fn);
+ free(fn_esc);
+
+ if ((out != NULL) && (strstr(out, "text") != NULL)) {
+ put(node, command);
+ report("OK (%s)\n", command);
+ ret = 1;
+ }
+
+ free(out);
+ return ret;
+}
+
+int find_fstools_cp(const char *name, int logdepth, int fatal)
+{
+ const char *cp;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for cp... ");
+ logprintf(logdepth, "find_fstools_cp: trying to find cp...\n");
+ logdepth++;
+
+ cp = get("/arg/fstools/cp");
+ if (cp == NULL) {
+ if (test_cp_ln(logdepth, "cp -rp", 0)) return 0;
+ if (test_cp_ln(logdepth, "cp -r", 0)) return 0;
+ if (test_cp_ln(logdepth, "copy /r", 0)) return 0; /* wine */
+ }
+ else {
+ report(" user provided (%s)...", cp);
+ if (test_cp_ln(logdepth, cp, 0)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_ln(const char *name, int logdepth, int fatal)
+{
+ const char *ln;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for ln... ");
+ logprintf(logdepth, "find_fstools_ln: trying to find ln...\n");
+ logdepth++;
+
+ ln = get("/arg/fstools/ln");
+ if (ln == NULL) {
+ if (test_cp_ln(logdepth, "ln -sf",1 )) return 0;
+ if (test_cp_ln(logdepth, "ln -s",1 )) return 0;
+ if (test_cp_ln(logdepth, "ln", 1)) return 0;
+ /* "mklink /H" -> win32 equivalent to "ln" */
+ /* "cp -s" -> same as "ln -s" */
+ /* "cp -l" -> same as "ln" */
+ if (test_cp_ln(logdepth, "cp", 1)) return 0;
+ }
+ else {
+ report(" user provided (%s)...", ln);
+ if (test_cp_ln(logdepth, ln, 1)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_mv(const char *name, int logdepth, int fatal)
+{
+ const char *mv;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for mv... ");
+ logprintf(logdepth, "find_fstools_mv: trying to find mv...\n");
+ logdepth++;
+
+ mv = get("/arg/fstools/mv");
+ if (mv == NULL) {
+ if (test_mv(logdepth, "mv")) return 0;
+ if (test_mv(logdepth, "move")) return 0; /* win32 */
+ if (test_mv(logdepth, "cp")) return 0;
+ }
+ else {
+ report(" user provided (%s)...", mv);
+ if (test_mv(logdepth, mv)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_rm(const char *name, int logdepth, int fatal)
+{
+ const char *rm;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for rm... ");
+ logprintf(logdepth, "find_fstools_rm: trying to find rm...\n");
+ logdepth++;
+
+ rm = get("/arg/fstools/rm");
+ if (rm == NULL) {
+ if (test_rm(logdepth, "rm -rf")) return 0;
+ if (test_rm(logdepth, "rm -f")) return 0;
+ if (test_rm(logdepth, "rm")) return 0;
+ if (test_rm(logdepth, "del")) return 0; /* for win32 */
+ }
+ else {
+ report(" user provided (%s)...", rm);
+ if (test_rm(logdepth, rm)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_mkdir(const char *name, int logdepth, int fatal)
+{
+ const char *mkdir;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for mkdir... ");
+ logprintf(logdepth, "find_fstools_mkdir: trying to find mkdir...\n");
+ logdepth++;
+
+ mkdir = get("/arg/fstools/mkdir");
+ if (mkdir == NULL) {
+ if (test_mkdir(logdepth, "mkdir -p")) return 0;
+ if (test_mkdir(logdepth, "md")) return 0; /* for win32 */
+ }
+ else {
+ report(" user provided (%s)...", mkdir);
+ if (test_mkdir(logdepth, mkdir)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_ar(const char *name, int logdepth, int fatal)
+{
+ const char *ar, *target;
+ char *targetar;
+ int len;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ require("sys/path_sep", logdepth, fatal);
+
+
+ report("Checking for ar... ");
+ logprintf(logdepth, "find_fstools_ar: trying to find ar...\n");
+ logdepth++;
+
+ ar = get("/arg/fstools/ar");
+ if (ar == NULL) {
+ target = get("/arg/sys/target");
+ if (target != NULL) {
+ logprintf(logdepth+1, "find_ar: crosscompiling for '%s', looking for target ar\n", target);
+ len = strlen(target);
+ targetar = malloc(len + 8);
+ memcpy(targetar, target, len);
+ strcpy(targetar + len, "-ar");
+ if (test_ar(logdepth, targetar)) {
+ free(targetar);
+ return 0;
+ }
+ free(targetar);
+ }
+ if (test_ar(logdepth, "ar")) return 0;
+ if (test_ar(logdepth, "/usr/bin/ar")) return 0;
+ }
+ else {
+ report(" user provided (%s)...", ar);
+ if (test_ar(logdepth, ar)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_ranlib(const char *name, int logdepth, int fatal)
+{
+ const char *ranlib, *target;
+ char *targetranlib;
+ int len;
+ char *test_code = NL "int zero() { return 0; }" NL;
+ char *obj = ".o";
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ require("fstools/ar", logdepth, fatal);
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for ranlib... ");
+ logprintf(logdepth, "find_fstools_ranlib: trying to find ranlib...\n");
+ logdepth++;
+
+ logprintf(logdepth, "compiling test object...\n");
+ if (compile_code(logdepth+1, test_code, &obj, NULL, "-c", NULL) != 0) {
+ logprintf(logdepth, "ERROR: Can't compile test object\n");
+ report("ERROR: Can't compile test object\n");
+ abort();
+ }
+
+ ranlib = get("/arg/fstools/ranlib");
+ if (ranlib == NULL) {
+ target = get("/arg/sys/target");
+ if (target != NULL) {
+ logprintf(logdepth+1, "find_ranlib: crosscompiling for '%s', looking for target ranlib\n", target);
+ len = strlen(target);
+ targetranlib = malloc(len + 16);
+ memcpy(targetranlib, target, len);
+ strcpy(targetranlib + len, "-ranlib");
+ if (test_ranlib(logdepth, targetranlib, obj)) {
+ free(targetranlib);
+ return 0;
+ }
+ free(targetranlib);
+ }
+ if (test_ranlib(logdepth, "ranlib", obj)) goto found;
+ if (test_ranlib(logdepth, "/usr/bin/ranlib", obj)) goto found;
+ if (test_ranlib(logdepth, "ar -s", obj)) goto found;
+ if (test_ranlib(logdepth, "/usr/bin/ar -s", obj)) goto found;
+
+ /* some systems (for example IRIX) can't run s without doing
+ something else; t is harmless */
+ if (test_ranlib(logdepth, "ar ts", obj)) goto found;
+ if (test_ranlib(logdepth, "/usr/bin/ar ts", obj)) goto found;
+
+ /* final fallback: some systems (for example minix3) simply
+ do not have ranlib or ar equivalent; it's easier to detect
+ a dummy command than to force conditions into Makefiles */
+ if (test_ranlib(logdepth, "true", obj)) goto found;
+ }
+ else {
+ report(" user provided (%s)...", ranlib);
+ if (test_ranlib(logdepth, ranlib, obj)) goto found;
+ }
+ unlink(obj);
+ free(obj);
+ return 1;
+found:;
+ unlink(obj);
+ free(obj);
+ return 0;
+}
+
+int find_fstools_awk(const char *name, int logdepth, int fatal)
+{
+ const char *awk;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for awk... ");
+ logprintf(logdepth, "find_fstools_awk: trying to find awk...\n");
+ logdepth++;
+
+ awk = get("/arg/fstools/awk");
+ if (awk == NULL) {
+ if (test_awk(logdepth, "awk")) return 0;
+ if (test_awk(logdepth, "gawk")) return 0;
+ if (test_awk(logdepth, "mawk")) return 0;
+ if (test_awk(logdepth, "nawk")) return 0;
+ }
+ else {
+ report(" user provided (%s)...", awk);
+ if (test_awk(logdepth, awk)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_chmodx(const char *name, int logdepth, int fatal)
+{
+ const char *chmod;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for chmod to executable... ");
+ logprintf(logdepth, "find_fstools_awk: trying to find chmod to executable...\n");
+ logdepth++;
+
+ chmod = get("/arg/fstools/chmodx");
+ if (chmod == NULL) {
+ if (test_chmodx(logdepth, "chmod +x")) return 0;
+ if (test_chmodx(logdepth, "chmod 755")) return 0;
+ if (test_chmodx(logdepth, "")) return 0; /* on some systems we don't need to do anything */
+ }
+ else {
+ report(" user provided (%s)...", chmod);
+ if (test_chmodx(logdepth, chmod)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_cat(const char *name, int logdepth, int fatal)
+{
+ const char *cat;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for cat... ");
+ logprintf(logdepth, "find_fstools_cat: trying to find cat...\n");
+ logdepth++;
+
+ cat = get("/arg/fstools/cat");
+ if (cat == NULL) {
+ if (test_cat(logdepth, "cat")) return 0;
+ if (test_cat(logdepth, "type")) return 0;
+ }
+ else {
+ report(" user provided (%s)...", cat);
+ if (test_cat(logdepth, cat)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_sed(const char *name, int logdepth, int fatal)
+{
+ const char *sed;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for sed... ");
+ logprintf(logdepth, "find_fstools_sed: trying to find sed...\n");
+ logdepth++;
+
+ sed = get("/arg/fstools/sed");
+ if (sed == NULL) {
+ if (test_sed(logdepth, "sed")) return 0;
+ }
+ else {
+ report(" user provided (%s)...", sed);
+ if (test_sed(logdepth, sed)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_file_l(const char *name, int logdepth, int fatal)
+{
+ const char *file;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for file... ");
+ logprintf(logdepth, "find_fstools_file_l: trying to find file -L...\n");
+ logdepth++;
+
+ file = get("/arg/fstools/file_l");
+ if (file == NULL) {
+ if (test_file(logdepth, "fstools/file_l", "file -L")) return 0;
+ if (test_file(logdepth, "fstools/file_l", "file")) return 0;
+ }
+ else {
+ report(" user provided (%s)...", file);
+ if (test_file(logdepth, "fstools/file_l", file)) return 0;
+ }
+ return 1;
+}
+
+int find_fstools_file(const char *name, int logdepth, int fatal)
+{
+ const char *file;
+
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ report("Checking for file... ");
+ logprintf(logdepth, "find_fstools_file: trying to find file...\n");
+ logdepth++;
+
+ file = get("/arg/fstools/file");
+ if (file == NULL) {
+ if (test_file(logdepth, "fstools/file", "file")) return 0;
+ }
+ else {
+ report(" user provided (%s)...", file);
+ if (test_file(logdepth, "fstools/file", file)) return 0;
+ }
+ return 1;
+}
diff --git a/scconfig/src/default/find_io.c b/scconfig/src/default/find_io.c
new file mode 100644
index 0000000..c94a3d6
--- /dev/null
+++ b/scconfig/src/default/find_io.c
@@ -0,0 +1,189 @@
+/*
+ scconfig - detect I/O features of the system
+ Copyright (C) 2010 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_io_pipe(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " int fd[2];"
+ NL " if (pipe(fd) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for pipe(2)... ");
+ logprintf(logdepth, "find_io_pipe: trying to find pipe(2)...\n");
+ logdepth++;
+
+
+ if (try_icl(logdepth, "libs/io/pipe", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/io/pipe");
+}
+
+int find_io_dup2(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " int fd;"
+ NL " if (dup2(1, 4) == 4)"
+ NL " write(4, \"OK\\n\", 3); "
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for dup2(2)... ");
+ logprintf(logdepth, "find_io_dup2: trying to find dup2(2)...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/io/dup2", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/io/dup2");
+}
+
+
+int find_io_fileno(const char *name, int logdepth, int fatal)
+{
+ char test_c[256];
+ char *test_c_ =
+ NL "#include "
+ NL "int main() {"
+ NL no_implicit(int, "%s", "%s")
+ NL " if (%s(stdout) >= 0)"
+ NL " puts(\"OK\"); "
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for fileno(3)... ");
+ logprintf(logdepth, "find_io_fileno: trying to find fileno(3)...\n");
+ logdepth++;
+
+ /* UNIX */
+ sprintf(test_c, test_c_, "fileno", "fileno", "fileno");
+ if (try_icl(logdepth, "libs/io/fileno", test_c, "#include \n", NULL, NULL)) {
+ put("libs/io/fileno/call", "fileno");
+ return 0;
+ }
+
+ sprintf(test_c, test_c_, "fileno", "fileno", "fileno");
+ if (try_icl(logdepth, "libs/io/fileno", test_c, "#define _XOPEN_SOURCE\n#include \n", NULL, NULL)) {
+ put("libs/io/fileno/call", "fileno");
+ return 0;
+ }
+
+ /* windows */
+ sprintf(test_c, test_c_, "_fileno", "_fileno", "_fileno");
+ if (try_icl(logdepth, "libs/io/fileno", test_c, "#include \n", NULL, NULL)) {
+ put("libs/io/fileno/call", "_fileno");
+ return 0;
+ }
+
+ return try_fail(logdepth, "libs/io/fileno");
+}
+
+int find_io_lseek(const char *name, int logdepth, int fatal)
+{
+#define NODE "libs/io/lseek"
+
+ char test_c[3256];
+ const char *test_c_template =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " const char *filename = \"%s\";"
+ NL no_implicit(int, "%s", "%s")
+ NL " int fd = open(filename, O_WRONLY);"
+ NL " if (write(fd, \"abc\", 3) == 3 && %s(fd, 1, SEEK_SET) == 1)"
+ NL " puts(\"OK\"); "
+ NL " return 0;"
+ NL "}"
+ NL;
+ char *tmpf;
+ const char *incs[] = {"#include ","#include ",NULL};
+ const char *fns[] = {"lseek", "_lseek", NULL};
+ const char **inc;
+ const char **fn;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for lseek(2)... ");
+ logprintf(logdepth, "find_io_lseek: trying to find lseek(2)...\n");
+ logdepth++;
+
+ tmpf = tempfile_new(".psx");
+
+ for (inc = incs, fn = fns; *fn; ++inc, ++fn) {
+ sprintf(test_c, test_c_template, tmpf, *fn, *fn, *fn);
+ if (try_icl(logdepth, NODE, test_c, *inc, NULL, NULL)) {
+ put(NODE "/call", *fn);
+ return 0;
+ }
+ }
+
+ return try_fail(logdepth, NODE);
+#undef NODE
+}
+
+int find_io_popen(const char *name, int logdepth, int fatal)
+{
+ const char **i, *incs[] = {"#define _XOPEN_SOURCE", "#define _BSD_SOURCE", "#define _POSIX_C_SOURCE 2", NULL};
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " FILE *f = popen(\"echo OK\", \"r\");"
+ NL " char line[16];"
+ NL " if (f == NULL) return 0;"
+ NL " if (fgets(line, sizeof(line)-1, f) == NULL) return 0;"
+ NL " puts(line);"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for popen(3)... ");
+ logprintf(logdepth, "find_io_popen: trying to find popen(3)...\n");
+ logdepth++;
+
+ for(i = incs; *i != NULL; i++)
+ if (try_icl(logdepth, "libs/io/popen", test_c, *i, NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/io/popen");
+}
+
diff --git a/scconfig/src/default/find_libs.c b/scconfig/src/default/find_libs.c
new file mode 100644
index 0000000..4ba66f1
--- /dev/null
+++ b/scconfig/src/default/find_libs.c
@@ -0,0 +1,194 @@
+/*
+ scconfig - detection of standard library features
+ Copyright (C) 2009 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+static int trydlc(int logdepth, const char *test_c_dlc, const char *cflagsf, const char *ldflagsf, const char *dlc)
+{
+ char *cflags, *ldflags;
+
+ cflags = malloc(strlen(dlc) + 64);
+ ldflags = malloc(strlen(dlc)*2 + 256);
+ sprintf(cflags, cflagsf, dlc);
+ sprintf(ldflags, ldflagsf, dlc, dlc);
+ if (try_icl(logdepth, NULL, test_c_dlc, NULL, cflags, ldflags)) {
+ *cflags = ' ';
+ append("cc/cflags", cflags);
+ put("libs/ldl", ldflags);
+ put("libs/dl-compat", strue);
+ report("OK (%s and %s)\n", cflags, ldflags);
+ free(cflags);
+ free(ldflags);
+ return 1;
+ }
+ free(cflags);
+ free(ldflags);
+ return 0;
+}
+
+int find_lib_ldl(const char *name, int logdepth, int fatal)
+{
+ const char *ldl, *dlc;
+ char *s;
+
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " void *handle;"
+ NL " handle = dlopen(\"/this file does not exist.\", RTLD_NOW);"
+ NL " if (handle == NULL) printf(\"OK\\n\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ char *test_c_dlc =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " void *handle;"
+ NL " handle = dlopen(\"/this file does not exist.\", RTLD_NOW);"
+ NL " if (handle == NULL) printf(\"OK\\n\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for -ldl... ");
+ logprintf(logdepth, "find_lib_ldl: trying to find ldl...\n");
+ logdepth++;
+
+ ldl = get("/arg/libs/ldl");
+ if (ldl == NULL) {
+ dlc = get("/arg/libs/dl-compat");
+
+ if (dlc == NULL) {
+ /* If dlc is not explicitly requested by the user, try standard
+ dl (see whether we need -ldl for dlopen()) */
+ if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) {
+ put("libs/ldl", "");
+ put("libs/ldl/includes", "#include \\n");
+ report("OK ()\n");
+ return 0;
+ }
+ if (try_icl(logdepth, NULL, test_c, NULL, NULL, "-ldl")) {
+ put("libs/ldl", "-ldl");
+ put("libs/ldl/includes", "#include \\n");
+ report("OK (-ldl)\n");
+ return 0;
+ }
+ }
+ /* try dl-compat (dl compatibility lib) */
+ if (dlc != NULL) {
+ /* test at user supplied dlc prefix:
+ - first assume the linker will find it
+ - next assume gcc and pass rpath to the linker
+ - finally try static linking */
+ if (trydlc(logdepth, test_c_dlc, "-I%s/include", "-L%s/lib -ldl-compat\000%s", dlc)) {
+ put("libs/ldl/includes", "#include \\n");
+ return 0;
+ }
+ if (trydlc(logdepth, test_c_dlc, "-I%s/include", "-L%s/lib -Wl,-rpath=%s/lib -ldl-compat", dlc)) {
+ put("libs/ldl/includes", "#include \\n");
+ return 0;
+ }
+ if (trydlc(logdepth, test_c_dlc, "-I%s/include", "%s/lib/libdl-compat.a\000%s", dlc)) {
+ put("libs/ldl/includes", "#include \\n");
+ return 0;
+ }
+ }
+ else if (try_icl(logdepth, NULL, test_c_dlc, NULL, NULL, "-ldl-compat")) {
+ /* check at normal system installation */
+ put("libs/ldl", "-ldl-compat");
+ put("libs/ldl/includes", "#include \\n");
+ report("OK (-ldl-compat)\n");
+ return 0;
+ }
+ }
+ else {
+ report("User provided... ");
+ s = strclone(ldl);
+ if (try_icl(logdepth, NULL, test_c, NULL, NULL, s)) {
+ put("libs/ldl", ldl);
+ put("libs/ldl/includes", "#include \\n");
+ report("OK (%s)\n", ldl);
+ free(s);
+ return 0;
+ }
+ free(s);
+ }
+ report("Not found\n");
+ return 1;
+}
+
+int find_lib_LoadLibrary(const char *name, int logdepth, int fatal)
+{
+ /*char *s;*/
+
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " void *handle;"
+ NL " handle = LoadLibrary(\"/this file does not exist.\");"
+ NL " if (handle == NULL) printf(\"OK\\n\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for LoadLibrary... ");
+ logprintf(logdepth, "find_lib_LoadLibrary: trying to find LoadLibrary...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/LoadLibrary", test_c, "#include ", NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "libs/LoadLibrary");
+}
+
+int find_lib_errno(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " errno = 0;"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for errno.h... ");
+ logprintf(logdepth, "find_lib_errno: trying to find errno...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/errno", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "libs/errno");
+}
diff --git a/scconfig/src/default/find_printf.c b/scconfig/src/default/find_printf.c
new file mode 100644
index 0000000..1aeb782
--- /dev/null
+++ b/scconfig/src/default/find_printf.c
@@ -0,0 +1,318 @@
+/*
+ scconfig - detection of printf-related features
+ Copyright (C) 2009 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+static int tryx(int logdepth, const char *test_c, const char *trying, const char *expected)
+{
+ char *out = NULL;
+ char buff[512];
+
+ logprintf(logdepth, "trying '%s'\n", trying);
+ sprintf(buff, test_c, trying, trying);
+ if (compile_run(logdepth+1, buff, NULL, NULL, NULL, &out) == 0) {
+ if (strncmp(out, expected, strlen(expected)) == 0) {
+ free(out);
+ return 1;
+ }
+ free(out);
+ }
+ return 0;
+}
+
+static int tryc(int logdepth, const char *test_c, const char *trying)
+{
+ char *out = NULL;
+ char buff[512];
+ char *spc, *end;
+
+ logprintf(logdepth, "trying '%s'\n", trying);
+ sprintf(buff, test_c, trying);
+ if (compile_run(logdepth+1, buff, NULL, NULL, NULL, &out) == 0) {
+ spc = str_chr(out, ' ');
+ if (spc == NULL)
+ return 0;
+ *spc = '\0';
+ spc++;
+ end = str_chr(spc, ' ');
+ if (end == NULL)
+ return 0;
+ *end = '\0';
+ if (strcmp(out, spc) == 0) {
+ free(out);
+ put("libs/printf_ptrcast", trying);
+ report("OK (%s)\n", trying);
+ return 1;
+ }
+ free(out);
+ }
+ return 0;
+}
+
+int find_printf_x(const char *name, int logdepth, int fatal)
+{
+ const char *pfx;
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " printf(\"'%s%%x'/'%s%%x'\\n\", (size_t)0x1234, NULL);"
+ NL " return 0;"
+ NL "}"
+ NL;
+ char *expected = "'0x1234'/'0x0'";
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for printf %%x prefix... ");
+ logprintf(logdepth, "find_printf_x: trying to find printf %%x prefix...\n");
+ logdepth++;
+
+ pfx = get("/arg/libs/printf_x");
+ if (pfx == NULL) {
+
+ if (tryx(logdepth, test_c, "", expected)) {
+ put("libs/printf_x", "");
+ report("OK ()\n");
+ return 0;
+ }
+ if (tryx(logdepth, test_c, "0x", expected)) {
+ put("libs/printf_x", "0x");
+ report("OK (0x)\n");
+ return 0;
+ }
+ }
+ else {
+ report("User provided... ");
+ if (tryx(logdepth, test_c, pfx, expected)) {
+ put("libs/printf_x", pfx);
+ report("OK (%s)\n", pfx);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int find_printf_ptrcast(const char *name, int logdepth, int fatal)
+{
+ const char *cast;
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " printf(\"%%d %%d \\n\", sizeof(void *), sizeof(%s));"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for printf %%x pointer cast... ");
+ logprintf(logdepth, "find_printf_ptrcast: trying to find printf %%x pointer cast...\n");
+ logdepth++;
+
+ cast = get("/arg/libs/printf_ptrcast");
+ if (cast == NULL) {
+ if (tryc(logdepth, test_c, "unsigned int")) return 0;
+ if (tryc(logdepth, test_c, "unsigned long")) return 0;
+ if (tryc(logdepth, test_c, "unsigned long long")) return 0;
+ }
+ else {
+ report("User provided... ");
+ if (tryc(logdepth, test_c, cast)) return 0;
+ }
+ return 1;
+}
+
+int find_snprintf(const char *name, int logdepth, int fatal)
+{
+ char *out;
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " char buff[9];"
+ NL " char *s = buff+2;"
+ NL
+ NL " /* build a fence */"
+ NL " buff[0] = 0;"
+ NL " buff[1] = 65;"
+ NL " buff[7] = 66;"
+ NL " buff[8] = 0;"
+ NL
+ NL " snprintf(s, 4, \"%d\", 123456);"
+ NL " if ((buff[0] == 0) && (buff[1] == 65) && (buff[7] == 65) && (buff[8] == 0))"
+ NL " printf(\"%s\\n\", s);"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for snprintf... ");
+ logprintf(logdepth, "find_snprintf_works: trying to find snprintf...\n");
+ logdepth++;
+ logprintf(logdepth, "trying snprintf...\n");
+
+ if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) {
+ if (cross_blind) {
+ put("libs/snprintf", strue);
+ report("OK (can't check if safe)\n");
+ free(out);
+ return 0;
+ }
+ if (strcmp(out, "123")) {
+ put("libs/snprintf", strue);
+ put("libs/snprintf_safe", strue);
+ report("OK (safe)\n");
+ free(out);
+ return 0;
+ }
+ if (strcmp(out, "1234")) {
+ put("libs/snprintf", strue);
+ put("libs/snprintf_safe", sfalse);
+ report("OK (UNSAFE)\n");
+ free(out);
+ return 0;
+ }
+ free(out);
+ report("not found (broken output).\n");
+ }
+ else {
+ report("not found (no output).\n");
+ }
+ put("libs/snprintf", sfalse);
+ return 1;
+}
+
+int find_dprintf(const char *name, int logdepth, int fatal)
+{
+ char *out;
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " dprintf(1, \"OK\\n\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for dprintf... ");
+ logprintf(logdepth, "find_dprintf: trying to find dprintf...\n");
+ logdepth++;
+ logprintf(logdepth, "trying dprintf...\n");
+
+ if ((compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) && (strcmp(out, "OK"))) {
+ put("libs/dprintf", strue);
+ report("found\n");
+ free(out);
+ return 0;
+ }
+ put("libs/dprintf", sfalse);
+ report("not found\n");
+ return 1;
+}
+
+int find_vdprintf(const char *name, int logdepth, int fatal)
+{
+ char *out;
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "void local_dprintf(int fd, const char *fmt, ...)"
+ NL "{"
+ NL " va_list ap;"
+ NL " va_start(ap, fmt);"
+ NL " vdprintf(fd, fmt, ap);"
+ NL "}"
+ NL "int main() {"
+ NL " local_dprintf(1, \"OK\\n\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for vdprintf... ");
+ logprintf(logdepth, "find_vdprintf: trying to find vdprintf...\n");
+ logdepth++;
+ logprintf(logdepth, "trying vdprintf...\n");
+
+ if ((compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) && (strcmp(out, "OK"))) {
+ put("libs/vdprintf", strue);
+ report("found\n");
+ free(out);
+ return 0;
+ }
+ put("libs/vdprintf", sfalse);
+ report("not found\n");
+ return 1;
+}
+
+int find_vsnprintf(const char *name, int logdepth, int fatal)
+{
+ char *out;
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "#include "
+ NL "void local_vsnprintf(char *s, int len, const char *fmt, ...)"
+ NL "{"
+ NL " va_list ap;"
+ NL " va_start(ap, fmt);"
+ NL " vsnprintf(s, len, fmt, ap);"
+ NL "}"
+ NL "int main() {"
+ NL " char s[16];"
+ NL " *s = '\\0';"
+ NL " local_vsnprintf(s, 14, \"OK\\n\");"
+ NL " printf(\"%s\", s);"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for vsnprintf... ");
+ logprintf(logdepth, "find_vsnprintf: trying to find vsnprintf...\n");
+ logdepth++;
+ logprintf(logdepth, "trying vsnprintf...\n");
+
+ if ((compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) && (strcmp(out, "OK"))) {
+ put("libs/vsnprintf", strue);
+ report("found\n");
+ free(out);
+ return 0;
+ }
+ put("libs/vsnprintf", sfalse);
+ report("not found\n");
+ return 1;
+}
diff --git a/scconfig/src/default/find_proc.c b/scconfig/src/default/find_proc.c
new file mode 100644
index 0000000..fec68a4
--- /dev/null
+++ b/scconfig/src/default/find_proc.c
@@ -0,0 +1,137 @@
+/*
+ scconfig - detection of standard library features (processes)
+ Copyright (C) 2016 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_proc__spawnvp(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " const char *a[3] = {\"/c\", \"echo OK\", NULL};"
+ NL " _spawnvp(_P_WAIT, \"cmd\", a);"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for _spawnvp... ");
+ logprintf(logdepth, "find_proc__spawnvp: trying to find _spawnvp...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/proc/_spawnvp", test_c, "#include ", NULL, NULL)) return 0;
+
+ return try_fail(logdepth, "libs/proc/_spawnvp");
+}
+
+
+
+int find_proc_fork(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " if (fork() == 0) { return 0; }"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ /* NOTE: can't print OK from the child process because of a possible race
+ with the parent immediately exiting without wait(). */
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for fork... ");
+ logprintf(logdepth, "find_proc_fork: trying to find fork...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/proc/fork", test_c, "#include ", NULL, NULL)) return 0;
+
+ return try_fail(logdepth, "libs/proc/fork");
+}
+
+
+int find_proc_wait(const char *name, int logdepth, int fatal)
+{
+ char *inc;
+ const char *inc1;
+ char test_c[1024];
+ char *test_c_in =
+ NL "%s\n"
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " int st = 0;"
+ NL " if (fork() == 0) {"
+ NL " printf(\"O\");"
+ NL " return 42;"
+ NL " }"
+ NL " wait(&st);"
+ NL " if (WIFEXITED(st) && (WEXITSTATUS(st) == 42))"
+ NL " printf(\"K\");"
+ NL " else"
+ NL " printf(\"%%d\", st);"
+ NL " printf(\"\\n\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ require("cc/cc", logdepth, fatal);
+ if (require("libs/proc/fork", logdepth, fatal))
+ return try_fail(logdepth, "libs/proc/wait");
+
+ report("Checking for wait... ");
+ logprintf(logdepth, "find_proc_wait: trying to find wait...\n");
+ logdepth++;
+
+ inc1 = get("libs/proc/fork/includes");
+ if (inc1 != NULL) {
+ char *i, *o;
+ inc = strclone(inc1);
+ for(i = o = inc; *i != '\0'; i++,o++) {
+ if ((i[0] == '\\') && (i[1] == 'n')) {
+ *o = '\n';
+ i++;
+ }
+ else
+ *o = *i;
+ }
+ *o = '\0';
+ sprintf(test_c, test_c_in, inc);
+ free(inc);
+ }
+ else
+ sprintf(test_c, test_c_in, "");
+
+ if (try_icl(logdepth, "libs/proc/wait", test_c, "#include \n#include ", NULL, NULL)) return 0;
+
+ return try_fail(logdepth, "libs/proc/wait");
+}
diff --git a/scconfig/src/default/find_signal.c b/scconfig/src/default/find_signal.c
new file mode 100644
index 0000000..c7f46f8
--- /dev/null
+++ b/scconfig/src/default/find_signal.c
@@ -0,0 +1,141 @@
+/*
+ scconfig - detection of standard library features
+ Copyright (C) 2009 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+static int try_bad(int logdepth, const char *test_c, char *cflags, char *ldflags)
+{
+ char *out = NULL;
+
+ logprintf(logdepth, "trying signal (neg) with ldflags '%s'\n", ldflags == NULL ? get("cc/ldflags") : ldflags);
+ if (compile_run(logdepth+1, test_c, NULL, cflags, ldflags, &out) == 0) {
+ if (target_emu_fail(out) || (strncmp(out, "BAD", 3) == 0)) {
+ free(out);
+ return 0;
+ }
+ free(out);
+ }
+ return 1;
+}
+
+
+int find_signal_raise(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main(int argc, char *argv[]) {"
+ NL " printf(\"OK\\n\");"
+ NL " if (argc == 16)"
+ NL " raise(1);"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for raise()... ");
+ logprintf(logdepth, "find_signal_raise: trying to find raise()...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "signal/raise", test_c, NULL, NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "signal/raise");
+}
+
+
+int find_signal_names(const char *rname, int logdepth, int fatal)
+{
+ char *test_c_exists =
+ NL "#include "
+ NL "#include "
+ NL "int main(int argc, char *argv[]) {"
+ NL " printf(\"OK\\n\");"
+ NL " if (argc == 16)"
+ NL " raise(%s);"
+ NL " return 0;"
+ NL "}"
+ NL;
+ char *test_c_terms =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " raise(%s);"
+ NL " printf(\"BAD\\n\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ char test_c[256];
+ const char *names[] = {"SIGINT", "SIGABRT", "SIGKILL", "SIGTERM", "SIGQUIT", "SIGHUP", "SIGFPE", "SIGSEGV", "SIGPIPE", NULL};
+ const char **name;
+ char path[256], *pathend;
+ const char *prefix = "signal/names/";
+
+ require("cc/cc", logdepth, fatal);
+ require("signal/raise/*", logdepth, fatal);
+
+ strcpy(path, prefix);
+ pathend = path + strlen(prefix);
+
+ for(name = names; *name != NULL; name++) {
+ /* check whether it exists */
+ report("Checking whether %s exists... ", *name);
+ logprintf(logdepth, "find_signal_names: checking whether %s exists\n", *name);
+ logdepth++;
+ sprintf(test_c, test_c_exists, *name);
+ strcpy(pathend, *name);
+ if (!try_icl(logdepth, path, test_c, NULL, NULL, NULL)) {
+ logdepth--;
+ continue;
+ }
+
+ /* check whether it exists */
+ logdepth--;
+ report("Checking whether %s terminates... ", *name);
+ logprintf(logdepth, "find_signal_names: checking whether %s terminates\n", *name);
+ logdepth++;
+
+ sprintf(test_c, test_c_terms, *name);
+ sprintf(pathend, "%s/terminates", *name);
+ if (try_bad(logdepth, test_c, NULL, "")) {
+ put(path, strue);
+ report("terminates\n");
+ }
+ else {
+ report("does not terminate\n");
+ put(path, sfalse);
+ }
+ logdepth--;
+ }
+
+/* to avoid redetection */
+ put("signal/names/presents", strue);
+
+ return 0;
+}
+
diff --git a/scconfig/src/default/find_str.c b/scconfig/src/default/find_str.c
new file mode 100644
index 0000000..5e29531
--- /dev/null
+++ b/scconfig/src/default/find_str.c
@@ -0,0 +1,76 @@
+/*
+ scconfig - detection of standard library features: strings
+ Copyright (C) 2017 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_strcasecmp(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " if ((strcasecmp(\"foo\", \"FoO\") == 0) && (strcasecmp(\"foo\", \"bar\") != 0))"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for strcasecmp()... ");
+ logprintf(logdepth, "find_fs_strcasecmp: trying to find strcasecmp...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "str/strcasecmp", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "str/strcasecmp");
+}
+
+
+int find_strncasecmp(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "#include "
+ NL "int main() {"
+ NL " if ((strncasecmp(\"foo1\", \"FoO2\", 3) == 0) && (strncasecmp(\"foo1\", \"bar2\", 3) != 0))"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for strncasecmp()... ");
+ logprintf(logdepth, "find_fs_strncasecmp: trying to find strncasecmp...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "str/strncasecmp", test_c, NULL, NULL, NULL)) return 0;
+ return try_fail(logdepth, "str/strncasecmp");
+}
+
diff --git a/scconfig/src/default/find_sys.c b/scconfig/src/default/find_sys.c
new file mode 100644
index 0000000..79803cf
--- /dev/null
+++ b/scconfig/src/default/find_sys.c
@@ -0,0 +1,491 @@
+/*
+ scconfig - detect features of the system or the host/target computer
+ Copyright (C) 2009..2012 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_sys_ptrwidth(const char *name, int logdepth, int fatal)
+{
+ char *end, W[32];
+ char *out = NULL;
+ int w;
+
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " void *ptr;"
+ NL " printf(\"%d\\n\", sizeof(ptr));"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for pointer width... ");
+ logprintf(logdepth, "find_sys_ptrwidth: trying to find pointer width...\n");
+ logdepth++;
+
+ if (compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) {
+ w = strtol(out, &end, 10);
+ if ((*end != '\0') && (*end != '\n') && (*end != '\r')) {
+ report("FAILED (test code failed)\n");
+ logprintf(logdepth+1, "FAILED: returned '%s' which is not a valid decimal number (at '%s')\n", out, end);
+ return 1;
+ }
+ sprintf(W, "%d", w * 8);
+ report("OK (%s bits)\n", W);
+ put("sys/ptrwidth", W);
+ logprintf(logdepth+1, "OK (%s bits)\n", W);
+ }
+ return 0;
+}
+
+int find_sys_byte_order(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " long int i = 8;"
+ NL " char *s = (char *)&i;"
+ NL " printf(\"%d\\n\", s[0]);"
+ NL " return 0;"
+ NL "}"
+ NL;
+ const char* test_c_blind_template =
+ NL "#include "
+ NL "#include "
+ NL "#ifndef __BYTE_ORDER"
+ NL "#error \"ERROR 1\""
+ NL "void void *;"
+ NL "#endif"
+ NL "#ifndef __%s_ENDIAN"
+ NL "#error \"ERROR 2\""
+ NL "char char *;"
+ NL "#endif"
+ NL "#if __BYTE_ORDER != __%s_ENDIAN"
+ NL "#error \"ERROR 3\""
+ NL "int int *;"
+ NL "#endif"
+ NL "int main() {"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ const char *key = "sys/byte_order";
+ const char *endians1[] = { "LITTLE", "BIG", NULL };
+ const char *endians2[] = { "LSB", "MSB", NULL };
+ int index;
+ char test_c_blind[1024];
+ char *end;
+ const char *W;
+ char *out = NULL;
+ int w;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for byte order... ");
+ logprintf(logdepth, "find_sys_byte_order: trying to find byte order...\n");
+ logdepth++;
+
+ if ((!isblind(db_cwd)) && compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) {
+ w = strtol(out, &end, 10);
+ if (((*end != '\0') && (*end != '\n') && (*end != '\r')) || ((w != 0) && (w != 8))) {
+ report("FAILED (test code failed)\n");
+ logprintf(logdepth+1, "FAILED: returned '%s' which is not a valid decimal number (at '%s')\n", out, end);
+ return 1;
+ }
+ if (w == 0)
+ W = "MSB";
+ else
+ W = "LSB";
+
+ report("OK (%s first)\n", W);
+ put("sys/byte_order", W);
+ logprintf(logdepth+1, "OK (%s first)\n", W);
+ return 0;
+ }
+
+ for (index=0; endians1[index]!=NULL; ++index)
+ {
+ sprintf(test_c_blind, test_c_blind_template, endians1[index], endians1[index]);
+
+ if (compile_run(logdepth, test_c_blind, NULL, NULL, NULL, NULL) == 0)
+ {
+ W = endians2[index];
+ report("OK (%s first)\n", W);
+ put(key, W);
+ return 0;
+ }
+ }
+
+ report("FAILED (cannot determine byte order, you must supply it)\n");
+ return try_fail(logdepth, key);
+}
+
+static int test_shell_eats_backslash(int logdepth)
+{
+ char *test = "echo c:\\n";
+ char *out;
+
+
+ logprintf(logdepth, "testing if shell eats \\...\n");
+ run_shell(logdepth+1, test, &out);
+ if (out == NULL) {
+ logprintf(logdepth+1, "oops, couldn't run shell?! (returned NULL)\n");
+ report("ERROR: shell fails.");
+ abort();
+ }
+ if (out[2] == '\\') {
+ logprintf(logdepth, "shell does NOT eat \\...\n");
+ put("sys/shell_eats_backslash", sfalse);
+ free(out);
+ return 0;
+ }
+
+ free(out);
+ logprintf(logdepth, "shell eats \\...\n");
+ put("sys/shell_eats_backslash", strue);
+ return 1;
+}
+
+
+static int try_get_cwd(int logdepth, const char *cmd)
+{
+ char *cwd, *end;
+ run_shell(logdepth+1, cmd, &cwd);
+ if (cwd != NULL) {
+ end = strpbrk(cwd, "\r\n");
+ if (end != NULL)
+ *end = '\0';
+ if (*cwd != '\0') {
+ end = cwd + strlen(cwd) - 1;
+ while((*end == ' ') || (*end == '\t')) { *end = '\0'; end--; }
+ put("sys/tmp", cwd);
+ /* ugly hack for win32: paths there start as c:\ */
+ if ((cwd[1] == ':') && (cwd[2] == '\\'))
+ append("sys/tmp", "\\");
+ else
+ append("sys/tmp", "/");
+ logprintf(logdepth, "cwd is '%s'\n", get("sys/tmp"));
+ free(cwd);
+ return 1;
+ }
+ else
+ free(cwd);
+ }
+ return 0;
+}
+
+static int try_tmp(int logdepth)
+{
+ char *fn;
+
+ logprintf(logdepth, "validating temp dir '%s'\n", get("sys/tmp"));
+ fn = tempfile_new_noabort("");
+ if (fn != NULL) {
+ unlink(fn);
+ free(fn);
+ logprintf(logdepth, "temp dir works!\n");
+ return 1;
+ }
+
+ logprintf(logdepth, "temp dir fails\n");
+ return 0;
+}
+
+/* test temp dir with all sort of workarounds */
+static int try_tmp_all(int logdepth)
+{
+ const char *tmp, *si;
+ char c;
+ char *t, *so, *old_tmp;
+ int eats, n;
+
+ tmp = get("sys/tmp");
+
+ /* path must end in path separator */
+ c = tmp[strlen(tmp)-1];
+ if ((c != '/') && (c != '\\')) {
+ append("sys/tmp", "/");
+ tmp = get("sys/tmp");
+ }
+
+ logprintf(logdepth, "trying detected temp dir '%s'\n", tmp);
+ if (try_tmp(logdepth+1)) return 1;
+
+ /* try msys-on-windows hack: if path starts with /d/something, try d:/ instead */
+ if ((tmp[0] == '/') && (isalpha(tmp[1])) && (tmp[2] == '/')) {
+ /* for the next test we shouldn't use our half-detected tmp path but go with . */
+ old_tmp = strclone(tmp);
+ put("sys/tmp", "");
+ eats = istrue(get("sys/shell_eats_backslash"));
+ tmp = old_tmp;
+ logprintf(logdepth, "tmp2='%s' eats=%d\n", tmp, eats);
+ t = malloc(strlen(tmp) * 2);
+ t[0] = tmp[1];
+ t[1] = ':';
+ for(si = tmp + 2, so = t + 2; *si != '\0'; si++, so++) {
+ if (*si == '/') {
+ *so = '\\';
+ if (eats) {
+ for(n = 0; n < 3; n++) {
+ so++;
+ *so = '\\';
+ }
+ }
+ }
+ else
+ *so = *si;
+ }
+ *so = '\0';
+ free(old_tmp);
+
+ logprintf(logdepth, "trying windows fix: '%s'\n", t);
+ put("sys/tmp", t);
+ free(t);
+ if (try_tmp(logdepth+1)) {
+ if (eats)
+ put("sys/path_sep", "\\\\\\\\");
+ else
+ put("sys/path_sep", "\\");
+ return 1;
+ }
+ tmp = get("sys/tmp");
+ }
+
+ /* fail. Set back tmp to empty so next command has a chance to run */
+ put("sys/tmp", "");
+ return 0;
+}
+
+int find_tmp(const char *name, int logdepth, int fatal)
+{
+ const char *usertmp;
+
+ if (in_cross_target) {
+ report("Temp dir for cross compilation target is the same as for host...");
+ logprintf(logdepth, "Copying temp dir from host to target\n");
+ require("/host/sys/tmp", logdepth, fatal);
+ usertmp = get("/host/sys/tmp");
+ if (usertmp == NULL) {
+ report("Host temp dir not found.\n");
+ logprintf(logdepth, "Host temp dir not found.\n");
+ return 1;
+ }
+ put("sys/tmp", usertmp);
+ return 0;
+ }
+
+ /* we need shell for later tests; do this detection in . */
+ put("sys/tmp", "");
+ require("sys/shell", logdepth, fatal);
+
+ put("sys/path_sep", "/");
+
+ report("Detecting temp dir...");
+ logprintf(logdepth, "Finding temp dir (current working directory)...\n");
+
+ usertmp = get("/arg/sys/tmp");
+
+ /* . as tmp would fail for commands including a "cd" - this would cause
+ temporary files left in the target dir. We start out with empty
+ string (which is ., but on windows ./ would fail), and run
+ pwd (without cd) to find out the current directory (as getcwd() is not
+ portable). If pwd fails, we stay with ./ */
+ put("sys/tmp", "");
+
+ /* we need to know about shell backslash problem regardless of how
+ we end up with tmp - currently tmp is ., where the test could run
+ safely */
+ test_shell_eats_backslash(logdepth+1);
+
+ /* Special case: cross-compilation with emulator; we can not assume
+ the emulator uses the same paths as the host system, while we mix
+ accessing files from host and emu. If we stay in ., both emulator
+ and host system should be (more or less) happy. */
+ if (istarget(db_cwd) && iscross) {
+ if (usertmp == NULL) {
+ report("using temp dir . for cross-compilation\n");
+ logprintf(logdepth, "staying with . for cross-compilation\n");
+ }
+ else {
+ put("sys/tmp", usertmp);
+ report("using user supplied temp dir '%s' for cross-compilation\n", usertmp);
+ logprintf(logdepth, "using user supplied temp dir '%s' for cross-compilation\n", usertmp);
+ }
+ return 0;
+ }
+
+ if ((usertmp != NULL))
+ put("sys/tmp", usertmp);
+
+ if (
+ ((usertmp != NULL) && (try_tmp_all(logdepth+2))) || /* try user supplied temp dir */
+ ((try_get_cwd(logdepth+1, "pwd")) && (try_tmp_all(logdepth+2))) || /* try pwd for finding out cwd */
+ ((try_get_cwd(logdepth+1, "echo %cd%") && (try_tmp_all(logdepth+2))))) { /* try windows-specific way for finding out cwd */
+
+ report(" validated %s\n", get("sys/tmp"));
+ logprintf(logdepth, "Detected temp dir '%s'\n", get("sys/tmp"));
+ return 0;
+ }
+
+ put("sys/tmp", "");
+ report("using temp dir fallback .\n");
+ logprintf(logdepth, "all temp directories failed, using . as tmp\n");
+ return 0;
+}
+
+int test_shell(const char *shell, int logdepth, int quote)
+{
+ char *test = "echo hello";
+ char *cmd;
+ char *out;
+ char *q;
+
+ if (quote)
+ q = "\"";
+ else
+ q = "";
+
+ logprintf(logdepth, "testing '%s' as shell\n", shell);
+ cmd = malloc(strlen(test) + strlen(shell) + 8);
+ sprintf(cmd, "%s %s%s%s", shell, q, test, q);
+
+ run(logdepth+1, cmd, &out);
+
+ free(cmd);
+
+ if ((out != NULL) && (strncmp(out, "hello", 5) == 0)) {
+ put("sys/shell", shell);
+ if (quote)
+ put("sys/shell_needs_quote", strue);
+ else
+ put("sys/shell_needs_quote", sfalse);
+ logprintf(logdepth, "accepted.\n");
+ free(out);
+ return 1;
+ }
+
+ logprintf(logdepth, "refused.\n");
+ free(out);
+ return 0;
+}
+
+static int find_shell_escape(const char *name, int logdepth, int fatal, const char *shell)
+{
+ char cmdline[256];
+ char **t;
+ char *tests[] = {
+ "\\", "\\ {}&;|",
+ "^", "^ &",
+ NULL, NULL
+ };
+ (void) fatal; /* not used */
+ (void) shell; /* not used */
+
+ report("Looking for a shell escape character... ");
+ logprintf(logdepth, "finding shell escape character...\n");
+
+ for(t = tests; *t != NULL; t += 2) {
+ char *s, *end, *out, *start;
+ strcpy(cmdline, "echo ");
+ end = cmdline+5;
+ for(s = t[1]; *s != '\0'; s++) {
+ *end++ = *t[0];
+ *end++ = *s;
+ }
+ *end = '\0';
+ run(logdepth+1, cmdline, &out);
+ if (out != NULL) {
+ int res;
+ if (*out == '\"') /* wine likes to wrap the output in quotes for some reason */
+ start = out+1;
+ else
+ start = out;
+
+ res = strncmp(start, t[1], strlen(t[1]));
+ free(out);
+ if (res == 0) {
+ report("found: '%s'\n", t[0]);
+ logprintf(logdepth, "found shell escape char '%s'\n", t[0]);
+ put("sys/shell_escape_char", t[0]);
+ return 0;
+ }
+ }
+ }
+ report("NOT FOUND\n");
+ logprintf(logdepth, "shell escape character not found\n");
+
+ return 1;
+}
+
+int find_shell(const char *name, int logdepth, int fatal)
+{
+ const char *shells[] = {
+ "/bin/sh -c",
+ "/bin/bash -c",
+ "bash -c",
+ "cmd.exe /c",
+ "sh -c",
+ "/bin/dash -c",
+ "dash -c",
+ "/bin/ksh -c",
+ "ksh -c",
+ NULL
+ };
+ const char **s;
+
+ if (cross_blind) {
+ const char *shell = get("/arg/sys/target-shell");
+ if (shell == NULL) {
+ report("Need to specify sys/target-shell in blind cross compiling mode, because the shell cannot be detected (note: scconfig will not attempt to run the target shell)\n");
+ exit(1);
+ }
+
+ put("sys/shell", shell);
+ report("Blind cross compiling: accepting '%s' as shell\n", shell);
+ logprintf(logdepth, "Blind cross compiling: accepting '%s' as shell\n", shell);
+ return 0;
+ }
+
+ report("Looking for a shell... ");
+ logprintf(logdepth, "finding a shell\n");
+
+ for(s = shells; *s != NULL; s++) {
+ if ((test_shell(*s, logdepth+1, 0)) || (test_shell(*s, logdepth+1, 1))) {
+ report("%s\n", *s);
+ logprintf(logdepth, "found a shell '%s', need quote: %s\n", *s, get("sys/shell_needs_quote"));
+ return find_shell_escape(name, logdepth, fatal, *s);
+ }
+ }
+
+ report("NOT FOUND\n");
+ logprintf(logdepth, "shell not found\n");
+ return 1;
+}
diff --git a/scconfig/src/default/find_target.c b/scconfig/src/default/find_target.c
new file mode 100644
index 0000000..2a6993d
--- /dev/null
+++ b/scconfig/src/default/find_target.c
@@ -0,0 +1,58 @@
+/*
+ scconfig - glue layer for proper crosscompiling to target
+ Copyright (C) 2009 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include "db.h"
+#include "libs.h"
+
+int find_target(const char *name, int logdepth, int fatal)
+{
+ const char *target = get("/arg/sys/target");
+ const char *emu = get("/arg/sys/emu");
+
+ (void) logdepth; /* to suppress compiler warnings about not using logdepth */
+ (void) fatal; /* to suppress compiler warnings about not using fatal */
+
+ /* Does target differ from host? */
+ if (target == NULL) {
+ db_link("/host", "/target");
+#ifdef RUNTIME
+ db_link("/host", "/runtime");
+#endif
+ put("/target/sys/cross", sfalse);
+ put("/target/sys/cross_blind", sfalse);
+ return 0;
+ }
+ else
+ db_mkdir("/target");
+
+ put("/target/sys/target", target);
+ put("/target/sys/cross", strue);
+ if (emu != NULL)
+ put("/target/sys/emu", emu);
+
+ /* If so, check if emulator is provided */
+ cross_blind = ((emu == NULL) || (*emu == '\0'));
+ put("/target/sys/cross_blind", cross_blind ? strue : sfalse);
+
+ return 0;
+}
diff --git a/scconfig/src/default/find_thread.c b/scconfig/src/default/find_thread.c
new file mode 100644
index 0000000..4ab793b
--- /dev/null
+++ b/scconfig/src/default/find_thread.c
@@ -0,0 +1,223 @@
+/*
+ scconfig - detection of standard library features
+ Copyright (C) 2009,2017 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_lib_lpthread(const char *name, int logdepth, int fatal)
+{
+ const char *lpthread;
+ char *s;
+ int ret = 0;
+
+ char *test_c_recursive =
+ NL "#define _GNU_SOURCE 1 /* Needed for recursive thread-locking */"
+ NL "#include "
+ NL "pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;"
+ NL "int main() {"
+ NL " pthread_attr_t a;"
+ NL " if (pthread_attr_init(&a) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ char *test_c_simple =
+ NL "#include "
+ NL "int main() {"
+ NL " pthread_attr_t a;"
+ NL " if (pthread_attr_init(&a) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL ;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for -lpthread... ");
+ logprintf(logdepth, "find_lib_lpthread: trying to find lpthread...\n");
+ logdepth++;
+
+ lpthread = get("/arg/libs/lpthread");
+
+ if (lpthread != NULL) {
+ put("libs/lpthread", lpthread);
+ report("User provided... ");
+ s = strclone(lpthread);
+ }
+ else
+ s = strclone("-lpthread");
+
+ if (try_icl(logdepth, NULL, test_c_recursive, NULL, NULL, s)) {
+ put("libs/lpthread", s);
+ put("libs/lpthread-recursive", strue);
+ report("OK, recursive (%s)\n", s);
+ }
+ else if (try_icl(logdepth, NULL, test_c_simple, NULL, NULL, s)) {
+ put("libs/lpthread", s);
+ put("libs/lpthread-recursive", sfalse);
+ report("OK, NOT RECURSIVE (%s)\n", s);
+ }
+ else
+ ret = 1;
+
+ free(s);
+ return ret;
+}
+
+int find_thread_semget(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "int main()"
+ NL "{"
+ NL " int semid = semget(IPC_PRIVATE, 1, IPC_CREAT);"
+ NL " if (semid < 0) return 0;"
+ NL " if(semctl(semid, 0, IPC_RMID) < 0) return 0;"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ const char *node = "thread/semget";
+ char **inc, *incs[] = {"#include \n#include \n#include ", "#include \n#include ", "#include \n#include ", NULL};
+
+ if (require("cc/cc", logdepth, fatal))
+ return try_fail(logdepth, node);
+
+ report("Checking for semget... ");
+ logprintf(logdepth, "find_semget:\n");
+ logdepth++;
+
+ for(inc = incs; *inc != NULL; inc++)
+ if (try_icl(logdepth, node, test_c, *inc, NULL, NULL) != 0)
+ return 0;
+
+ return try_fail(logdepth, node);
+}
+
+int find_thread_pthread_create(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "void* test_thread(void* param)"
+ NL "{"
+ NL " return NULL;"
+ NL "}"
+ NL "int main()"
+ NL "{"
+ NL " pthread_t pt;"
+ NL " if (pthread_create(&pt, NULL, test_thread, NULL) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ const char *node = "thread/pthread_create";
+ char **inc, *incs[] = {"#include ", "#include \n#include ", NULL};
+ const char *lpthread;
+ char* s;
+
+ if (require("cc/cc", logdepth, fatal))
+ return try_fail(logdepth, node);
+
+ report("Checking for pthread_create... ");
+ logprintf(logdepth, "find_pthread_create:\n");
+ logdepth++;
+
+ lpthread = get("/arg/libs/lpthread");
+
+ if (lpthread != NULL) {
+ report("User provided... ");
+ s = strclone(lpthread);
+ }
+ else
+ s = strclone("-lpthread");
+
+ for(inc = incs; *inc != NULL; inc++)
+ if (try_icl(logdepth, node, test_c, *inc, NULL, s) != 0) {
+ free(s);
+ return 0;
+ }
+
+ free(s);
+ return try_fail(logdepth, node);
+}
+
+int find_thread_CreateSemaphore(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "int main()"
+ NL "{"
+ NL " if (CreateSemaphore(NULL, 1, 1, NULL))"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ const char *node = "thread/CreateSemaphore";
+
+ if (require("cc/cc", logdepth, fatal))
+ return try_fail(logdepth, node);
+
+ report("Checking for CreateSemaphore... ");
+ logprintf(logdepth, "find_thread_CreateSemaphore:\n");
+ logdepth++;
+
+ if (try_icl(logdepth, node, test_c, "#include ", NULL, NULL) != 0)
+ return 0;
+
+ return try_fail(logdepth, node);
+}
+
+int find_thread_CreateThread(const char *name, int logdepth, int fatal)
+{
+ const char *test_c =
+ NL "#include "
+ NL "DWORD WINAPI test_thread(void* param)"
+ NL "{"
+ NL " return 0;"
+ NL "}"
+ NL "int main()"
+ NL "{"
+ NL " if (CreateThread(NULL, 0, test_thread, NULL, 0, NULL))"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+ const char *node = "thread/CreateThread";
+
+ if (require("cc/cc", logdepth, fatal))
+ return try_fail(logdepth, node);
+
+ report("Checking for CreateThread... ");
+ logprintf(logdepth, "find_thread_CreateThread:\n");
+ logdepth++;
+
+ if (try_icl(logdepth, node, test_c, "#include ", NULL, NULL) != 0)
+ return 0;
+
+ return try_fail(logdepth, node);
+}
diff --git a/scconfig/src/default/find_time.c b/scconfig/src/default/find_time.c
new file mode 100644
index 0000000..e7189e3
--- /dev/null
+++ b/scconfig/src/default/find_time.c
@@ -0,0 +1,247 @@
+/*
+ scconfig - detection of standard library features: time/date/sleep related calls
+ Copyright (C) 2011..2012 Tibor Palinkas
+
+ 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.1 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
+
+ Project page: http://repo.hu/projects/scconfig
+ Contact via email: scconfig [at] igor2.repo.hu
+*/
+
+#include
+#include
+#include
+#include "libs.h"
+#include "log.h"
+#include "db.h"
+#include "dep.h"
+
+int find_time_usleep(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " if (usleep(1) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for usleep()... ");
+ logprintf(logdepth, "find_time_usleep: trying to find usleep...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/time/usleep", test_c, NULL, NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "libs/time/usleep");
+}
+
+int find_time_Sleep(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "int main() {"
+ NL " Sleep(1);"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for Sleep()... ");
+ logprintf(logdepth, "find_time_Sleep: trying to find Sleep...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/time/Sleep", test_c, "#include ", NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "libs/time/Sleep");
+}
+
+int find_time_gettimeofday(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "#include "
+ NL "int main() {"
+ NL " struct timeval tv;"
+ NL " if (gettimeofday(&tv, NULL) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for gettimeofday()... ");
+ logprintf(logdepth, "find_time_gettimeofday: trying to find gettimeofday...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/time/gettimeofday", test_c, "#include ", NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "libs/time/gettimeofday");
+}
+
+
+int find_time_ftime(const char *name, int logdepth, int fatal)
+{
+ char *test_c =
+ NL "int main() {"
+ NL " struct timeb tb;"
+ NL " if (ftime(&tb) == 0)"
+ NL " puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL;
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for ftime()... ");
+ logprintf(logdepth, "find_time_ftime: trying to find ftime...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/time/ftime", test_c, "#include ", NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "libs/time/ftime");
+}
+
+static const char timegm_test_c_template[] =
+ NL "void my_puts(const char *s);"
+ NL "int main() {"
+ NL " struct tm tm;"
+ NL " tm.tm_sec = 50;"
+ NL " tm.tm_min = 30;"
+ NL " tm.tm_hour = 6;"
+ NL " tm.tm_mday = 1;"
+ NL " tm.tm_mon = 11;"
+ NL " tm.tm_year = 2018 - 1900;"
+ NL " tm.tm_wday = 0;"
+ NL " tm.tm_yday = 0;"
+ NL " if (%s(&tm) != (time_t)(-1))"
+ NL " my_puts(\"OK\");"
+ NL " return 0;"
+ NL "}"
+ NL "#include "
+ NL "void my_puts(const char *s)"
+ NL "{"
+ NL " puts(s);"
+ NL "}"
+ NL;
+
+int find_time_timegm(const char *name, int logdepth, int fatal)
+{
+ char test_c[1000];
+ sprintf(test_c, timegm_test_c_template, "timegm");
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for timegm()... ");
+ logprintf(logdepth, "find_time_timegm: trying to find timegm...\n");
+ logdepth++;
+
+ if (try_icl(logdepth, "libs/time/timegm", test_c, "#include ", NULL, NULL))
+ return 0;
+ return try_fail(logdepth, "libs/time/timegm");
+}
+
+int find_time_mkgmtime(const char *name, int logdepth, int fatal)
+{
+ char test_c[1000];
+ const char *ldflags[] = {"","-lmsvcr120","-lmsvcr110","-lmsvcr100","-lmsvcr90","-lmsvcr80","-lmsvcr71","-lmsvcr70",NULL};
+ const char **ldf;
+
+ sprintf(test_c, timegm_test_c_template, "_mkgmtime");
+
+ require("cc/cc", logdepth, fatal);
+
+ report("Checking for _mkgmtime()... ");
+ logprintf(logdepth, "find_time_mkgmtime: trying to find _mkgmtime...\n");
+ logdepth++;
+
+ for (ldf = ldflags; *ldf; ++ldf)
+ if (try_icl(logdepth, "libs/time/_mkgmtime", test_c, "#include