├── tests
├── cmd
├── README.md
├── cmd.c
├── test.test
├── test-generator.py
└── test.json
├── docker
├── systemf-sleep-infinity
├── systemf-check
├── systemf-build
├── gdb-entitlement.xml
├── Dockerfile
├── systemf-gdbserv
├── docker-compose.yml
└── systemf-release-build
├── ChangeLog
├── COPYING
├── autogen.sh
├── NEWS
├── .gitignore
├── .vscode
├── settings.json
├── launch.json
└── tasks.json
├── AUTHORS
├── .github
└── workflows
│ └── c-cpp.yml
├── m4
├── ltversion.m4
├── ax_prog_bison.m4
├── ax_check_gnu_make.m4
├── ltsugar.m4
├── lt~obsolete.m4
├── ax_valgrind_check.m4
├── ax_code_coverage.m4
└── ltoptions.m4
├── src
├── systemf.h
├── close.c
├── systemf.c
├── pid-chain.c
├── systemf-internal.h
├── lexer.l
├── parser.y
├── derived-parser.h
├── file-sandbox-check.c
├── parser-support.c
├── task.c
└── derived-lexer.h
├── config.h.in
├── Makefile.am
├── DEVELOP.md
├── configure.ac
├── LICENSE
└── README.md
/tests/cmd:
--------------------------------------------------------------------------------
1 | ../cmd
--------------------------------------------------------------------------------
/docker/systemf-sleep-infinity:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | exec sleep infinity
3 |
--------------------------------------------------------------------------------
/ChangeLog:
--------------------------------------------------------------------------------
1 | All changes are tracked in github:
2 |
3 | wwwin-github.cisco.com/ASIG/systemf
4 |
--------------------------------------------------------------------------------
/docker/systemf-check:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | cd /systemf
3 | /usr/bin/systemf-build
4 | make check-code-coverage
5 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | Copyright 2019 by Cisco Systems Inc.
2 |
3 | Currently this is restricted to Cisco use only, but the goal would be
4 | to distribute this to the world.
5 |
--------------------------------------------------------------------------------
/docker/systemf-build:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | cd /systemf
3 |
4 | # autoreconf -i # Just use the current config file.
5 | ./configure CFLAGS="-g -O0" --enable-code-coverage
6 | make
7 |
--------------------------------------------------------------------------------
/docker/gdb-entitlement.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.cs.debugger
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # docker build -t meklund/systemf-gh-action:$(date +%Y-%m-%d) .
2 | # docker push meklund/systemf-gh-action
3 | FROM ubuntu
4 | RUN apt-get update
5 | RUN apt-get update
6 | RUN apt-get install -y git build-essential autotools-dev automake libtool bison flex lcov python3 gdbserver
7 | COPY systemf-build systemf-gdbserv systemf-check systemf-release-build systemf-sleep-infinity /usr/bin/
8 |
--------------------------------------------------------------------------------
/autogen.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # As seen on
3 | # Autotools A Practitioner's Guide to GNU Autoconf,
4 | # Automake, and Libtool by John Calcote
5 | #
6 | # autoreconf runs all the autotool configuration tools in the right order
7 | # and will avoid regenerating files.
8 | #
9 | autoreconf --install --make # install missing files
10 | # automake --add-missing --copy >/dev/null 2>&1 # add install-sh
11 |
--------------------------------------------------------------------------------
/docker/systemf-gdbserv:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | cd /systemf
3 | /usr/bin/systemf-build
4 |
5 | export SHOULD_NOT_FORK=true
6 | export SHOULD_NOT_CAPTURE_STDOUT_STDERR=true
7 | export LD_LIBRARY_PATH=/systemf/.libs/
8 |
9 | GDBSERVER_TARGET=${GDBSERVER_TARGET:-../.libs/test-runner}
10 | GDBSERVER_PORT=${GDBSERVER_PORT:-2345}
11 |
12 | cd tests
13 | while true ; do
14 | gdbserver :$GDBSERVER_PORT $GDBSERVER_TARGET
15 | done
16 |
--------------------------------------------------------------------------------
/NEWS:
--------------------------------------------------------------------------------
1 | Read GNU Coding Standards 6.7
2 | http://www.gnu.org/prep/standards/html_node/NEWS-File.html#NEWS-File
3 |
4 | Example: http://git.savannah.gnu.org/cgit/make.git/tree/NEWS
5 |
6 | YourProject NEWS
7 | History of user-visible changes
8 | 25 Jan 2015
9 |
10 | See the end of this file for copyrights and conditions.
11 |
12 |
13 | Version 1.0.0
14 |
15 | * New features: XXXX
16 | Description of the feature
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.lo
3 | *.la
4 | *.gcno
5 | build-aux/
6 | *~
7 | .DS_Store
8 |
9 | *.swp
10 | .libs/
11 | Makefile
12 | autom4te.cache/
13 | config.h
14 | config.log
15 | config.status
16 | libtool
17 | myexecutable
18 | .deps/
19 | .dirstamp
20 | src/derived-parser.output
21 | stamp-h1
22 | test-suite.log
23 | tests/test.log
24 | tests/test.trs
25 | test-runner
26 | test-runner.c
27 | cmd
28 | tests/tmp/
29 | systemf-coverage*
30 |
31 | systemf-*.tar.gz
32 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.tabCompletion": "onlySnippets",
3 | "editor.quickSuggestions": false,
4 | "search.exclude": {
5 | "src/derived-*": true
6 | },
7 | "files.exclude": {
8 | "src/derived-*": true
9 | },
10 | "editor.renderLineHighlight": "gutter",
11 | "files.associations": {
12 | "__bit_reference": "c",
13 | "algorithm": "c",
14 | "bitset": "c",
15 | "chrono": "c",
16 | "unordered_map": "c",
17 | "cstring": "c",
18 | "locale": "c"
19 | },
20 | "lcov.path": "./systemf-0.9-coverage.info"
21 | }
22 |
--------------------------------------------------------------------------------
/docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 | services:
3 | systemf-test:
4 | build:
5 | context: ./
6 | dockerfile: Dockerfile
7 | image: meklund/systemf-gh-action
8 | volumes:
9 | - type: bind
10 | source: ../
11 | target: /systemf
12 | environment:
13 | - GDBSERVER_PORT=${GDBSERVER_PORT:-2345}
14 | - GDBSERVER_TARGET=${GDBSERVER_TARGET:-../.libs/test-runner}
15 | - LD_LIBRARY_PATH=/systemf/.libs/
16 | cap_add:
17 | - SYS_PTRACE
18 | security_opt:
19 | - seccomp=unconfined
20 | ports:
21 | - "${GDBSERVER_LOCAL_PORT:-2345}:${GDBSERVER_PORT:-2345}"
22 | command: ["${COMMAND:-/usr/bin/systemf-sleep-infinity}"]
23 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | -------------------------------------------------------------------------------
2 | systemf code by:
3 | Mark Eklund
4 | James Spadaro
5 |
6 | -------------------------------------------------------------------------------
7 | Automake Templates are based on https://github.com/ecerulm/autotools-template
8 | The Contents of that AUTHORS file were:
9 |
10 | GNU Coding Standards 6.3: Recording Contributors
11 | http://www.gnu.org/prep/maintain/html_node/Recording-Contributors.html
12 |
13 | First version of all files by Ruben Laguna
14 |
15 | -------------------------------------------------------------------------------
16 |
17 |
--------------------------------------------------------------------------------
/.github/workflows/c-cpp.yml:
--------------------------------------------------------------------------------
1 | name: C/C++ CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 |
13 | container: meklund/systemf-gh-action:2020-06-22
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: autoreconf
18 | run: autoreconf -i
19 | - name: configure
20 | run: ./configure --enable-code-coverage
21 | - name: make
22 | run: make
23 | - name: make check-code-coverage
24 | run: make check-code-coverage
25 | - name: make print-code-coverage
26 | run: make print-code-coverage
27 | - name: show test failure log
28 | if: ${{ failure() }}
29 | run: cat tests/test.log
30 |
--------------------------------------------------------------------------------
/m4/ltversion.m4:
--------------------------------------------------------------------------------
1 | # ltversion.m4 -- version numbers -*- Autoconf -*-
2 | #
3 | # Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
4 | # Written by Scott James Remnant, 2004
5 | #
6 | # This file is free software; the Free Software Foundation gives
7 | # unlimited permission to copy and/or distribute it, with or without
8 | # modifications, as long as this notice is preserved.
9 |
10 | # @configure_input@
11 |
12 | # serial 4179 ltversion.m4
13 | # This file is part of GNU Libtool
14 |
15 | m4_define([LT_PACKAGE_VERSION], [2.4.6])
16 | m4_define([LT_PACKAGE_REVISION], [2.4.6])
17 |
18 | AC_DEFUN([LTVERSION_VERSION],
19 | [macro_version='2.4.6'
20 | macro_revision='2.4.6'
21 | _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
22 | _LT_DECL(, macro_revision, 0)
23 | ])
24 |
--------------------------------------------------------------------------------
/src/systemf.h:
--------------------------------------------------------------------------------
1 | #ifndef __systemf_h__
2 | #define __systemf_h__
3 | #include
4 |
5 | extern int systemf1(const char *fmt, ...);
6 |
7 | /*
8 | * Debug Flags used with the global systemf1_debug_set() and systemf1_debug_get()
9 | * Flags starting with SYSTEMF1_DBG_DBG_ only work if systemf is configured with --enhanced-debug
10 | */
11 | enum {
12 | SYSTEMF1_DBG_ERRORS = 0x01, // Print a debug any time a nonzero value would be returned.
13 | SYSTEMF1_DBG_EXEC = 0x02, // Print the command and arguments being launched.
14 | SYSTEMF1_DBG_DBG_LEX = 0x04,
15 | SYSTEMF1_DBG_DBG_PARSER = 0x08,
16 | };
17 | extern int systemf1_debug_set(int flags);
18 | extern int systemf1_debug_get();
19 | extern FILE *systemf1_debug_file_set(FILE *file);
20 | extern FILE *systemf1_debug_file_get();
21 |
22 | #endif /* __systemf_h__ */
23 |
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 | ## Running Tests
2 |
3 | To run tests, either:
4 | 1. `make check` to run the automated framework
5 | 2. `python3 tests/test.test` to do a manual run
6 |
7 | ## JSON File Spec
8 |
9 |
10 | test.json is a list of tests in JSON format that will run.
11 |
12 | A test is defined as a JSON object looking like this:
13 | ```
14 | {
15 | "description": "*test description*",
16 | "command": [ "*binary*", "*arg1*", "*arg2*" ],
17 | "stdout": ["*operator*", "*expected stdout including newlines*"],
18 | "stderr": null,
19 | "return_code": null
20 | }
21 | ```
22 |
23 | Note that "stdout", "stderr", and "return_code" can all be null if it doesn't matter
24 | what they are. Otherwise, they should be an array of "operator", "expected
25 | value".
26 |
27 | Operator can be: "==", "!=", ">", "<", "contains"
28 |
29 | These objects are defined in a JSON list.
30 |
31 |
--------------------------------------------------------------------------------
/src/close.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "systemf-internal.h"
3 | #include
4 | #include
5 | #include
6 |
7 | /*
8 | * Closes all other files but stdin, stdout, and stderr.
9 | *
10 | * All documentation advises that all open files should be closed
11 | * after a fork. But there is no consistent advice on how to close
12 | * these files. It is expected that this implementation will become
13 | * several different versions detecting on what operating system
14 | * ./configure detects.
15 | *
16 | * This does not close fd 0, 1, and 2 (stdin, stdout, and stderr)
17 | */
18 | void _sf1_close_upper_fd() {
19 | struct rlimit rlim;
20 | int prev_errno;
21 |
22 | // if getrlimit ever fails, we need to figure out why and then properly handle it.
23 | assert(!getrlimit(RLIMIT_NOFILE, &rlim));
24 |
25 | prev_errno = errno;
26 | for (int i = 3; i < rlim.rlim_cur; i++) {
27 | close(i);
28 | }
29 | errno = prev_errno;
30 | }
31 |
--------------------------------------------------------------------------------
/docker/systemf-release-build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | fail() {
4 | echo ERROR: "$*" >&1
5 | exit 1
6 | }
7 |
8 | cd /systemf
9 |
10 | test -z "$(git status -s | grep -v src/derived-)" || fail Checked out or untracked files found in \"git status -s\".
11 | version=$(cat configure.ac | tr '[]' ' ' | awk '/AC_INIT/ {print $4}')
12 | test -n "$version" || fail Empty version detected.
13 | git tag | grep "^V$version\$" >/dev/null && fail "$version" already exists in git. If this is a rebuild then you may \"git tag -d V$version\".
14 |
15 | set -e -x
16 | rm -f src/derived-*
17 | autoreconf -if
18 | ./configure
19 | make clean
20 | make
21 | make check
22 | set +e +x
23 |
24 | test -z "$(git status -s)" || fail Build created new code. Audit, commit if good, and rerun release.
25 |
26 | set -e -x
27 | make dist
28 | cd ..
29 | tar xzf systemf/systemf-$version.tar.gz
30 | cd systemf-$version
31 | ./configure
32 | make check
33 | make install
34 | set +e +x
35 |
36 | echo '#######'
37 | echo SUCCESS
38 | echo '#######'
39 |
40 | echo Please do this when ready: git tag -a V$version -m "$version"
41 |
42 |
--------------------------------------------------------------------------------
/src/systemf.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "derived-parser.h"
6 | #include "derived-lexer.h"
7 | #include "systemf-internal.h"
8 |
9 | int _sf1_yyerror(_SF1_YYLTYPE *locp, yyscan_t scanner, _sf1_parse_args *result, const char *msg) {
10 | fprintf(stderr, "ERROR: %d:%d:%s\n", locp->first_line, locp->first_column, msg);
11 | return 1;
12 | }
13 |
14 | int systemf1(const char *fmt, ...)
15 | {
16 | // extern int _sf1_yydebug; _sf1_yydebug = 1; // for debugging issues
17 | va_list argp;
18 | yyscan_t scanner;
19 | _sf1_parse_args *result = calloc(1, sizeof(_sf1_parse_args));
20 |
21 | va_start(argp, fmt);
22 | result->argpp = &argp;
23 |
24 | if (_sf1_yylex_init(&scanner)) {
25 | fprintf(stderr, "systemf: Unexpected failure in yylex_init().");
26 | return -1;
27 | }
28 | YY_BUFFER_STATE buf = _sf1_yy_scan_string(fmt, scanner);
29 | if (_sf1_yyparse(scanner, result)) {
30 | free(result);
31 | return -1;
32 | }
33 | va_end(argp);
34 | _sf1_yy_delete_buffer(buf, scanner);
35 | _sf1_yylex_destroy(scanner);
36 |
37 | int ret = _sf1_tasks_run(result->tasks);
38 | _sf1_task_free(result->tasks);
39 | free(result);
40 |
41 | return (ret);
42 | }
43 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | // https://code.visualstudio.com/docs/cpp/launch-json-reference
6 | "version": "0.2.0",
7 | "configurations": [
8 | {
9 | "name": "(lldb) test-runner",
10 | "type": "cppdbg",
11 | "request": "launch",
12 | "program": "${workspaceFolder}/.libs/test-runner",
13 | "args": ["30"],
14 | "environment": [
15 | {"name": "DYLD_LIBRARY_PATH", "value": "${workspaceFolder}/.libs:${env:DYLD_LIBRARY_PATH}"}],
16 | "stopAtEntry": false,
17 | "cwd": "${workspaceFolder}/tests/",
18 | "externalConsole": false,
19 | "MIMode": "lldb"
20 | },
21 | {
22 | "name": "Docker gdb",
23 | "type": "cppdbg",
24 | "request": "launch",
25 | "program": ".libs/test-runner",
26 | "miDebuggerServerAddress": "localhost:2345",
27 | "debugServerArgs": "30",
28 | "args": ["30"],
29 | "stopAtEntry": false,
30 | "cwd": "${workspaceRoot}",
31 | "environment": [],
32 | "externalConsole": false,
33 | "linux": {
34 | "MIMode": "gdb"
35 | },
36 | "osx": {
37 | "MIMode": "gdb"
38 | },
39 | "windows": {
40 | "MIMode": "gdb"
41 | }
42 | }
43 | ]
44 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "shell",
6 | "label": "Make check",
7 | "command": "/usr/bin/make",
8 | "args": [
9 | "check"
10 | ],
11 | "options": {
12 | "cwd": "${workspaceFolder}",
13 | "environment": [
14 | {
15 | "name": "CFLAGS",
16 | "value": "-gO0"
17 | }
18 | ]
19 | },
20 | "group": {
21 | "kind": "build",
22 | "isDefault": true
23 | },
24 | "problemMatcher": [
25 | "$gcc"
26 | ]
27 | },
28 | {
29 | "type": "shell",
30 | "label": "Docker check",
31 | "command": "/usr/local/bin/docker-compose",
32 | "args": [
33 | "up",
34 | "--build",
35 | "--force-recreate"
36 | ],
37 | "options": {
38 | "cwd": "${workspaceFolder}/docker",
39 | "env": {
40 | "COMMAND": "/usr/bin/systemf-check"
41 | }
42 | },
43 | "group": {
44 | "kind": "build",
45 | "isDefault": true
46 | },
47 | "problemMatcher": [
48 | "$gcc"
49 | ]
50 | },
51 | {
52 | "type": "shell",
53 | "label": "Docker gdbserver",
54 | "command": "/usr/local/bin/docker-compose",
55 | "args": [
56 | "up",
57 | "--build",
58 | "--force-recreate"
59 | ],
60 | "options": {
61 | "cwd": "${workspaceFolder}/docker",
62 | "env": {
63 | "COMMAND": "/usr/bin/systemf-gdbserv"
64 | }
65 | },
66 | "group": {
67 | "kind": "build",
68 | "isDefault": true
69 | },
70 | "problemMatcher": [
71 | "$gcc"
72 | ]
73 | }
74 | ]
75 | }
--------------------------------------------------------------------------------
/config.h.in:
--------------------------------------------------------------------------------
1 | /* config.h.in. Generated from configure.ac by autoheader. */
2 |
3 | /* Define to 1 if you have the header file. */
4 | #undef HAVE_DLFCN_H
5 |
6 | /* Define to 1 if you have the header file. */
7 | #undef HAVE_INTTYPES_H
8 |
9 | /* Define to 1 if you have the header file. */
10 | #undef HAVE_MEMORY_H
11 |
12 | /* Define to 1 if you have the header file. */
13 | #undef HAVE_STDINT_H
14 |
15 | /* Define to 1 if you have the header file. */
16 | #undef HAVE_STDLIB_H
17 |
18 | /* Define to 1 if you have the header file. */
19 | #undef HAVE_STRINGS_H
20 |
21 | /* Define to 1 if you have the header file. */
22 | #undef HAVE_STRING_H
23 |
24 | /* Define to 1 if you have the header file. */
25 | #undef HAVE_SYS_STAT_H
26 |
27 | /* Define to 1 if you have the header file. */
28 | #undef HAVE_SYS_TYPES_H
29 |
30 | /* Define to 1 if you have the header file. */
31 | #undef HAVE_UNISTD_H
32 |
33 | /* Define to the sub-directory where libtool stores uninstalled libraries. */
34 | #undef LT_OBJDIR
35 |
36 | /* myfeature is enabled */
37 | #undef MYFEATURE
38 |
39 | /* Define to 1 if assertions should be disabled. */
40 | #undef NDEBUG
41 |
42 | /* Define to 1 if your C compiler doesn't accept -c and -o together. */
43 | #undef NO_MINUS_C_MINUS_O
44 |
45 | /* Name of package */
46 | #undef PACKAGE
47 |
48 | /* Define to the address where bug reports for this package should be sent. */
49 | #undef PACKAGE_BUGREPORT
50 |
51 | /* Define to the full name of this package. */
52 | #undef PACKAGE_NAME
53 |
54 | /* Define to the full name and version of this package. */
55 | #undef PACKAGE_STRING
56 |
57 | /* Define to the one symbol short name of this package. */
58 | #undef PACKAGE_TARNAME
59 |
60 | /* Define to the home page for this package. */
61 | #undef PACKAGE_URL
62 |
63 | /* Define to the version of this package. */
64 | #undef PACKAGE_VERSION
65 |
66 | /* Define to 1 if you have the ANSI C header files. */
67 | #undef STDC_HEADERS
68 |
69 | /* Version number of package */
70 | #undef VERSION
71 |
--------------------------------------------------------------------------------
/src/pid-chain.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "systemf-internal.h"
5 |
6 | /*
7 | * _sf1_pid_chain_wait - Waits for all processes in a chain of pids to finish.
8 | *
9 | * stat_loc - The stat of the last pid in the pid_chain.
10 | *
11 | * returns - The pid of the last pid in the chain or 0 on error.
12 | */
13 | pid_t _sf1_pid_chain_waitpids(_sf1_pid_chain_t *pid_chain, int *stat_loc, int options) {
14 | // Currently this is always used in a run-to-completion, so don't support all waitpid options
15 | assert(options == 0);
16 | assert(stat_loc != NULL);
17 | pid_t pid = 0;
18 |
19 | for (int i = 0; i < pid_chain->size; i++) {
20 | pid = waitpid(pid_chain->pids[i], stat_loc, 0);
21 | // This should never fail.
22 | assert(pid == pid_chain->pids[i]);
23 | }
24 | return pid;
25 | }
26 |
27 | size_t _sf1_pid_chain_size(int capacity) {
28 | return sizeof(_sf1_pid_chain_t) + sizeof(pid_t) * capacity;
29 | }
30 |
31 | /*
32 | * Adds a pid to the pid_chain. If pid_chain is NULL, it allocates. If needs to be increased,
33 | * it reallocates. On failure, errno is set and NULL is returned.
34 | */
35 | _sf1_pid_chain_t *_sf1_pid_chain_add(_sf1_pid_chain_t *pid_chain, pid_t pid) {
36 | const int cap_steps = 4;
37 |
38 | if (!pid_chain) {
39 | pid_chain = malloc(_sf1_pid_chain_size(cap_steps));
40 | if (!pid_chain) {
41 | return NULL;
42 | }
43 | pid_chain->capacity = cap_steps;
44 | pid_chain->size = 0;
45 | }
46 |
47 | if (pid_chain->capacity == pid_chain->size) {
48 | _sf1_pid_chain_t *save = pid_chain;
49 |
50 | pid_chain->capacity += cap_steps;
51 | pid_chain = realloc(pid_chain, _sf1_pid_chain_size(pid_chain->capacity));
52 | if (!pid_chain) {
53 | free(save);
54 | return NULL;
55 | }
56 | }
57 |
58 | pid_chain->pids[pid_chain->size] = pid;
59 | pid_chain->size += 1;
60 | return pid_chain;
61 | }
62 |
63 | /*
64 | * Removes memory for the pid_chain. Always returns NULL.
65 | */
66 | void _sf1_pid_chain_free(_sf1_pid_chain_t *pid_chain) {
67 | free(pid_chain);
68 | }
69 |
70 | /*
71 | * Resets the pid_chain to zero length.
72 | */
73 | void _sf1_pid_chain_clear(_sf1_pid_chain_t *pid_chain) {
74 | pid_chain->size = 0;
75 | }
76 |
--------------------------------------------------------------------------------
/tests/cmd.c:
--------------------------------------------------------------------------------
1 | /*
2 | * cmd - Used by the test harness to have a consistent
3 | * test command with no reliance on any external files.
4 | */
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | static void cat(void) {
12 | char buf[257];
13 | while (1) {
14 | int count = read(0, buf, 256);
15 | if (count < 1) {
16 | break;
17 | }
18 | buf[count] = 0;
19 | printf("%s", buf);
20 | }
21 | }
22 |
23 | int main(int argc, char *argv[]) {
24 | int retval = 0;
25 |
26 | if (argc == 1) {
27 | printf("%s",
28 | "stdout: echo '1' to the stdout with no linefeed.\n"
29 | "stderr: echo '2' to the stderr with no linefeed.\n"
30 | "cat: cat the stdin to the stdout (does not take arguments)\n"
31 | "{}: wrap the stdin with {}\n"
32 | "incr: read the first integer from the stdin, add 1, and print to stdout.\n"
33 | "comma: read the rest of the arguments and print to the stdout comma separated.\n"
34 | "true: set the return value to 0 (the default).\n"
35 | "false: set the return value to 1.\n"
36 | "count: countinuously count from 1 to infinity to stdout with a newline.\n"
37 | "return: set the return value to the next argument\n");
38 | return retval;
39 | }
40 |
41 | for (int argi = 1; argi < argc; argi++) {
42 | if (!strcmp("stdout", argv[argi])) {
43 | printf("1");
44 | } else if (!strcmp("stderr", argv[argi])) {
45 | fprintf(stderr, "2");
46 | } else if (!strcmp("cat", argv[argi])) {
47 | cat();
48 | } else if (!strcmp("{}", argv[argi])) {
49 | printf("{");
50 | cat();
51 | printf("}");
52 | } else if (!strcmp("incr", argv[argi])) {
53 | int i = 0;
54 | if (scanf("%d", &i)) {
55 | i = i + 1;
56 | }
57 | printf("%d", i);
58 | } else if (!strcmp("comma", argv[argi])) {
59 | char *delim="";
60 | for (argi++; argi < argc; argi++) {
61 | printf("%s%s", delim, argv[argi]);
62 | delim = ",";
63 | }
64 | } else if (!strcmp("true", argv[argi])) {
65 | retval = 0;
66 | } else if (!strcmp("false", argv[argi])) {
67 | retval = 1;
68 | } else if (!strcmp("count", argv[argi])) {
69 | for (int i=1; 1; i += 1) {
70 | printf("%d\n", i);
71 | }
72 | } else if (!strcmp("return", argv[argi])) {
73 | argi++;
74 | if (argi < argc) {
75 | retval = atoi(argv[argi]);
76 | }
77 | } else {
78 | printf("\nInvalid option %s\n", argv[argi]);
79 | }
80 | }
81 | return retval;
82 | }
83 |
--------------------------------------------------------------------------------
/Makefile.am:
--------------------------------------------------------------------------------
1 | AM_CFLAGS = # CFLAGS applicable to all executables (products)
2 | AM_CPPFLAGS = -I$(top_srcdir)/src # so that tests also find header files
3 | AM_YFLAGS = -d
4 | EXTRA_DIST := README.md \
5 | tests/README.md \
6 | tests/test.test \
7 | tests/test.json \
8 | tests/test-generator.py \
9 | tests/cmd \
10 | src/parser.y \
11 | src/lexer.l
12 |
13 | lib_LTLIBRARIES = libsystemf.la
14 | libsystemf_la_SOURCES := \
15 | src/close.c \
16 | src/derived-lexer.c \
17 | src/derived-lexer.h \
18 | src/derived-parser.c \
19 | src/derived-parser.h \
20 | src/file-sandbox-check.c \
21 | src/parser-support.c \
22 | src/pid-chain.c \
23 | src/systemf.c \
24 | src/task.c \
25 | src/systemf-internal.h
26 |
27 | include_HEADERS := src/systemf.h
28 | libsystemf_la_LDFLAGS = -avoid-version -shared $(CODE_COVERAGE_LDFLAGS)
29 | libsystemf_la_CFLAGS := -D_FORTIFY_SOURCE=2 -Wall -Werror $(CODE_COVERAGE_CFLAGS)
30 |
31 | src/derived-lexer.c src/derived-lexer.h: src/lexer.l src/derived-parser.c
32 | $(LEX) --version | grep flex || (echo Flex is required ; false)
33 | flex src/lexer.l
34 |
35 | src/derived-parser.c src/derived-parser.h: src/parser.y
36 | $(BISON) --version | grep 'bison.* 3' || (echo Bison 3 is required ; false)
37 | bison -v src/parser.y
38 |
39 | # Anything ending in .test should be a runnable script that produces TAP output
40 | # Example Output, with 2 tests:
41 | # 1..2
42 | # ok 1 - message
43 | # not ok 2 - message
44 | # See http://www.gnu.org/software/automake/manual/html_node/Use-TAP-with-the-Automake-test-harness.html
45 | #
46 | # Run tests with 'make check'
47 |
48 | check_PROGRAMS = test-runner cmd
49 |
50 | test_runner_SOURCES = tests/test-runner.c
51 | test_runner_LDADD = libsystemf.la
52 |
53 | test_runner_CFLAGS = $(AM_CFLAGS)
54 |
55 | cmd_SOURCES = tests/cmd.c
56 | cmd_CFLAGS = $(AM_CFLAGS)
57 | cmd_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
58 |
59 | tests/test-runner.c: tests/test.json tests/test-generator.py
60 | python3 ./tests/test-generator.py
61 |
62 | TESTS = tests/test.test
63 | TESTS_ENVIRONMENT =
64 | TEST_EXTENSIONS = .test
65 |
66 | TEST_LOG_COMPILE = $(PYTHON)
67 | TEST_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
68 |
69 |
70 | @VALGRIND_CHECK_RULES@
71 | @CODE_COVERAGE_RULES@
72 |
73 | # See all code_coverage_* options in the Makefile or m4/ax_code_coverage.m4
74 | CODE_COVERAGE_OUTPUT_FILE = $(PACKAGE_NAME)-coverage.info
75 | CODE_COVERAGE_OUTPUT_DIRECTORY = $(PACKAGE_NAME)-coverage
76 | CODE_COVERAGE_IGNORE_PATTERN = $(abs_top_builddir)/tests/*
77 | CODE_COVERAGE_IGNORE_PATTERN += $(abs_top_builddir)/src/derived*
78 | CODE_COVERAGE_IGNORE_PATTERN += /usr/include/*
79 | CODE_COVERAGE_IGNORE_PATTERN += /usr/include/*/bits/*
80 |
81 | # Hack where there is much likely something better:
82 | print-code-coverage:
83 | @ grep -A3 Lines systemf-coverage/index.html | sed -e's/.*".//' -e 's/<.*//' | fmt -w80 | sed -e's/ /_/' -e 's, ,/,' -e 's/ / = /' -e 's/_/ /'
84 |
--------------------------------------------------------------------------------
/m4/ax_prog_bison.m4:
--------------------------------------------------------------------------------
1 | # ===========================================================================
2 | # https://www.gnu.org/software/autoconf-archive/ax_prog_bison.html
3 | # ===========================================================================
4 | #
5 | # SYNOPSIS
6 | #
7 | # AX_PROG_BISON(ACTION-IF-TRUE,ACTION-IF-FALSE)
8 | #
9 | # DESCRIPTION
10 | #
11 | # Check whether bison is the parser generator. Run ACTION-IF-TRUE if
12 | # successful, ACTION-IF-FALSE otherwise
13 | #
14 | # LICENSE
15 | #
16 | # Copyright (c) 2009 Francesco Salvestrini
17 | # Copyright (c) 2010 Diego Elio Petteno`
18 | #
19 | # This program is free software; you can redistribute it and/or modify it
20 | # under the terms of the GNU General Public License as published by the
21 | # Free Software Foundation; either version 2 of the License, or (at your
22 | # option) any later version.
23 | #
24 | # This program is distributed in the hope that it will be useful, but
25 | # WITHOUT ANY WARRANTY; without even the implied warranty of
26 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
27 | # Public License for more details.
28 | #
29 | # You should have received a copy of the GNU General Public License along
30 | # with this program. If not, see .
31 | #
32 | # As a special exception, the respective Autoconf Macro's copyright owner
33 | # gives unlimited permission to copy, distribute and modify the configure
34 | # scripts that are the output of Autoconf when processing the Macro. You
35 | # need not follow the terms of the GNU General Public License when using
36 | # or distributing such scripts, even though portions of the text of the
37 | # Macro appear in them. The GNU General Public License (GPL) does govern
38 | # all other use of the material that constitutes the Autoconf Macro.
39 | #
40 | # This special exception to the GPL applies to versions of the Autoconf
41 | # Macro released by the Autoconf Archive. When you make and distribute a
42 | # modified version of the Autoconf Macro, you may extend this special
43 | # exception to the GPL to apply to your modified version as well.
44 |
45 | #serial 10
46 |
47 | AC_DEFUN([AX_PROG_BISON], [
48 | AC_REQUIRE([AC_PROG_YACC])
49 | AC_REQUIRE([AC_PROG_EGREP])
50 |
51 | AC_CACHE_CHECK([if bison is the parser generator],[ax_cv_prog_bison],[
52 | AS_IF([$YACC --version 2>/dev/null | $EGREP -q '^bison '],
53 | [ax_cv_prog_bison=yes], [ax_cv_prog_bison=no])
54 | ])
55 | AS_IF([test "$ax_cv_prog_bison" = "yes"], [
56 | dnl replace the yacc-compatible compiler with the real bison, as
57 | dnl otherwise autoconf limits us to the POSIX yacc.
58 | dnl We also change the generated filename to the old one, so that
59 | dnl automake's ylwrap can deal with it.
60 | YACC="${YACC% -y} -o y.tab.c"
61 | ] m4_ifnblank([$1], [[$1]]),
62 | m4_ifnblank([$2], [[$2]])
63 | )
64 | ])
65 |
--------------------------------------------------------------------------------
/src/systemf-internal.h:
--------------------------------------------------------------------------------
1 | #ifndef __systemf_internal_h__
2 | #define __systemf_internal_h__
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef enum {
9 | _SF1_STDIN,
10 | _SF1_STDOUT,
11 | _SF1_SHARE, // If in use FD or out. If out, use FD of in.
12 | _SF1_STDERR,
13 | _SF1_PIPE,
14 | _SF1_FILE,
15 | } _sf1_stream;
16 |
17 | typedef enum {
18 | _SF1_RUN_ALWAYS = 0,
19 | _SF1_RUN_IF_PREV_FAILED = 1,
20 | _SF1_RUN_IF_PREV_SUCCEEDED = 2,
21 | } _sf1_run_if;
22 |
23 | typedef struct _sf1_task_arg_ {
24 | struct _sf1_task_arg_ *next;
25 | int is_glob;
26 | char *text;
27 | char *trusted_path;
28 | glob_t glob;
29 | } _sf1_task_arg;
30 |
31 | typedef struct _sf1_redirect_ {
32 | struct _sf1_redirect_ *next;
33 | _sf1_stream stream; // May only be STDIN, STDOUT, or STDERR
34 | _sf1_stream target;
35 | char *text;
36 | char *trusted_path;
37 | int append;
38 | } _sf1_redirect;
39 |
40 | typedef struct _sf1_task_ {
41 | char **argv;
42 | _sf1_run_if run_if;
43 | _sf1_redirect *redirects;
44 | _sf1_task_arg *args;
45 | struct _sf1_task_ *next;
46 | } _sf1_task;
47 |
48 | typedef struct _sf1_task_files_ {
49 | int in;
50 | int out;
51 | int err;
52 | int out_rd_pipe;
53 | } _sf1_task_files;
54 |
55 | typedef struct {
56 | _sf1_task *tasks;
57 | va_list *argpp;
58 | } _sf1_parse_args ;
59 |
60 | typedef enum {
61 | SYL_ESCAPE_GLOB=1,
62 | SYL_IS_GLOB=2,
63 | SYL_IS_FILE=4,
64 | SYL_IS_TRUSTED=8,
65 | } _sf1_syl_flags;
66 |
67 | typedef struct _sf1_syllable_ {
68 | struct _sf1_syllable_ *next;
69 | struct _sf1_syllable_ *next_word;
70 | _sf1_syl_flags flags;
71 | char text[];
72 | } _sf1_syllable;
73 |
74 | typedef struct {
75 | int size;
76 | int capacity;
77 | pid_t pids[];
78 | } _sf1_pid_chain_t;
79 |
80 | #ifndef YY_TYPEDEF_YY_SCANNER_T
81 | #define YY_TYPEDEF_YY_SCANNER_T
82 | typedef void* yyscan_t;
83 | #endif
84 |
85 | extern _sf1_redirect *_sf1_merge_redirects(_sf1_redirect *left, _sf1_redirect *right);
86 | extern _sf1_redirect *_sf1_create_redirect(_sf1_stream stream, _sf1_stream target, int append, _sf1_syllable *file_syllables);
87 | extern _sf1_task *_sf1_create_cmd(_sf1_syllable *syllables, _sf1_redirect *redirects);
88 | extern void _sf1_create_redirect_pipe (_sf1_task *left, _sf1_task *right);
89 |
90 | extern int _sf1_file_sandbox_check(char *trusted_path, char *path);
91 |
92 | extern pid_t _sf1_pid_chain_waitpids(_sf1_pid_chain_t *pid_chain, int *stat_loc, int options);
93 | extern _sf1_pid_chain_t *_sf1_pid_chain_add(_sf1_pid_chain_t *pid_chain, pid_t pid);
94 | extern void _sf1_pid_chain_free(_sf1_pid_chain_t *pid_chain);
95 | extern void _sf1_pid_chain_clear(_sf1_pid_chain_t *pid_chain);
96 |
97 | extern _sf1_task *_sf1_task_create();
98 | extern int _sf1_tasks_run(_sf1_task *task);
99 | extern _sf1_task_arg *_sf1_task_add_arg(_sf1_task *task, char *text, char *trusted_path, int is_glob);
100 | extern void _sf1_task_add_redirects(_sf1_task *task, _sf1_redirect *redirect);
101 | extern void _sf1_task_free(_sf1_task *task);
102 | extern char *_sf1_stream_name(_sf1_stream);
103 | extern void _sf1_close_upper_fd(void);
104 |
105 | #endif /* __systemf_internal_h__ */
106 |
--------------------------------------------------------------------------------
/src/lexer.l:
--------------------------------------------------------------------------------
1 | %option outfile="src/derived-lexer.c" header-file="src/derived-lexer.h"
2 | %option reentrant
3 | %option bison-bridge
4 | %option noyywrap nounput noinput
5 | %option prefix="_sf1_yy"
6 |
7 | %{
8 | #include
9 | #include "derived-parser.h"
10 |
11 | static void yy_user_action(_SF1_YYLTYPE *yylloc, const char *my_yytext)
12 | {
13 | yylloc->first_line = yylloc->last_line;
14 | yylloc->first_column = yylloc->last_column;
15 | for(int i = 0; my_yytext[i] != '\0'; i++) {
16 | if(my_yytext[i] == '\n') {
17 | yylloc->last_line++;
18 | yylloc->last_column = 0;
19 | } else {
20 | yylloc->last_column++;
21 | }
22 | }
23 | }
24 |
25 | #define YY_USER_ACTION yy_user_action(yylloc, yytext);
26 |
27 | static _sf1_syllable *syl (_sf1_parse_args *results, char *text, int flags) {
28 | size_t bufsize = strlen(text) + 1;
29 | _sf1_syllable *syl = malloc(sizeof(*syl) + bufsize);
30 | syl->flags = flags;
31 | memcpy(syl->text, text, bufsize);
32 | syl->next = NULL;
33 | syl->next_word = NULL;
34 | return syl;
35 | }
36 | static _sf1_syllable *syl_s (_sf1_parse_args *results) {
37 | return syl(results, va_arg(*results->argpp, char *), SYL_ESCAPE_GLOB);
38 | }
39 | static _sf1_syllable *syl_file (_sf1_parse_args *results) {
40 | return syl(results, va_arg(*results->argpp, char *), SYL_IS_FILE|SYL_ESCAPE_GLOB);
41 | }
42 | static _sf1_syllable *syl_glob (_sf1_parse_args *results) {
43 | return syl(results, va_arg(*results->argpp, char *), SYL_IS_FILE|SYL_IS_GLOB);
44 | }
45 | static _sf1_syllable *syl_trusted_file (_sf1_parse_args *results) {
46 | return syl(results, va_arg(*results->argpp, char *), SYL_IS_FILE|SYL_IS_TRUSTED|SYL_ESCAPE_GLOB);
47 | }
48 | static _sf1_syllable *syl_d (_sf1_parse_args *results) {
49 | char text[20];
50 | int val = va_arg(*results->argpp, int);
51 | snprintf(text, sizeof(text), "%d", val);
52 | return syl(results, text, 0);
53 | }
54 |
55 | %}
56 |
57 |
58 | %%
59 |
60 | [[:alnum:]/_.-]+ { yylval->SYLLABLE = syl(results, yytext, SYL_IS_TRUSTED); return SYLLABLE; }
61 | \[[[:alnum:]/_.-]+\] { yylval->SYLLABLE = syl(results, yytext, SYL_IS_FILE|SYL_IS_GLOB|SYL_IS_TRUSTED); return SYLLABLE; }
62 | [\*\?] { yylval->SYLLABLE = syl(results, yytext, SYL_IS_FILE|SYL_IS_GLOB|SYL_IS_TRUSTED); return SYLLABLE; }
63 | %s { yylval->SYLLABLE = syl_s(results); return SYLLABLE; }
64 | %p { yylval->SYLLABLE = syl_file(results); return SYLLABLE; }
65 | %!p { yylval->SYLLABLE = syl_trusted_file(results); return SYLLABLE; }
66 | %\*p { yylval->SYLLABLE = syl_glob(results); return SYLLABLE; }
67 | %d { yylval->SYLLABLE = syl_d(results); return SYLLABLE; }
68 | [ \t]+ { return SPACE;}
69 | [ \t]*\<[ \t]* { return LESSER; }
70 | [ \t]*2>&1 { return TWO_GREATER_AND_ONE; }
71 | [ \t]*2>[ \t]* { return TWO_GREATER; }
72 | [ \t]*&>[ \t]* { return AND_GREATER; }
73 | [ \t]*>[ \t]* { return GREATER; }
74 | [ \t]*>&2 { return GREATER_AND_TWO; }
75 | [ \t]*2>>[ \t]* { return TWO_GREATER_GREATER; }
76 | [ \t]*&>>[ \t]* { return AND_GREATER_GREATER; }
77 | [ \t]*>>[ \t]* { return GREATER_GREATER; }
78 | [ \t]*&&[ \t]* { return AND_AND; }
79 | [ \t]*\|[ \t]* { return OR; }
80 | [ \t]*\|\|[ \t]* { return OR_OR; }
81 | [ \t]*;[ \t]* { return SEMICOLON; }
82 | .|\n { return yytext[0]; }
83 |
84 | %%
85 |
86 |
--------------------------------------------------------------------------------
/src/parser.y:
--------------------------------------------------------------------------------
1 | %output "src/derived-parser.c"
2 | %defines "src/derived-parser.h"
3 | %define api.pure full
4 | %define api.prefix {_sf1_yy}
5 | %define api.value.type union
6 | %locations
7 | %lex-param { yyscan_t scanner }
8 | %lex-param { _sf1_parse_args *results }
9 | %parse-param { yyscan_t scanner }
10 | %parse-param { _sf1_parse_args *results }
11 |
12 |
13 | %code requires {
14 | #if 1 // This compiles in debug code enabled by _sf1_yydebug;
15 | #undef SYSTEMF1_YYDEBUG
16 | #define SYSTEMF1_YYDEBUG 1
17 | extern int _sf1_yydebug;
18 | #endif
19 |
20 | #include "systemf-internal.h"
21 |
22 | }
23 |
24 | %code provides
25 | {
26 | #define YYSTYPE _SF1_YYSTYPE
27 | #define YYLTYPE _SF1_YYLTYPE
28 | #define YY_DECL int _sf1_yylex(YYSTYPE * yylval_param , YYLTYPE *yylloc, yyscan_t yyscanner, _sf1_parse_args *results)
29 | extern YY_DECL;
30 |
31 | int _sf1_yyerror(_SF1_YYLTYPE *locp, yyscan_t scanner, _sf1_parse_args *results, const char *msg);
32 | }
33 |
34 | %code {
35 | /*
36 | * Put the parser code in a file that IDE's better understand, but
37 | * don't expose the statics to the world.
38 | */
39 | #include "systemf-internal.h"
40 | }
41 |
42 | %token <_sf1_syllable *> SYLLABLE
43 | %token SPACE QUOTE LESSER TWO_GREATER_AND_ONE TWO_GREATER AND_GREATER GREATER TWO_GREATER_GREATER AND_GREATER_GREATER
44 | %token GREATER_GREATER GREATER_AND_TWO AND_AND OR_OR SEMICOLON OR
45 | %type <_sf1_syllable *> syllables words
46 | %type <_sf1_task *> cmd cmds
47 | %type <_sf1_redirect *> redirect redirects
48 |
49 | %%
50 |
51 | cmds:
52 | cmd { results->tasks = $1; }
53 | | cmd SEMICOLON cmds { results->tasks = $1; $1->next = $3; $3->run_if = _SF1_RUN_ALWAYS; }
54 | | cmd OR_OR cmds { results->tasks = $1; $1->next = $3; $3->run_if = _SF1_RUN_IF_PREV_FAILED; }
55 | | cmd AND_AND cmds { results->tasks = $1; $1->next = $3; $3->run_if = _SF1_RUN_IF_PREV_SUCCEEDED; }
56 | | cmd OR cmds { results->tasks = $1; $1->next = $3; $3->run_if = _SF1_RUN_ALWAYS;
57 | _sf1_create_redirect_pipe($1, $3); }
58 |
59 | cmd:
60 | words redirects { $$ = _sf1_create_cmd($1, $2); }
61 |
62 | redirects:
63 | redirect redirects { $$ = _sf1_merge_redirects($1, $2); }
64 | | redirect { $$ = $1; }
65 | | /* empty */ { $$ = NULL; }
66 |
67 | redirect:
68 | LESSER syllables { $$ = _sf1_create_redirect(_SF1_STDIN, _SF1_FILE, 0, $2); }
69 | | GREATER syllables { $$ = _sf1_create_redirect(_SF1_STDOUT, _SF1_FILE, 0, $2); }
70 | | GREATER_GREATER syllables { $$ = _sf1_create_redirect(_SF1_STDOUT, _SF1_FILE, 1, $2); }
71 | | GREATER_AND_TWO { $$ = _sf1_create_redirect(_SF1_STDOUT, _SF1_SHARE, 0, NULL); }
72 | | TWO_GREATER_AND_ONE { $$ = _sf1_create_redirect(_SF1_STDERR, _SF1_SHARE, 0, NULL); }
73 | | TWO_GREATER syllables { $$ = _sf1_create_redirect(_SF1_STDERR, _SF1_FILE, 0, $2); }
74 | | TWO_GREATER_GREATER syllables { $$ = _sf1_create_redirect(_SF1_STDERR, _SF1_FILE, 1, $2); }
75 | | AND_GREATER syllables { $$ = _sf1_create_redirect(_SF1_STDERR, _SF1_SHARE, 0, NULL);
76 | $$->next = _sf1_create_redirect(_SF1_STDOUT, _SF1_FILE, 0, $2); }
77 | | AND_GREATER_GREATER syllables { $$ = _sf1_create_redirect(_SF1_STDERR, _SF1_SHARE, 1, NULL);
78 | $$->next = _sf1_create_redirect(_SF1_STDOUT, _SF1_FILE, 1, $2); }
79 |
80 | words:
81 | syllables { $$ = $1; }
82 | | syllables SPACE words { $1->next_word = $3; $$ = $1; }
83 | | error { YYABORT; }
84 |
85 | syllables:
86 | SYLLABLE { $$ = $1; }
87 | | SYLLABLE syllables { $1->next = $2; $$ = $1; }
88 |
89 |
90 | %%
91 |
92 |
--------------------------------------------------------------------------------
/DEVELOP.md:
--------------------------------------------------------------------------------
1 | # Developer notes
2 |
3 | This uses gnu AutoTools to generate a configure and makefile.
4 | To remove that dependency and allow building directly from a
5 | git pull, the files generated from these are checked into this
6 | repository.
7 |
8 | ## OSX installation from Scratch
9 | ```
10 | brew update
11 | brew install autoconf automake
12 | autoreconf -i
13 | ./configure && make
14 | make checks
15 | ```
16 |
17 | **Note:** There is a configure option of --enable-code-coverage,
18 | but currently the `make check` complains, `ld: library not found for -lgcov`
19 | Instead, we are calculating code coverage using the docker build with
20 | [github actions](https://github.com/yonhan3/systemf/actions).
21 |
22 | ## Ubuntu Installation from Scratch
23 |
24 | The easiest way to use ubuntu-like is to use the latest docker meklund/systemf-gh-action.
25 |
26 | If you want to build from scratch, use the files in Docker for guidence:
27 |
28 | For candidate RPMS, look at the Dockerfile:
29 | ```
30 | grep apt-get docker/Dockerfile
31 | ```
32 | For building for development, either run `docker/systemf-build` or use it as guidence.
33 |
34 | ## Code Coverage
35 |
36 | Code coverage is only currently working in the ubuntu docker
37 | container. OSX has issues. To run code coverage:
38 |
39 | ```
40 | ./configure --enable-code-coverage
41 | make check-code-coverage
42 | ```
43 | Alternatively:
44 | ```
45 | (cd docker && COMMAND=/usr/bin/systemf-check docker-compose up)
46 | ```
47 | The resulting html will be printed, but it will likely
48 | point to [here](systemf-coverage/index.html)
49 | (this is not checked into git).
50 |
51 | ## Visual Studio and GDB in OSX Docker container
52 |
53 | ### OSX Installation
54 |
55 | ```
56 | brew install gdb
57 | ```
58 | * [Follow these signing instructions.](https://sourceware.org/gdb/wiki/PermissionsDarwin)
59 | * Note that there is a ./docker/gdb-entitlement.xml for that step in the instructions.
60 |
61 | > Special thanks to [Spencer Elliott](https://medium.com/@spe_) for his blog post on [Debugging C/C++ Programs Remotely Using Visual Studio Code and gdbserver](https://medium.com/@spe_/debugging-c-c-programs-remotely-using-visual-studio-code-and-gdbserver-559d3434fb78)
62 |
63 | ## How to Create a Release
64 |
65 | Currently creating a release is a manual process. Once a consistent
66 | process is created, a github action will be created.
67 |
68 | 1. Update AC_INIT in configure.ac with the [semantic version](http://semver.org/).
69 | 2. Start the docker container with latest code:
70 | ```
71 | docker-compose -f docker/docker-compose.yml up --build -d
72 | ```
73 | 3. Run the script to do most of the work in the container:
74 | ```
75 | docker-compose -f docker/docker-compose.yml exec systemf-test systemf-release-build
76 | ```
77 | 4. Tag this release and push the tag.
78 | For example:
79 | ```
80 | git tag -a V0.9.0 -m 0.9.0
81 | git push --follow-tags
82 | ```
83 | *The last line output from step 3 gives the exact command to use.*
84 | 5. You may have to get your branch merged from a fork into master. Push your branch to cisco.
85 | ```
86 | git remote add cisco git@github.com:cisco/systemf.git
87 | git push --follow-tags cisco
88 | ```
89 | 6. Create a [github release](https://github.com/cisco/systemf/releases/new)
90 | 1. Enter your tag version in `Tag Version`.
91 | 2. For `Release title` enter a title with a format of "Release 0.9.0".
92 | 3. For the description, give a bulleted list of all major changes.
93 | 4. Attach the systemf-$version.tar.gz
94 | 5. Click 'this is a pre-release' if it isn't yet ready for general consumption.
95 | 6. `Publish release`
96 |
97 |
98 | ## Appendix A - printf options
99 |
100 | Some of these options were used as the basis for the format strings
101 | in the systemf() code. They are here for reference.
102 |
103 | | option | data type | option | data type |
104 | | ------ | --------- | ------ | --------- |
105 | | d, i, o, u, x, X | int | e, E, f, F, g, G, a A | double |
106 | | c | char | s | string |
107 | | C, S, | Not C99, "Don't use" | p | pointer |
108 | | n | count of chars written so far | m | GlibC strerror(errno) |
109 | | % | % | | |
110 |
111 |
--------------------------------------------------------------------------------
/tests/test.test:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import json
3 | import os
4 | import sys
5 | import shutil
6 | from subprocess import run, PIPE
7 |
8 | from multiprocessing import Process, SimpleQueue
9 |
10 | def get_tests() -> list:
11 | with open('test.json', 'r') as json_file:
12 | try:
13 | return json.load(json_file)
14 | except Exception as e:
15 | eprint(f'# Error loading json: {str(e)}')
16 | sys.exit(-1)
17 |
18 | def run_command(index):
19 | # Run these from the location of this file.
20 | return run(['../test-runner', str(index + 1)], stdout=PIPE, stderr=PIPE)
21 |
22 | def fixup_compare_value(compare_value):
23 | """Convert systemf() int return value to process return value
24 |
25 | systemf() returns an int, but the harness can only return a uchar.
26 | This converts the expected int to the uchar returned. This may
27 | be platform-specific and will be enhanced when differences are discovered.
28 | """
29 | if compare_value < 0:
30 | return 256 + compare_value
31 | else:
32 | return compare_value
33 |
34 | # Run a comparison operation on return_code, stdout, or stderr
35 | def do_comparison(i, operator, real_value, compare_value):
36 | # Everything we get from systemf() will be bytes
37 |
38 | if type(compare_value) is int:
39 | compare_value = fixup_compare_value(compare_value)
40 |
41 | if type(compare_value) is str:
42 | compare_value = str.encode(compare_value.replace("#", str(i+1)))
43 |
44 | if operator == '==':
45 | return real_value == compare_value
46 | elif operator == '!=':
47 | return real_value != compare_value
48 | elif operator == '>':
49 | return real_value > compare_value
50 | elif operator == '<':
51 | return real_value < compare_value
52 | elif operator == 'contains':
53 | return compare_value in real_value
54 |
55 | # Run this from within the tests directory
56 | os.chdir(os.path.dirname(sys.argv[0]))
57 | if os.path.isdir('tmp'):
58 | shutil.rmtree('tmp')
59 | os.mkdir('tmp')
60 |
61 | # Pull in the JSON test data
62 | # See readme for test data json specification
63 | tests = get_tests()
64 |
65 | # Tell the test framework how many tests we'll have
66 | print(f'1..{len(tests)}')
67 |
68 | # Go through each test
69 | for i in range(len(tests)):
70 | test = tests[i]
71 | # Test description
72 | errlogs = ''
73 | description = test['description']
74 |
75 | print()
76 | print(f'# Running test {i+1} - {description}')
77 |
78 | setup = test.get('setup')
79 | if setup:
80 | # Oh the irony of calling system here.
81 | os.system(setup.replace("#", str(i+1)))
82 |
83 | # Command to run (array)
84 | command = test['command']
85 | # Either 'null' or ['operator', 'value']
86 | test_return_code = test['return_code']
87 | # Either 'null' or ['operator', 'value']
88 | test_stdout = test['stdout']
89 | # Either 'null' or ['operator', 'value']
90 | test_stderr = test['stderr']
91 |
92 | returned = run_command(i)
93 | return_code = returned.returncode
94 | stdout = returned.stdout
95 | stderr = returned.stderr
96 |
97 | result = 'ok'
98 |
99 | if test_return_code != None and not do_comparison(i, test_return_code[0], return_code, test_return_code[1]):
100 | print(f'# Failed: {{"return_code": {json.dumps(test_return_code)}}}')
101 | result = 'not ok'
102 | if test_stdout != None and not do_comparison(i, test_stdout[0], stdout, test_stdout[1]):
103 | print(f'# Failed: {{"stdout": {json.dumps(test_stdout)}}}')
104 | result = 'not ok'
105 | if test_stderr != None and not do_comparison(i, test_stderr[0], stderr, test_stderr[1]):
106 | print(f'# Failed: {{"stderr": {json.dumps(test_stderr)}}}')
107 | result = 'not ok'
108 |
109 | if result == 'not ok':
110 | print('#')
111 | print(f'# return_code = {return_code}')
112 | for io in (("STDOUT", stdout), ("STDERR", stderr)):
113 | print('#')
114 | print(f'# {io[0]}:')
115 | for line in io[1].decode(errors="replace").split("\n"):
116 | print(f'# {line}')
117 |
118 | print(f'{result} {i+1} - {description}')
119 |
120 | print('exit status: 1')
121 |
--------------------------------------------------------------------------------
/m4/ax_check_gnu_make.m4:
--------------------------------------------------------------------------------
1 | # ===========================================================================
2 | # https://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html
3 | # ===========================================================================
4 | #
5 | # SYNOPSIS
6 | #
7 | # AX_CHECK_GNU_MAKE([run-if-true],[run-if-false])
8 | #
9 | # DESCRIPTION
10 | #
11 | # This macro searches for a GNU version of make. If a match is found:
12 | #
13 | # * The makefile variable `ifGNUmake' is set to the empty string, otherwise
14 | # it is set to "#". This is useful for including a special features in a
15 | # Makefile, which cannot be handled by other versions of make.
16 | # * The makefile variable `ifnGNUmake' is set to #, otherwise
17 | # it is set to the empty string. This is useful for including a special
18 | # features in a Makefile, which can be handled
19 | # by other versions of make or to specify else like clause.
20 | # * The variable `_cv_gnu_make_command` is set to the command to invoke
21 | # GNU make if it exists, the empty string otherwise.
22 | # * The variable `ax_cv_gnu_make_command` is set to the command to invoke
23 | # GNU make by copying `_cv_gnu_make_command`, otherwise it is unset.
24 | # * If GNU Make is found, its version is extracted from the output of
25 | # `make --version` as the last field of a record of space-separated
26 | # columns and saved into the variable `ax_check_gnu_make_version`.
27 | # * Additionally if GNU Make is found, run shell code run-if-true
28 | # else run shell code run-if-false.
29 | #
30 | # Here is an example of its use:
31 | #
32 | # Makefile.in might contain:
33 | #
34 | # # A failsafe way of putting a dependency rule into a makefile
35 | # $(DEPEND):
36 | # $(CC) -MM $(srcdir)/*.c > $(DEPEND)
37 | #
38 | # @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND)))
39 | # @ifGNUmake@ include $(DEPEND)
40 | # @ifGNUmake@ else
41 | # fallback code
42 | # @ifGNUmake@ endif
43 | #
44 | # Then configure.in would normally contain:
45 | #
46 | # AX_CHECK_GNU_MAKE()
47 | # AC_OUTPUT(Makefile)
48 | #
49 | # Then perhaps to cause gnu make to override any other make, we could do
50 | # something like this (note that GNU make always looks for GNUmakefile
51 | # first):
52 | #
53 | # if ! test x$_cv_gnu_make_command = x ; then
54 | # mv Makefile GNUmakefile
55 | # echo .DEFAULT: > Makefile ;
56 | # echo \ $_cv_gnu_make_command \$@ >> Makefile;
57 | # fi
58 | #
59 | # Then, if any (well almost any) other make is called, and GNU make also
60 | # exists, then the other make wraps the GNU make.
61 | #
62 | # LICENSE
63 | #
64 | # Copyright (c) 2008 John Darrington
65 | # Copyright (c) 2015 Enrico M. Crisostomo
66 | #
67 | # Copying and distribution of this file, with or without modification, are
68 | # permitted in any medium without royalty provided the copyright notice
69 | # and this notice are preserved. This file is offered as-is, without any
70 | # warranty.
71 |
72 | #serial 12
73 |
74 | AC_DEFUN([AX_CHECK_GNU_MAKE],dnl
75 | [AC_PROG_AWK
76 | AC_CACHE_CHECK([for GNU make],[_cv_gnu_make_command],[dnl
77 | _cv_gnu_make_command="" ;
78 | dnl Search all the common names for GNU make
79 | for a in "$MAKE" make gmake gnumake ; do
80 | if test -z "$a" ; then continue ; fi ;
81 | if "$a" --version 2> /dev/null | grep GNU 2>&1 > /dev/null ; then
82 | _cv_gnu_make_command=$a ;
83 | AX_CHECK_GNU_MAKE_HEADLINE=$("$a" --version 2> /dev/null | grep "GNU Make")
84 | ax_check_gnu_make_version=$(echo ${AX_CHECK_GNU_MAKE_HEADLINE} | ${AWK} -F " " '{ print $(NF); }')
85 | break ;
86 | fi
87 | done ;])
88 | dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise
89 | AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifGNUmake], ["#"])], [AS_VAR_SET([ifGNUmake], [""])])
90 | AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifnGNUmake], [""])], [AS_VAR_SET([ifnGNUmake], ["#"])])
91 | AS_VAR_IF([_cv_gnu_make_command], [""], [AS_UNSET(ax_cv_gnu_make_command)], [AS_VAR_SET([ax_cv_gnu_make_command], [${_cv_gnu_make_command}])])
92 | AS_VAR_IF([_cv_gnu_make_command], [""],[$2],[$1])
93 | AC_SUBST([ifGNUmake])
94 | AC_SUBST([ifnGNUmake])
95 | ])
96 |
--------------------------------------------------------------------------------
/m4/ltsugar.m4:
--------------------------------------------------------------------------------
1 | # ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
2 | #
3 | # Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
4 | # Foundation, Inc.
5 | # Written by Gary V. Vaughan, 2004
6 | #
7 | # This file is free software; the Free Software Foundation gives
8 | # unlimited permission to copy and/or distribute it, with or without
9 | # modifications, as long as this notice is preserved.
10 |
11 | # serial 6 ltsugar.m4
12 |
13 | # This is to help aclocal find these macros, as it can't see m4_define.
14 | AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
15 |
16 |
17 | # lt_join(SEP, ARG1, [ARG2...])
18 | # -----------------------------
19 | # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
20 | # associated separator.
21 | # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
22 | # versions in m4sugar had bugs.
23 | m4_define([lt_join],
24 | [m4_if([$#], [1], [],
25 | [$#], [2], [[$2]],
26 | [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
27 | m4_define([_lt_join],
28 | [m4_if([$#$2], [2], [],
29 | [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
30 |
31 |
32 | # lt_car(LIST)
33 | # lt_cdr(LIST)
34 | # ------------
35 | # Manipulate m4 lists.
36 | # These macros are necessary as long as will still need to support
37 | # Autoconf-2.59, which quotes differently.
38 | m4_define([lt_car], [[$1]])
39 | m4_define([lt_cdr],
40 | [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
41 | [$#], 1, [],
42 | [m4_dquote(m4_shift($@))])])
43 | m4_define([lt_unquote], $1)
44 |
45 |
46 | # lt_append(MACRO-NAME, STRING, [SEPARATOR])
47 | # ------------------------------------------
48 | # Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
49 | # Note that neither SEPARATOR nor STRING are expanded; they are appended
50 | # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
51 | # No SEPARATOR is output if MACRO-NAME was previously undefined (different
52 | # than defined and empty).
53 | #
54 | # This macro is needed until we can rely on Autoconf 2.62, since earlier
55 | # versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
56 | m4_define([lt_append],
57 | [m4_define([$1],
58 | m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
59 |
60 |
61 |
62 | # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
63 | # ----------------------------------------------------------
64 | # Produce a SEP delimited list of all paired combinations of elements of
65 | # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
66 | # has the form PREFIXmINFIXSUFFIXn.
67 | # Needed until we can rely on m4_combine added in Autoconf 2.62.
68 | m4_define([lt_combine],
69 | [m4_if(m4_eval([$# > 3]), [1],
70 | [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
71 | [[m4_foreach([_Lt_prefix], [$2],
72 | [m4_foreach([_Lt_suffix],
73 | ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
74 | [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
75 |
76 |
77 | # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
78 | # -----------------------------------------------------------------------
79 | # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
80 | # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
81 | m4_define([lt_if_append_uniq],
82 | [m4_ifdef([$1],
83 | [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
84 | [lt_append([$1], [$2], [$3])$4],
85 | [$5])],
86 | [lt_append([$1], [$2], [$3])$4])])
87 |
88 |
89 | # lt_dict_add(DICT, KEY, VALUE)
90 | # -----------------------------
91 | m4_define([lt_dict_add],
92 | [m4_define([$1($2)], [$3])])
93 |
94 |
95 | # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
96 | # --------------------------------------------
97 | m4_define([lt_dict_add_subkey],
98 | [m4_define([$1($2:$3)], [$4])])
99 |
100 |
101 | # lt_dict_fetch(DICT, KEY, [SUBKEY])
102 | # ----------------------------------
103 | m4_define([lt_dict_fetch],
104 | [m4_ifval([$3],
105 | m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
106 | m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
107 |
108 |
109 | # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
110 | # -----------------------------------------------------------------
111 | m4_define([lt_if_dict_fetch],
112 | [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
113 | [$5],
114 | [$6])])
115 |
116 |
117 | # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
118 | # --------------------------------------------------------------
119 | m4_define([lt_dict_filter],
120 | [m4_if([$5], [], [],
121 | [lt_join(m4_quote(m4_default([$4], [[, ]])),
122 | lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
123 | [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
124 | ])
125 |
--------------------------------------------------------------------------------
/src/derived-parser.h:
--------------------------------------------------------------------------------
1 | /* A Bison parser, made by GNU Bison 3.5.1. */
2 |
3 | /* Bison interface for Yacc-like parsers in C
4 |
5 | Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
6 | Inc.
7 |
8 | This program is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | This program is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with this program. If not, see . */
20 |
21 | /* As a special exception, you may create a larger work that contains
22 | part or all of the Bison parser skeleton and distribute that work
23 | under terms of your choice, so long as that work isn't itself a
24 | parser generator using the skeleton or a modified version thereof
25 | as a parser skeleton. Alternatively, if you modify or redistribute
26 | the parser skeleton itself, you may (at your option) remove this
27 | special exception, which will cause the skeleton and the resulting
28 | Bison output files to be licensed under the GNU General Public
29 | License without this special exception.
30 |
31 | This special exception was added by the Free Software Foundation in
32 | version 2.2 of Bison. */
33 |
34 | /* Undocumented macros, especially those whose name start with YY_,
35 | are private implementation details. Do not rely on them. */
36 |
37 | #ifndef YY__SF1_YY_SRC_DERIVED_PARSER_H_INCLUDED
38 | # define YY__SF1_YY_SRC_DERIVED_PARSER_H_INCLUDED
39 | /* Debug traces. */
40 | #ifndef _SF1_YYDEBUG
41 | # if defined YYDEBUG
42 | #if YYDEBUG
43 | # define _SF1_YYDEBUG 1
44 | # else
45 | # define _SF1_YYDEBUG 0
46 | # endif
47 | # else /* ! defined YYDEBUG */
48 | # define _SF1_YYDEBUG 0
49 | # endif /* ! defined YYDEBUG */
50 | #endif /* ! defined _SF1_YYDEBUG */
51 | #if _SF1_YYDEBUG
52 | extern int _sf1_yydebug;
53 | #endif
54 | /* "%code requires" blocks. */
55 | #line 13 "src/parser.y"
56 |
57 | #if 1 // This compiles in debug code enabled by _sf1_yydebug;
58 | #undef SYSTEMF1_YYDEBUG
59 | #define SYSTEMF1_YYDEBUG 1
60 | extern int _sf1_yydebug;
61 | #endif
62 |
63 | #include "systemf-internal.h"
64 |
65 |
66 | #line 67 "src/derived-parser.h"
67 |
68 | /* Token type. */
69 | #ifndef _SF1_YYTOKENTYPE
70 | # define _SF1_YYTOKENTYPE
71 | enum _sf1_yytokentype
72 | {
73 | SYLLABLE = 258,
74 | SPACE = 259,
75 | QUOTE = 260,
76 | LESSER = 261,
77 | TWO_GREATER_AND_ONE = 262,
78 | TWO_GREATER = 263,
79 | AND_GREATER = 264,
80 | GREATER = 265,
81 | TWO_GREATER_GREATER = 266,
82 | AND_GREATER_GREATER = 267,
83 | GREATER_GREATER = 268,
84 | GREATER_AND_TWO = 269,
85 | AND_AND = 270,
86 | OR_OR = 271,
87 | SEMICOLON = 272,
88 | OR = 273
89 | };
90 | #endif
91 |
92 | /* Value type. */
93 | #if ! defined _SF1_YYSTYPE && ! defined _SF1_YYSTYPE_IS_DECLARED
94 | union _SF1_YYSTYPE
95 | {
96 |
97 | /* redirects */
98 | _sf1_redirect * redirects;
99 | /* redirect */
100 | _sf1_redirect * redirect;
101 | /* SYLLABLE */
102 | _sf1_syllable * SYLLABLE;
103 | /* words */
104 | _sf1_syllable * words;
105 | /* syllables */
106 | _sf1_syllable * syllables;
107 | /* cmds */
108 | _sf1_task * cmds;
109 | /* cmd */
110 | _sf1_task * cmd;
111 | #line 112 "src/derived-parser.h"
112 |
113 | };
114 | typedef union _SF1_YYSTYPE _SF1_YYSTYPE;
115 | # define _SF1_YYSTYPE_IS_TRIVIAL 1
116 | # define _SF1_YYSTYPE_IS_DECLARED 1
117 | #endif
118 |
119 | /* Location type. */
120 | #if ! defined _SF1_YYLTYPE && ! defined _SF1_YYLTYPE_IS_DECLARED
121 | typedef struct _SF1_YYLTYPE _SF1_YYLTYPE;
122 | struct _SF1_YYLTYPE
123 | {
124 | int first_line;
125 | int first_column;
126 | int last_line;
127 | int last_column;
128 | };
129 | # define _SF1_YYLTYPE_IS_DECLARED 1
130 | # define _SF1_YYLTYPE_IS_TRIVIAL 1
131 | #endif
132 |
133 |
134 |
135 | int _sf1_yyparse (yyscan_t scanner, _sf1_parse_args *results);
136 | /* "%code provides" blocks. */
137 | #line 25 "src/parser.y"
138 |
139 | #define YYSTYPE _SF1_YYSTYPE
140 | #define YYLTYPE _SF1_YYLTYPE
141 | #define YY_DECL int _sf1_yylex(YYSTYPE * yylval_param , YYLTYPE *yylloc, yyscan_t yyscanner, _sf1_parse_args *results)
142 | extern YY_DECL;
143 |
144 | int _sf1_yyerror(_SF1_YYLTYPE *locp, yyscan_t scanner, _sf1_parse_args *results, const char *msg);
145 |
146 | #line 147 "src/derived-parser.h"
147 |
148 | #endif /* !YY__SF1_YY_SRC_DERIVED_PARSER_H_INCLUDED */
149 |
--------------------------------------------------------------------------------
/configure.ac:
--------------------------------------------------------------------------------
1 | # -*- Autoconf -*-
2 | # Process this file with autoconf to produce a configure script.
3 | #
4 | # From https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.69/autoconf.html:
5 | #
6 | # Files used in preparing a software package for distribution, when using just Autoconf:
7 | #
8 | # your source files --> [autoscan*] --> [configure.scan] --> configure.ac
9 | #
10 | # configure.ac --.
11 | # | .------> autoconf* -----> configure
12 | # [aclocal.m4] --+---+
13 | # | `-----> [autoheader*] --> [config.h.in]
14 | # [acsite.m4] ---'
15 | #
16 | # Makefile.in
17 | # Additionally, if you use Automake, the following additional productions come into play:
18 | #
19 | # [acinclude.m4] --.
20 | # |
21 | # [local macros] --+--> aclocal* --> aclocal.m4
22 | # |
23 | # configure.ac ----'
24 | #
25 | # configure.ac --.
26 | # +--> automake* --> Makefile.in
27 | # Makefile.am ---'
28 | # Files used in configuring a software package:
29 | #
30 | # .-------------> [config.cache]
31 | # configure* ------------+-------------> config.log
32 | # |
33 | # [config.h.in] -. v .-> [config.h] -.
34 | # +--> config.status* -+ +--> make*
35 | # Makefile.in ---' `-> Makefile ---'
36 |
37 | AC_PREREQ([2.69])
38 | AC_INIT([systemf], [1.0.0], [systemf@patnan.com])
39 | AC_CONFIG_AUX_DIR([build-aux])
40 | AM_INIT_AUTOMAKE([foreign subdir-objects]) # Does not require NEWS, COPYING, AUTHORS, ChangeLog or README
41 |
42 | # silent make https://autotools.io/automake/silent.html
43 | # silent rules enabled by default with 'yes'
44 | # disable silent runles with ./configure --disable-silent-rules
45 | AM_SILENT_RULES([yes]) # less verbose make output
46 | # AM_SILENT_RULES() # use make -s to get silent output
47 |
48 | AC_CONFIG_SRCDIR([src/systemf.c])
49 | AC_CONFIG_HEADERS([config.h]) # use config.h instead of passing -D in the command line
50 | AC_CONFIG_MACRO_DIR([m4])
51 |
52 | AC_LANG([C]) # Use C not C++
53 |
54 | # Checks for programs.
55 | AC_PROG_CC
56 |
57 | # In case that you want to check for specific versions of gcc
58 | # For example in case that you need C11 support you want to
59 | # check for gcc-4.9
60 | #AC_PROG_CC([gcc-4.9 gcc cc])
61 |
62 | AC_PROG_CC_C99 # or AC_PROG_CC_89 to force C89 mode or AC_PROG_CC_STDC to go to latest supported standard (currently C99)
63 |
64 | AC_PROG_INSTALL
65 | AC_PROG_CC_C_O # Need to have per product flags myexecutable_CFLAG
66 | AC_PROG_RANLIB # Need for to create libraries: .a
67 |
68 | # Need libtool init to make .so's
69 | LT_INIT
70 |
71 | dnl For now, use src/makefile to compile with bison and flex.
72 | dnl AX_PROG_BISON()
73 | dnl AM_PROG_LEX
74 | dnl AC_PROG_YACC
75 |
76 | # Checks for libraries.
77 |
78 | # Found libraries are automatically addded to LIBS
79 | # AC_SEARCH_LIBS([pthread_cond_wait], [pthread],[],[
80 | # AC_MSG_ERROR([You need to install pthreads library.])
81 | # ])
82 |
83 | # AC_SEARCH_LIBS([g_test_init], [glib-2.0],[],[
84 | # AC_MSG_ERROR([You need to install glib-2.0 library.])
85 | # ])
86 |
87 | # Checks for header files.
88 | AC_HEADER_ASSERT # ./configure --disable-assert to define NDEBUG
89 | AC_CHECK_HEADER([stdlib.h])
90 |
91 | # Check for C11's optional Atomic operations library
92 | # AC_CHECK_HEADER([stdatomic.h], [], [
93 | # AC_MSG_ERROR([C11 with atomic support needed.])
94 | # ])
95 |
96 | # Checks for typedefs, structures, and compiler characteristics.
97 |
98 | # Checks for library functions.
99 |
100 | # The following statement will use pkg-config --cflags --libs
101 | # to find out CFLAGS and -l options required to build a target that
102 | # it's going to link against glib2.0.
103 | # The required CFLAGS and -l options are available as DEPS_CFLAGS
104 | # and DEPS_LIBS in Makefile.am
105 | # PKG_CHECK_MODULES([DEPS], [glib-2.0 >= 2.24.1])
106 |
107 | AC_ARG_ENABLE([myfeature],
108 | AS_HELP_STRING([--disable-myfeature],[disable myfeature to get remove support for this and that]),
109 | [enable_myfeature=${enableval}],[enable_myfeature=yes])
110 |
111 | if test "x${enable_myfeature}" == "xyes"; then
112 | AC_DEFINE([MYFEATURE], 1, [myfeature is enabled])
113 | else
114 | AC_MSG_WARN([
115 | -----------------------------------------------------------------------------------
116 | Are you sure that you want to have myfeature disabled? You will lose this and that.
117 | -----------------------------------------------------------------------------------
118 | ])
119 | fi
120 |
121 | AC_CONFIG_FILES([
122 | Makefile
123 | ])
124 | AC_REQUIRE_AUX_FILE([tap-driver.sh])
125 | AX_VALGRIND_CHECK # http://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html - make check-valgrind
126 | AX_CODE_COVERAGE # http://www.gnu.org/software/autoconf-archive/ax_code_coverage.html#ax_code_coverage - make check-code-coverage generates coverage report
127 | AC_OUTPUT
128 |
--------------------------------------------------------------------------------
/tests/test-generator.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | from typing import Any
4 | import string
5 | import json
6 |
7 | template = """
8 | /*
9 | * DO NOT EDIT THIS FILE.
10 | * This files was generated by test-generator.py.
11 | * Any changes should be done there.
12 | * DO NOT EDIT THIS FILE.
13 | */
14 |
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include "../src/systemf.h"
22 |
23 | static void sigsegv_handler(int signo) {{
24 | fputs("Test aborted due to a SEGV\\n", stderr);
25 | exit(EXIT_FAILURE);
26 | }}
27 |
28 | static void sanity_check_tests_dir(void) {{
29 | const size_t bufsize = 4096;
30 | char *buf = malloc(bufsize);
31 | if (!buf) {{
32 | fprintf(stderr, "Test aborted because %zu bytes were not available.\\n", bufsize);
33 | exit(EXIT_FAILURE);
34 | }}
35 |
36 | char *cwd = getcwd(buf, bufsize);
37 | if (!cwd) {{
38 | fputs("Test aborted because current working directory could not be extracted\\n", stderr);
39 | exit(EXIT_FAILURE);
40 | }}
41 |
42 | char *tests = NULL;
43 | for (char *s = strstr(cwd, "tests"); s; s = strstr(s+5, "tests")) {{
44 | tests = s;
45 | }}
46 | if (!tests || (tests - cwd) < (strlen(cwd) - 6)) {{
47 | fputs("This command must be run in the tests directory.\\n", stderr);
48 | exit(EXIT_FAILURE);
49 | }}
50 | }}
51 |
52 | {test_funcs}
53 |
54 | {test_funcs_table}
55 |
56 | static void usage() {{
57 | puts("usage: test-runner ");
58 | puts("");
59 | puts("testnum: Test Description");
60 | {usage_tests}
61 | }}
62 |
63 | int main(int argc, const char *argv[]) {{
64 | int index = 0;
65 | if (argc == 2) {{
66 | index = atoi(argv[1]);
67 | }}
68 |
69 | if (index == 0 || index > {max_tests}) {{
70 | usage();
71 | exit(EXIT_FAILURE);
72 | }}
73 |
74 | sanity_check_tests_dir();
75 |
76 | // Set above function as signal handler for the SIGINT signal:
77 | if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) {{
78 | fputs("An error occurred while setting a signal handler.\\n", stderr);
79 | return EXIT_FAILURE;
80 | }}
81 |
82 | return test_func_table[index - 1]();
83 | }}
84 | """
85 |
86 | def eprint(s: str):
87 | print(s, file=sys.stderr)
88 |
89 | def str2func(index: int, s: str) -> str:
90 | """Creates a unique c-function name from a string.
91 | Credit: https://stackoverflow.com/questions/34544784/arbitrary-string-to-valid-python-name
92 | """
93 | VALID_NAME_CHARACTERS = string.ascii_letters + string.digits
94 | PLACEHOLDER = "_"
95 | prefix = f"test_{index + 1}_"
96 | sym = ''.join(c.lower() if c in VALID_NAME_CHARACTERS else PLACEHOLDER for c in s)
97 | return prefix + sym
98 |
99 | def cstr_escape(s: str) -> str:
100 | return s.replace("\"", "\\\"")
101 |
102 | test_func_template = """
103 | static int {test_name}() {{
104 | return systemf1({test_args});
105 | }}
106 | """
107 |
108 | def generate_test_func(index: int, test: dict) -> str:
109 | command = test['command']
110 | test_name = str2func(index, test['description'])
111 | test_args = []
112 |
113 | for arg in command:
114 | if type(arg) is str:
115 | # Replace ['#'] arguments with the string representation of this test number.
116 | arg = arg.replace("#", str(index+1))
117 | test_args.append(f'"{cstr_escape(arg)}"')
118 | elif type(arg) is int:
119 | test_args.append(f'{arg}')
120 | else:
121 | eprint(f'# Unknown arg type, aborting: {arg}')
122 | sys.exit(1)
123 | test_args = ", ".join(test_args)
124 | return test_func_template.format(test_name=test_name, test_args=test_args)
125 |
126 | def generate_test_funcs(tests: list) -> str:
127 | return "".join([generate_test_func(i, f) for i, f in enumerate(tests)])
128 |
129 | def generate_usage_tests(tests: list) -> str:
130 | result = ""
131 | for index, test in enumerate(tests):
132 | line = f"{index + 1:7}: {test['description']}"
133 | result += f' puts("{cstr_escape(line)}");\n'
134 | return result
135 |
136 | test_func_table_template = """
137 | int (*(test_func_table[]))(void) = {{
138 | {test_functions}
139 | }};
140 | """
141 |
142 | def generate_test_func_table(tests: list) -> str:
143 | test_functions = "\n".join([f' {str2func(i, t["description"])},' for i, t in enumerate(tests)])
144 | return(test_func_table_template.format(test_functions=test_functions))
145 |
146 | def generate_kwargs(tests: list) -> str:
147 | kwargs = {}
148 | kwargs["usage_tests"] = generate_usage_tests(tests)
149 | kwargs["test_funcs"] = generate_test_funcs(tests)
150 | kwargs["test_funcs_table"] = generate_test_func_table(tests)
151 | kwargs["max_tests"] = len(tests)
152 | return kwargs
153 |
154 | def get_tests() -> list:
155 | current_dir = os.path.dirname(sys.argv[0])
156 | TEST_JSON = os.path.join(current_dir, 'test.json')
157 | with open(TEST_JSON, 'r') as json_file:
158 | try:
159 | return json.load(json_file)
160 | except Exception as e:
161 | eprint(f'# Error loading json: {str(e)}')
162 | sys.exit(-1)
163 |
164 | def main() -> int:
165 | tests = get_tests()
166 | kwargs = generate_kwargs(tests)
167 |
168 | with open("tests/test-runner.c", "w") as t:
169 | t.write(template.format(**kwargs))
170 | return 0
171 |
172 | sys.exit(main())
--------------------------------------------------------------------------------
/m4/lt~obsolete.m4:
--------------------------------------------------------------------------------
1 | # lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
2 | #
3 | # Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
4 | # Foundation, Inc.
5 | # Written by Scott James Remnant, 2004.
6 | #
7 | # This file is free software; the Free Software Foundation gives
8 | # unlimited permission to copy and/or distribute it, with or without
9 | # modifications, as long as this notice is preserved.
10 |
11 | # serial 5 lt~obsolete.m4
12 |
13 | # These exist entirely to fool aclocal when bootstrapping libtool.
14 | #
15 | # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
16 | # which have later been changed to m4_define as they aren't part of the
17 | # exported API, or moved to Autoconf or Automake where they belong.
18 | #
19 | # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
20 | # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
21 | # using a macro with the same name in our local m4/libtool.m4 it'll
22 | # pull the old libtool.m4 in (it doesn't see our shiny new m4_define
23 | # and doesn't know about Autoconf macros at all.)
24 | #
25 | # So we provide this file, which has a silly filename so it's always
26 | # included after everything else. This provides aclocal with the
27 | # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
28 | # because those macros already exist, or will be overwritten later.
29 | # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
30 | #
31 | # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
32 | # Yes, that means every name once taken will need to remain here until
33 | # we give up compatibility with versions before 1.7, at which point
34 | # we need to keep only those names which we still refer to.
35 |
36 | # This is to help aclocal find these macros, as it can't see m4_define.
37 | AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
38 |
39 | m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
40 | m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
41 | m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
42 | m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
43 | m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
44 | m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
45 | m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
46 | m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
47 | m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
48 | m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
49 | m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
50 | m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
51 | m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
52 | m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
53 | m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
54 | m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
55 | m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
56 | m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
57 | m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
58 | m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
59 | m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
60 | m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
61 | m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
62 | m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
63 | m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
64 | m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
65 | m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
66 | m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
67 | m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
68 | m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
69 | m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
70 | m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
71 | m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
72 | m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
73 | m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
74 | m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
75 | m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
76 | m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
77 | m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
78 | m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
79 | m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
80 | m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
81 | m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
82 | m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
83 | m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
84 | m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
85 | m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
86 | m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
87 | m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
88 | m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
89 | m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
90 | m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
91 | m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
92 | m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
93 | m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
94 | m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
95 | m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
96 | m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
97 | m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
98 | m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
99 | m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])
100 |
--------------------------------------------------------------------------------
/src/file-sandbox-check.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | /*
8 | * The dir structure is used in walking directories. For these two cases,
9 | * these map to directory tuples: {start, end, depth}:
10 | * char *a = "/job/one" {&a[0], &a[1], 1} {&a[1], &a[5], 2} {&a[5], &a[8], 3} {&a[8], &a[8], 4}}
11 | * char *b = "job/one" {&a[0], &a[4], 1} {&a[4], &a[7], 2} {&a[7], &a[7], 3}
12 | * char *c = "/job/" {&a[0], &a[1], 1} {&a[1], &a[5], 2} {&a[5], &a[5], 3}
13 | *
14 | * In addition there is a zero {NULL, &a[0], 0} and and EOF {e, e, end+1) where e=&a[strlen(a))
15 | */
16 | struct dir {
17 | char *buffer;
18 | char *start;
19 | char *end;
20 | int depth;
21 | };
22 |
23 | static int is_dot_dot(struct dir *path) {
24 | const char *s = path->start;
25 | return ((s[0] == '.') && (s[1] == '.') && ((s[2] == '/') || (!s[2])));
26 | }
27 |
28 | static int is_one_dot(struct dir *path) {
29 | const char *s = path->start;
30 | return ((s[0] == '.') && ((s[1] == '/') || (!s[1])));
31 | }
32 |
33 | static struct dir zero_dir(char *buffer) {
34 | struct dir dir;
35 | dir.buffer = buffer;
36 | dir.start = NULL;
37 | dir.end = buffer;
38 | dir.depth = 0;
39 | return dir;
40 | }
41 |
42 | static int is_zero_dir(struct dir *dir) {
43 | return dir->start == NULL;
44 | }
45 |
46 | static int is_first_dir(struct dir *dir) {
47 | return (dir->start == dir->buffer);
48 | }
49 |
50 | static int eof_dir(struct dir *dir) {
51 | // Have we even started processing and if so, are we at the end.
52 | return (dir->start != NULL) && !dir->start[0];
53 | }
54 |
55 | static int next_dir(struct dir *dir) {
56 | // We are already at the end.
57 | if (eof_dir(dir)) {
58 | return 0;
59 | }
60 |
61 | // Special case. A leading '/' is a dir all by itself.
62 | if (is_zero_dir(dir) && dir->buffer[0] == '/') {
63 | dir->start = dir->buffer;
64 | dir->end = dir->buffer;
65 | dir->depth = 1;
66 | return 1;
67 | }
68 |
69 | // Walk over all leading '/'
70 | dir->start = dir->end + strspn(dir->end, "/");
71 |
72 | // Walk over non '/'
73 | dir->end = dir->start + strcspn(dir->start, "/");
74 |
75 | // Include the trailing '/' if it is present
76 | if (dir->end[0]) {
77 | dir->end += 1;
78 | }
79 |
80 | if (is_dot_dot(dir)) {
81 | dir->depth -= 1;
82 | } else if (!is_one_dot(dir)) {
83 | dir->depth += 1;
84 | }
85 |
86 | return 1;
87 | }
88 |
89 | static int prev_dir(struct dir *dir) {
90 | if (is_zero_dir(dir)) {
91 | return 0;
92 | }
93 |
94 | if (is_first_dir(dir)) {
95 | *dir = zero_dir(dir->buffer);
96 | return 1;
97 | }
98 |
99 | if (dir->start - 1 == dir->buffer) {
100 | // Special case where previous is the root '/'
101 | dir->start = dir->buffer;
102 | dir->end = &dir->buffer[1];
103 | return 1;
104 | }
105 |
106 | // Move dir->end and then walk over any doubled "//"
107 | for (dir->end = dir->start; dir->end[-1] == '/' && dir->end[-2] == '/'; dir->end -= 1);
108 |
109 | // Walk dir->start back
110 | for (dir->start = dir->end - 1;
111 | (dir->start[-1] != '/') && (dir->start != dir->buffer);
112 | dir->start--);
113 |
114 | if (is_dot_dot(dir)) {
115 | dir->depth += 1;
116 | } else if (!is_one_dot(dir)) {
117 | dir->depth -=1;
118 | }
119 | return 1;
120 | }
121 |
122 | static size_t path_copy(char *dest, struct dir *source) {
123 | size_t slen = source->end - source->start;
124 | memcpy(dest, source->start, slen);
125 | dest[slen] = 0;
126 | return slen;
127 | }
128 |
129 | static char *extract_simplified_path(char *path) {
130 | char *simple_path;
131 | struct dir simple_dir;
132 | struct dir cursor;
133 |
134 | simple_path = malloc(strlen(path)+1);
135 | if (!simple_path) {
136 | return NULL;
137 | }
138 | simple_dir = zero_dir(simple_path);
139 |
140 | cursor = zero_dir(path);
141 | while (next_dir(&cursor)) {
142 | if (is_one_dot(&cursor)) {
143 | continue;
144 | }
145 | if (is_dot_dot(&cursor) && !is_zero_dir(&simple_dir) && !is_dot_dot(&simple_dir)) {
146 | // This destroys the previous dir.
147 | prev_dir(&simple_dir);
148 | simple_dir.end[0] = 0;
149 | continue;
150 | }
151 |
152 | // Copy the data and move forward.
153 | path_copy(simple_dir.end, &cursor);
154 | next_dir(&simple_dir);
155 | }
156 | return (simple_path);
157 | }
158 |
159 | /*
160 | * simple_sandbox_check assumes that the trusted_path is the preamble
161 | * to the path (that is how systemf works) and it never has enough .. to
162 | * go into the trusted path".
163 | */
164 | static int simple_sandbox_check(char *trusted_path, char *path) {
165 | char *untrusted_path = path + strlen(trusted_path);
166 | struct dir up = zero_dir(untrusted_path);
167 |
168 | while (next_dir(&up)) {
169 | if (up.depth < 0) {
170 | return(EACCES);
171 | }
172 | }
173 | return 0;
174 | }
175 |
176 | /*
177 | * complex_sandbox_check converts the paths to simplified paths
178 | * and then compares those resulting paths.
179 | */
180 | static int complex_sandbox_check(char *trusted_path, char *path) {
181 | char *tp = extract_simplified_path(trusted_path);
182 | char *p = extract_simplified_path(path);
183 | int retval;
184 |
185 | if (p && tp) {
186 | size_t slen = strlen(tp) - 1;
187 | assert(tp[slen] == '/');
188 |
189 | if (strncmp(p, tp, slen)) {
190 | // No match even before the trailing slash.
191 | retval = EACCES;
192 | } else if (p[slen] && p[slen] != '/') {
193 | // EG: tp = /hope/ and p = /hopeless
194 | retval = EACCES;
195 | } else {
196 | retval = 0;
197 | }
198 | } else {
199 | retval = ENOMEM;
200 | }
201 | free(tp);
202 | free(p);
203 |
204 | return retval;
205 | }
206 |
207 | /*
208 | * Checks if the path is under the trusted_path.
209 | * Returns 0 on good and EACCES on bad or ENOMEM if unable to make check.
210 | */
211 | int _sf1_file_sandbox_check(char *trusted_path, char *path) {
212 | int retval;
213 |
214 | if (0 == simple_sandbox_check(trusted_path, path)) {
215 | retval = 0;
216 | } else if (!trusted_path[0]) {
217 | // Fail: Trusted is current directory and simple check was EACCES.
218 | retval = EACCES;
219 | } else {
220 | retval = complex_sandbox_check(trusted_path, path);
221 | }
222 |
223 | if (retval) {
224 | fprintf(stderr, "systemf: %s: sandboxing %s\n", strerror(retval), path);
225 | }
226 |
227 | return retval;
228 | }
--------------------------------------------------------------------------------
/m4/ax_valgrind_check.m4:
--------------------------------------------------------------------------------
1 | # ===========================================================================
2 | # http://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html
3 | # ===========================================================================
4 | #
5 | # SYNOPSIS
6 | #
7 | # AX_VALGRIND_CHECK()
8 | #
9 | # DESCRIPTION
10 | #
11 | # Checks whether Valgrind is present and, if so, allows running `make
12 | # check` under a variety of Valgrind tools to check for memory and
13 | # threading errors.
14 | #
15 | # Defines VALGRIND_CHECK_RULES which should be substituted in your
16 | # Makefile; and $enable_valgrind which can be used in subsequent configure
17 | # output. VALGRIND_ENABLED is defined and substituted, and corresponds to
18 | # the value of the --enable-valgrind option, which defaults to being
19 | # enabled if Valgrind is installed and disabled otherwise.
20 | #
21 | # If unit tests are written using a shell script and automake's
22 | # LOG_COMPILER system, the $(VALGRIND) variable can be used within the
23 | # shell scripts to enable Valgrind, as described here:
24 | #
25 | # https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html
26 | #
27 | # Usage example:
28 | #
29 | # configure.ac:
30 | #
31 | # AX_VALGRIND_CHECK
32 | #
33 | # Makefile.am:
34 | #
35 | # @VALGRIND_CHECK_RULES@
36 | # VALGRIND_SUPPRESSIONS_FILES = my-project.supp
37 | # EXTRA_DIST = my-project.supp
38 | #
39 | # This results in a "check-valgrind" rule being added to any Makefile.am
40 | # which includes "@VALGRIND_CHECK_RULES@" (assuming the module has been
41 | # configured with --enable-valgrind). Running `make check-valgrind` in
42 | # that directory will run the module's test suite (`make check`) once for
43 | # each of the available Valgrind tools (out of memcheck, helgrind, drd and
44 | # sgcheck), and will output results to test-suite-$toolname.log for each.
45 | # The target will succeed if there are zero errors and fail otherwise.
46 | #
47 | # The macro supports running with and without libtool.
48 | #
49 | # LICENSE
50 | #
51 | # Copyright (c) 2014, 2015 Philip Withnall
52 | #
53 | # Copying and distribution of this file, with or without modification, are
54 | # permitted in any medium without royalty provided the copyright notice
55 | # and this notice are preserved. This file is offered as-is, without any
56 | # warranty.
57 |
58 | #serial 3
59 |
60 | AC_DEFUN([AX_VALGRIND_CHECK],[
61 | dnl Check for --enable-valgrind
62 | AC_MSG_CHECKING([whether to enable Valgrind on the unit tests])
63 | AC_ARG_ENABLE([valgrind],
64 | [AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])],
65 | [enable_valgrind=$enableval],[enable_valgrind=])
66 |
67 | # Check for Valgrind.
68 | AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind])
69 |
70 | AS_IF([test "$enable_valgrind" = "yes" -a "$VALGRIND" = ""],[
71 | AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind])
72 | ])
73 | AS_IF([test "$enable_valgrind" != "no"],[enable_valgrind=yes])
74 |
75 | AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
76 | AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind])
77 | AC_MSG_RESULT([$enable_valgrind])
78 |
79 | # Check for Valgrind tools we care about.
80 | m4_define([valgrind_tool_list],[[memcheck], [helgrind], [drd], [exp-sgcheck]])
81 |
82 | AS_IF([test "$VALGRIND" != ""],[
83 | m4_foreach([vgtool],[valgrind_tool_list],[
84 | m4_define([vgtooln],AS_TR_SH(vgtool))
85 | m4_define([ax_cv_var],[ax_cv_valgrind_tool_]vgtooln)
86 | AC_CACHE_CHECK([for Valgrind tool ]vgtool,ax_cv_var,[
87 | ax_cv_var=
88 | AS_IF([`$VALGRIND --tool=vgtool --help 2&>/dev/null`],[
89 | ax_cv_var="vgtool"
90 | ])
91 | ])
92 |
93 | AC_SUBST([VALGRIND_HAVE_TOOL_]vgtooln,[$ax_cv_var])
94 | ])
95 | ])
96 |
97 | VALGRIND_CHECK_RULES='
98 | # Valgrind check
99 | #
100 | # Optional:
101 | # - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions
102 | # files to load. (Default: empty)
103 | # - VALGRIND_FLAGS: General flags to pass to all Valgrind tools.
104 | # (Default: --num-callers=30)
105 | # - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of:
106 | # memcheck, helgrind, drd, sgcheck). (Default: various)
107 |
108 | # Optional variables
109 | VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES))
110 | VALGRIND_FLAGS ?= --num-callers=30
111 | VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no
112 | VALGRIND_helgrind_FLAGS ?= --history-level=approx
113 | VALGRIND_drd_FLAGS ?=
114 | VALGRIND_sgcheck_FLAGS ?=
115 |
116 | # Internal use
117 | valgrind_tools = memcheck helgrind drd sgcheck
118 | valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools)))
119 |
120 | valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS)
121 | valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS)
122 | valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS)
123 | valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS)
124 |
125 | valgrind_quiet = $(valgrind_quiet_$(V))
126 | valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY))
127 | valgrind_quiet_0 = --quiet
128 |
129 | # Support running with and without libtool.
130 | ifneq ($(LIBTOOL),)
131 | valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute
132 | else
133 | valgrind_lt =
134 | endif
135 |
136 | # Use recursive makes in order to ignore errors during check
137 | check-valgrind:
138 | ifeq ($(VALGRIND_ENABLED),yes)
139 | -$(foreach tool,$(valgrind_tools), \
140 | $(if $(VALGRIND_HAVE_TOOL_$(tool))$(VALGRIND_HAVE_TOOL_exp_$(tool)), \
141 | $(MAKE) $(AM_MAKEFLAGS) -k check-valgrind-tool VALGRIND_TOOL=$(tool); \
142 | ) \
143 | )
144 | else
145 | @echo "Need to reconfigure with --enable-valgrind"
146 | endif
147 |
148 | # Valgrind running
149 | VALGRIND_TESTS_ENVIRONMENT = \
150 | $(TESTS_ENVIRONMENT) \
151 | env VALGRIND=$(VALGRIND) \
152 | G_SLICE=always-malloc,debug-blocks \
153 | G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly
154 |
155 | VALGRIND_LOG_COMPILER = \
156 | $(valgrind_lt) \
157 | $(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS)
158 |
159 | check-valgrind-tool:
160 | ifeq ($(VALGRIND_ENABLED),yes)
161 | $(MAKE) check-TESTS \
162 | TESTS_ENVIRONMENT="$(VALGRIND_TESTS_ENVIRONMENT)" \
163 | LOG_COMPILER="$(VALGRIND_LOG_COMPILER)" \
164 | LOG_FLAGS="$(valgrind_$(VALGRIND_TOOL)_flags)" \
165 | TEST_SUITE_LOG=test-suite-$(VALGRIND_TOOL).log
166 | else
167 | @echo "Need to reconfigure with --enable-valgrind"
168 | endif
169 |
170 | DISTCHECK_CONFIGURE_FLAGS ?=
171 | DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind
172 |
173 | MOSTLYCLEANFILES ?=
174 | MOSTLYCLEANFILES += $(valgrind_log_files)
175 |
176 | .PHONY: check-valgrind check-valgrind-tool
177 | '
178 |
179 | AC_SUBST([VALGRIND_CHECK_RULES])
180 | m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])])
181 | ])
182 |
--------------------------------------------------------------------------------
/src/parser-support.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "systemf-internal.h"
12 |
13 | #define DEBUG 0
14 | #define VA_ARGS(...) , ##__VA_ARGS__
15 | #define DBG(fmt, ...) if (DEBUG) { printf("%s:%-3d:%24s: " fmt "\n", __FILE__, __LINE__, __FUNCTION__ VA_ARGS(__VA_ARGS__)); }
16 |
17 | void _sf1_merge_and_free_syllables (_sf1_syllable *syl, char **text_pp, char **trusted_path_pp, int *is_glob_p) {
18 | int is_glob = 0;
19 | int is_file = 0;
20 | int is_trusted = 1;
21 | size_t escapes = 0;
22 | size_t slen = 0;
23 | size_t sandbox_len = 0;
24 | size_t sandbox_candidate = 0;
25 | size_t sandbox_index = 0;
26 | int doing_sandbox_detection = 1;
27 | const char glob_chars[] = "?*[]"; // Globs supported by glob()
28 | char *text, *cursor, *trusted_path;
29 |
30 | /*
31 | * First, walk all the syllables. Determine the following of the merged argument:
32 | * is_glob: Glob processing should be run because one syllable is a glob.
33 | * is_file: Filename sandboxing candidate was detected.
34 | * is_trusted: No syllables are not trusted. (%s, %p, %*p, %i)
35 | * escapes: Number of globlike characters that need escaping if is_glob is detected.
36 | * slen: String length of all syllables concatenated (not including escapes)
37 | * sandbox_len: Longest span of trusted bytes ending with '/'
38 | */
39 | DBG("begin")
40 | for (_sf1_syllable *s = syl; s != NULL; s = s->next)
41 | {
42 | int syl_is_glob = s->flags & SYL_IS_GLOB;
43 | int syl_escape_glob = s->flags & SYL_ESCAPE_GLOB;
44 | int syl_is_file = s->flags & SYL_IS_FILE;
45 | int syl_is_trusted = s->flags & SYL_IS_TRUSTED;
46 |
47 | DBG("SYL - %-8s ig %d, eg %d, if %d, it %d",
48 | s->text, syl_is_glob, syl_escape_glob, syl_is_file, syl_is_trusted);
49 |
50 | if (syl_escape_glob) {
51 | int i;
52 |
53 | // look for glob characters and if detected, count how many need to be escaped.
54 | for (i = strcspn(s->text, glob_chars); s->text[i]; i += strcspn(s->text + i, glob_chars)) {
55 | escapes += 1;
56 | i += 1;
57 | }
58 | slen += i;
59 | } else {
60 | slen += strlen(s->text);
61 | }
62 |
63 | // Scan for fmt string glob patterns and set as globbed file path if detected
64 | if (syl_is_file) {
65 | is_file = 1;
66 | }
67 | if (syl_is_glob) {
68 | is_glob = 1;
69 | }
70 | if (!syl_is_trusted) {
71 | is_trusted = 0;
72 | }
73 |
74 | if (doing_sandbox_detection) {
75 | if (syl_is_trusted && !syl_is_glob) {
76 | int i;
77 | char *cursor = s->text;
78 | // search for directory separators adding spans including them as we go
79 | for (cursor = s->text; *cursor; cursor += i) {
80 | i = strcspn(cursor, "/");
81 |
82 | if (cursor[i]) {
83 | i += 1;
84 | sandbox_len += sandbox_candidate + i;
85 | sandbox_candidate = 0;
86 | } else {
87 | sandbox_candidate += i;
88 | }
89 | }
90 | } else {
91 | doing_sandbox_detection = 0;
92 | }
93 | }
94 | }
95 |
96 | DBG("ig %d, if %d, it %d, es %lu, sl %lu, snl %lu, snc %lu, sni %lu, dsd %d",
97 | is_glob, is_file, is_trusted, escapes, slen, sandbox_len, sandbox_candidate,
98 | sandbox_index & 0, doing_sandbox_detection);
99 |
100 | if (is_glob) {
101 | slen += escapes;
102 | }
103 |
104 | // Allocate memory for the trusted path.
105 | if (is_file && !is_trusted) {
106 | if (sandbox_len) {
107 | trusted_path = malloc(sandbox_len + 1);
108 | trusted_path[sandbox_len] = 0;
109 | } else {
110 | trusted_path = strdup("");
111 | }
112 | // FIXME: handle NULL path
113 | } else {
114 | sandbox_len = 0;
115 | trusted_path = NULL;
116 | }
117 | text = malloc(slen + 1);
118 | cursor = text;
119 |
120 | /*
121 | * Now walk the syllables.
122 | * * Filling in the sandbox_path if needed.
123 | * * Filling in text (with escaped glob patterns when needed).
124 | */
125 | for (_sf1_syllable *s = syl; s != NULL;)
126 | {
127 | int syl_escape_glob = s->flags & SYL_ESCAPE_GLOB;
128 | _sf1_syllable *save_next;
129 |
130 | for (int i = 0; (sandbox_index < sandbox_len) && s->text[i]; i+=1, sandbox_index +=1) {
131 | trusted_path[sandbox_index] = s->text[i];
132 | }
133 |
134 | if (is_glob && syl_escape_glob) {
135 | // look for glob characters and if detected, count how many need to be escaped.
136 | char *start = s->text;
137 | while (*start) {
138 | int span = strcspn(start, glob_chars);
139 | memcpy(cursor, start, span);
140 | cursor += span;
141 | start += span;
142 | if (*start) {
143 | cursor[0] = '\\';
144 | cursor[1] = *start;
145 | start += 1;
146 | cursor += 2;
147 | }
148 | }
149 | } else {
150 | cursor = stpcpy(cursor, s->text);
151 | }
152 | save_next = s->next;
153 | free(s);
154 | s = save_next;
155 | cursor[0] = 0;
156 | }
157 | cursor[0] = 0;
158 | assert(cursor == text + slen);
159 |
160 | *text_pp = text;
161 | *trusted_path_pp = trusted_path;
162 | *is_glob_p = is_glob;
163 | DBG("end: trusted_path=%s", trusted_path)
164 | return;
165 | }
166 |
167 |
168 | _sf1_redirect *_sf1_merge_redirects (_sf1_redirect *left, _sf1_redirect *right) {
169 | _sf1_redirect *cursor;
170 | for (cursor = left; cursor->next; cursor = cursor->next);
171 | cursor->next = right;
172 | return left;
173 | }
174 |
175 | _sf1_redirect *_sf1_create_redirect(_sf1_stream stream, _sf1_stream target, int append, _sf1_syllable *file_syllables)
176 | {
177 | _sf1_redirect *redirect = calloc(1, sizeof(*redirect));
178 | // FIXME: Handle malloc error
179 | redirect->stream = stream;
180 | redirect->target = target;
181 | redirect->append = append;
182 | DBG("begin: stream %d, target %d, append %d, file %p", stream, target, append, file_syllables);
183 | if (file_syllables) {
184 | int is_glob;
185 |
186 | _sf1_merge_and_free_syllables(file_syllables, &redirect->text, &redirect->trusted_path, &is_glob);
187 | // FIXME: This needs to be cleanly handled similar to a syntax error.
188 | // Currently we don't support globs in file targets.
189 | assert(!is_glob);
190 | }
191 | DBG("end");
192 |
193 | return redirect;
194 | }
195 |
196 | static void append_redirect(_sf1_task *task, _sf1_redirect *redirect) {
197 | _sf1_redirect **next_pp = &(task->redirects);
198 | while (*next_pp) {
199 | next_pp = &((*next_pp)->next);
200 | }
201 | *next_pp = redirect;
202 | }
203 |
204 | void _sf1_create_redirect_pipe (_sf1_task *left, _sf1_task *right) {
205 | append_redirect(left, _sf1_create_redirect(_SF1_STDOUT, _SF1_PIPE, 0, NULL));
206 | append_redirect(right, _sf1_create_redirect(_SF1_STDIN, _SF1_PIPE, 0, NULL));
207 | }
208 |
209 | _sf1_task *_sf1_create_cmd (_sf1_syllable *syllables, _sf1_redirect *redirects) {
210 | int is_glob;
211 | char *text;
212 | char *trusted_path;
213 | _sf1_syllable *next;
214 | _sf1_task *task;
215 |
216 | task = _sf1_task_create();
217 |
218 | while (syllables) {
219 | next = syllables->next_word;
220 | _sf1_merge_and_free_syllables(syllables, &text, &trusted_path, &is_glob);
221 | _sf1_task_add_arg(task, text, trusted_path, is_glob);
222 | syllables = next;
223 | }
224 |
225 | _sf1_task_add_redirects(task, redirects);
226 |
227 | return task;
228 | }
229 |
--------------------------------------------------------------------------------
/m4/ax_code_coverage.m4:
--------------------------------------------------------------------------------
1 | # ===========================================================================
2 | # http://www.gnu.org/software/autoconf-archive/ax_code_coverage.html
3 | # ===========================================================================
4 | #
5 | # SYNOPSIS
6 | #
7 | # AX_CODE_COVERAGE()
8 | #
9 | # DESCRIPTION
10 | #
11 | # Defines CODE_COVERAGE_CFLAGS and CODE_COVERAGE_LDFLAGS which should be
12 | # included in the CFLAGS and LIBS/LDFLAGS variables of every build target
13 | # (program or library) which should be built with code coverage support.
14 | # Also defines CODE_COVERAGE_RULES which should be substituted in your
15 | # Makefile; and $enable_code_coverage which can be used in subsequent
16 | # configure output. CODE_COVERAGE_ENABLED is defined and substituted, and
17 | # corresponds to the value of the --enable-code-coverage option, which
18 | # defaults to being disabled.
19 | #
20 | # Test also for gcov program and create GCOV variable that could be
21 | # substituted.
22 | #
23 | # Note that all optimisation flags in CFLAGS must be disabled when code
24 | # coverage is enabled.
25 | #
26 | # Usage example:
27 | #
28 | # configure.ac:
29 | #
30 | # AX_CODE_COVERAGE
31 | #
32 | # Makefile.am:
33 | #
34 | # @CODE_COVERAGE_RULES@
35 | # my_program_LIBS = ... $(CODE_COVERAGE_LDFLAGS) ...
36 | # my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ...
37 | #
38 | # This results in a "check-code-coverage" rule being added to any
39 | # Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module
40 | # has been configured with --enable-code-coverage). Running `make
41 | # check-code-coverage` in that directory will run the module's test suite
42 | # (`make check`) and build a code coverage report detailing the code which
43 | # was touched, then print the URI for the report.
44 | #
45 | # This code was derived from Makefile.decl in GLib, originally licenced
46 | # under LGPLv2.1+.
47 | #
48 | # LICENSE
49 | #
50 | # Copyright (c) 2012 Philip Withnall
51 | # Copyright (c) 2012 Xan Lopez
52 | # Copyright (c) 2012 Christian Persch
53 | # Copyright (c) 2012 Paolo Borelli
54 | # Copyright (c) 2012 Dan Winship
55 | # Copyright (c) 2015 Bastien ROUCARIES
56 | #
57 | # This library is free software; you can redistribute it and/or modify it
58 | # under the terms of the GNU Lesser General Public License as published by
59 | # the Free Software Foundation; either version 2.1 of the License, or (at
60 | # your option) any later version.
61 | #
62 | # This library is distributed in the hope that it will be useful, but
63 | # WITHOUT ANY WARRANTY; without even the implied warranty of
64 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
65 | # General Public License for more details.
66 | #
67 | # You should have received a copy of the GNU Lesser General Public License
68 | # along with this program. If not, see .
69 |
70 | #serial 5
71 |
72 | AC_DEFUN([AX_CODE_COVERAGE],[
73 | dnl Check for --enable-code-coverage
74 | AC_REQUIRE([AC_PROG_SED])
75 |
76 | # allow to override gcov location
77 | AC_ARG_WITH([gcov],
78 | [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])],
79 | [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov],
80 | [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov])
81 |
82 | AC_MSG_CHECKING([whether to build with code coverage support])
83 | AC_ARG_ENABLE([code-coverage],
84 | AS_HELP_STRING([--enable-code-coverage],
85 | [Whether to enable code coverage support]),,
86 | enable_code_coverage=no)
87 |
88 | AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes])
89 | AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage])
90 | AC_MSG_RESULT($enable_code_coverage)
91 |
92 | AS_IF([ test "$enable_code_coverage" = "yes" ], [
93 | # check for gcov
94 | AC_CHECK_TOOL([GCOV],
95 | [$_AX_CODE_COVERAGE_GCOV_PROG_WITH],
96 | [:])
97 | AS_IF([test "X$GCOV" = "X:"],
98 | [AC_MSG_ERROR([gcov is needed to do coverage])])
99 | AC_SUBST([GCOV])
100 |
101 | dnl Check if gcc is being used
102 | AS_IF([ test "$GCC" = "no" ], [
103 | AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage])
104 | ])
105 |
106 | # List of supported lcov versions.
107 | lcov_version_list="1.6 1.7 1.8 1.9 1.10 1.11 1.14"
108 |
109 | AC_CHECK_PROG([LCOV], [lcov], [lcov])
110 | AC_CHECK_PROG([GENHTML], [genhtml], [genhtml])
111 |
112 | AS_IF([ test "$LCOV" ], [
113 | AC_CACHE_CHECK([for lcov version], ax_cv_lcov_version, [
114 | ax_cv_lcov_version=invalid
115 | lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'`
116 | for lcov_check_version in $lcov_version_list; do
117 | if test "$lcov_version" = "$lcov_check_version"; then
118 | ax_cv_lcov_version="$lcov_check_version (ok)"
119 | fi
120 | done
121 | ])
122 | ], [
123 | lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list"
124 | AC_MSG_ERROR([$lcov_msg])
125 | ])
126 |
127 | case $ax_cv_lcov_version in
128 | ""|invalid[)]
129 | lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)."
130 | AC_MSG_ERROR([$lcov_msg])
131 | LCOV="exit 0;"
132 | ;;
133 | esac
134 |
135 | AS_IF([ test -z "$GENHTML" ], [
136 | AC_MSG_ERROR([Could not find genhtml from the lcov package])
137 | ])
138 |
139 | dnl Build the code coverage flags
140 | CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
141 | CODE_COVERAGE_LDFLAGS="-lgcov"
142 |
143 | AC_SUBST([CODE_COVERAGE_CFLAGS])
144 | AC_SUBST([CODE_COVERAGE_LDFLAGS])
145 | ])
146 |
147 | CODE_COVERAGE_RULES='
148 | # Code coverage
149 | #
150 | # Optional:
151 | # - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting.
152 | # (Default: $(top_builddir))
153 | # - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated
154 | # by lcov for code coverage. (Default:
155 | # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info)
156 | # - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage
157 | # reports to be created. (Default:
158 | # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage)
159 | # - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov
160 | # - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the lcov instance.
161 | # (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
162 | # - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the lcov instance.
163 | # (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
164 | # - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml
165 | # instance. (Default: empty)
166 | # - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore
167 | #
168 | # The generated report will be titled using the $(PACKAGE_NAME) and
169 | # $(PACKAGE_VERSION). In order to add the current git hash to the title,
170 | # use the git-version-gen script, available online.
171 |
172 | # Optional variables
173 | CODE_COVERAGE_DIRECTORY ?= $(top_builddir)
174 | CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info
175 | CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage
176 | CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)"
177 | CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
178 | CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
179 | CODE_COVERAGE_GENHTML_OPTIONS ?=
180 | CODE_COVERAGE_IGNORE_PATTERN ?=
181 |
182 | code_coverage_quiet = $(code_coverage_quiet_$(V))
183 | code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY))
184 | code_coverage_quiet_0 = --quiet
185 |
186 | # Use recursive makes in order to ignore errors during check
187 | check-code-coverage:
188 | ifeq ($(CODE_COVERAGE_ENABLED),yes)
189 | -$(MAKE) $(AM_MAKEFLAGS) -k check
190 | $(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
191 | else
192 | @echo "Need to reconfigure with --enable-code-coverage"
193 | endif
194 |
195 | # Capture code coverage data
196 | code-coverage-capture: code-coverage-capture-hook
197 | ifeq ($(CODE_COVERAGE_ENABLED),yes)
198 | $(LCOV) $(code_coverage_quiet) --directory $(CODE_COVERAGE_DIRECTORY) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_OPTIONS)
199 | $(LCOV) $(code_coverage_quiet) --directory $(CODE_COVERAGE_DIRECTORY) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)"
200 | -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp
201 | LANG=C $(GENHTML) $(code_coverage_quiet) --prefix $(CODE_COVERAGE_DIRECTORY) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS)
202 | @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html"
203 | else
204 | @echo "Need to reconfigure with --enable-code-coverage"
205 | endif
206 |
207 | # Hook rule executed before code-coverage-capture, overridable by the user
208 | code-coverage-capture-hook:
209 |
210 | ifeq ($(CODE_COVERAGE_ENABLED),yes)
211 | clean: code-coverage-clean
212 | code-coverage-clean:
213 | -$(LCOV) --directory $(top_builddir) -z
214 | -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY)
215 | -find . -name "*.gcda" -o -name "*.gcov" -delete
216 | endif
217 |
218 | GITIGNOREFILES ?=
219 | GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
220 |
221 | DISTCHECK_CONFIGURE_FLAGS ?=
222 | DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
223 |
224 | .PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean
225 | '
226 |
227 | AC_SUBST([CODE_COVERAGE_RULES])
228 | m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])])
229 | ])
230 |
--------------------------------------------------------------------------------
/tests/test.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "description": "Test simple command",
4 | "command": [ "./cmd stdout stderr return 20" ],
5 | "stdout": ["==", "1"],
6 | "stderr": ["==", "2"],
7 | "return_code": ["==", 20]
8 | },
9 | {
10 | "description": "Test a non-existant binary",
11 | "command": [ "binarydoesntexist" ],
12 | "stdout": null,
13 | "stderr": null,
14 | "return_code": ["!=", 0]
15 | },
16 | {
17 | "description": "%s",
18 | "command": [ "./cmd %s", "stdout" ],
19 | "stdout": ["==", "1"],
20 | "stderr": ["==", ""],
21 | "return_code": ["==", 0]
22 | },
23 | {
24 | "description": "%d",
25 | "command": [ "./cmd comma %d %d %d%d", 1, 2, 3, 4 ],
26 | "stdout": ["==", "1,2,34"],
27 | "stderr": ["==", ""],
28 | "return_code": ["==", 0]
29 | },
30 | {
31 | "description": "\\n is not allowed",
32 | "command": [ "./cmd true\\n./cmd true" ],
33 | "stdout": ["==", ""],
34 | "stderr": ["contains", "ERROR: 1:11:syntax error"],
35 | "return_code": ["==", -1]
36 | },
37 | {
38 | "description": "Test 'ls' without absolute path (Check PATH functionality) # TODO",
39 | "command": [ "ls" ],
40 | "stdout": null,
41 | "stderr": null,
42 | "return_code": ["==", 0]
43 | },
44 | {
45 | "description": "fmt glob '*'",
46 | "command": [ "./cmd comma REA*ME.md" ],
47 | "stdout": ["==", "README.md"],
48 | "stderr": ["==", ""],
49 | "return_code": ["==", 0]
50 | },
51 | {
52 | "description": "fmt glob '?'",
53 | "command": [ "./cmd comma REA?ME.md" ],
54 | "stdout": ["==", "README.md"],
55 | "stderr": ["==", ""],
56 | "return_code": ["==", 0]
57 | },
58 | {
59 | "description": "param '%p'",
60 | "command": [ "./cmd comma %p", "bubba" ],
61 | "stdout": ["==", "bubba"],
62 | "stderr": ["==", ""],
63 | "return_code": ["==", 0]
64 | },
65 | {
66 | "description": "param '%*p'",
67 | "setup": "cd tmp; mkdir #; touch #/'XYZOOc{3,4}'",
68 | "command": [ "./cmd comma tmp/#/%*p", "?Y*[abcd]{3,4}" ],
69 | "stdout": ["==", "tmp/#/XYZOOc{3,4}"],
70 | "stderr": ["==", ""],
71 | "return_code": ["==", 0]
72 | },
73 | {
74 | "description": "param '%p' doesn't match",
75 | "setup": "cd tmp; mkdir #; touch #/'XYZOOc{3,4}'",
76 | "command": [ "./cmd comma tmp/#/%p", "?Y*[abcd]{3,4}" ],
77 | "stdout": ["==", "tmp/#/?Y*[abcd]{3,4}"],
78 | "stderr": ["==", ""],
79 | "return_code": ["==", 0]
80 | },
81 | {
82 | "description": "param '%p' glob escape success",
83 | "setup": "cd tmp; mkdir #; touch #/'XY*?[abcd]{3,4}'",
84 | "command": [ "./cmd comma tmp/#/?%p", "Y*?[abcd]{3,4}" ],
85 | "stdout": ["==", "tmp/#/XY*?[abcd]{3,4}"],
86 | "stderr": ["==", ""],
87 | "return_code": ["==", 0]
88 | },
89 | {
90 | "description": "param '%!p'",
91 | "command": [ "./cmd comma %!p", "../bubba" ],
92 | "stdout": ["==", "../bubba"],
93 | "stderr": ["==", ""],
94 | "return_code": ["==", 0]
95 | },
96 | {
97 | "description": "param '%*p' with no match",
98 | "command": [ "./cmd comma %*p", "/etc/passw?thisdoesnnotexist" ],
99 | "stdout": ["==", ""],
100 | "stderr": ["==", "systemf: no matches found: /etc/passw?thisdoesnnotexist\n"],
101 | "return_code": ["==", -1]
102 | },
103 | {
104 | "description": "Bad Redirects - (multiple stdin)",
105 | "command": [ "./cmd true &1 2>/dev/null" ],
113 | "stdout": null,
114 | "stderr": ["contains", "ERROR: There should only be one stderr per command."],
115 | "return_code": ["==", -1]
116 | },
117 | {
118 | "description": "Bad Redirects - (multiple stderr 2)",
119 | "command": [ "./cmd true 2>/dev/null 2>/tmp" ],
120 | "stdout": null,
121 | "stderr": ["contains", "ERROR: There should only be one stderr per command."],
122 | "return_code": ["==", -1]
123 | },
124 | {
125 | "description": "Bad Redirects - (stderr + stdout append + stdout)",
126 | "command": [ "./cmd true &>>/dev/null >/tmp" ],
127 | "stdout": null,
128 | "stderr": ["contains", "ERROR: There should only be one stdout per command."],
129 | "return_code": ["==", -1]
130 | },
131 | {
132 | "description": "Bad Redirects - (stderr + 2 stdout",
133 | "command": [ "./cmd true &>/dev/null >/tmp" ],
134 | "stdout": null,
135 | "stderr": ["contains", "ERROR: There should only be one stdout per command."],
136 | "return_code": ["==", -1]
137 | },
138 | {
139 | "description": "Bad Redirects - (stdout append + stdout)",
140 | "command": [ "./cmd >>/dev/null >/tmp" ],
141 | "stdout": null,
142 | "stderr": ["contains", "ERROR: There should only be one stdout per command."],
143 | "return_code": ["==", -1]
144 | },
145 | {
146 | "description": "'$' is illegal in fmt",
147 | "command": [ "./cmd comma $HOME" ],
148 | "stdout": ["==", ""],
149 | "stderr": ["contains", "ERROR: 1:13:syntax error"],
150 | "return_code": ["==", -1]
151 | },
152 | {
153 | "description": "pipe '|'",
154 | "command": [ "./cmd stdout | ./cmd incr | ./cmd incr" ],
155 | "stdout": ["==", "3"],
156 | "stderr": null,
157 | "return_code": ["==", 0]
158 | },
159 | {
160 | "description": "redirect '<'",
161 | "command": [ "./cmd cat < README.md" ],
162 | "stdout": ["contains", "JSON"],
163 | "stderr": null,
164 | "return_code": ["==", 0]
165 | },
166 | {
167 | "description": "redirect '>'",
168 | "command": [ "./cmd stdout stderr > tmp/#.txt && ./cmd incr < tmp/#.txt" ],
169 | "stdout": ["==", "2"],
170 | "stderr": ["==", "2"],
171 | "return_code": ["==", 0]
172 | },
173 | {
174 | "description": "redirect '>>'",
175 | "command": [ "./cmd stdout >> tmp/#.txt && ./cmd stdout >> tmp/#.txt && ./cmd incr < tmp/#.txt" ],
176 | "stdout": ["==", "12"],
177 | "stderr": ["==", ""],
178 | "return_code": ["==", 0]
179 | },
180 | {
181 | "description": "redirect '2>'",
182 | "command": [ "./cmd stdout >> tmp/#.txt && ./cmd stdout >> tmp/#.txt && ./cmd incr < tmp/#.txt" ],
183 | "stdout": ["==", "12"],
184 | "stderr": ["==", ""],
185 | "return_code": ["==", 0]
186 | },
187 | {
188 | "description": "redirect '2>>'",
189 | "command": [ "./cmd stdout stderr 2>> tmp/#.txt && ./cmd stdout stderr 2>> tmp/#.txt && ./cmd incr < tmp/#.txt" ],
190 | "stdout": ["==", "1123"],
191 | "stderr": ["==", ""],
192 | "return_code": ["==", 0]
193 | },
194 | {
195 | "description": "redirect '>&2'",
196 | "command": [ "./cmd stderr stderr stdout >&2" ],
197 | "stdout": ["==", ""],
198 | "stderr": ["==", "221"],
199 | "return_code": ["==", 0]
200 | },
201 | {
202 | "description": "redirect '2>&1'",
203 | "command": [ "./cmd stderr stdout stdout 2>&1" ],
204 | "stdout": ["==", "211"],
205 | "stderr": ["==", ""],
206 | "return_code": ["==", 0]
207 | },
208 | {
209 | "description": "redirect '&>'",
210 | "command": [ "./cmd stderr stdout stdout &> tmp/#.txt && ./cmd incr < tmp/#.txt" ],
211 | "stdout": ["==", "212"],
212 | "stderr": ["==", ""],
213 | "return_code": ["==", 0]
214 | },
215 | {
216 | "description": "redirect '&>>`*file*'",
217 | "command": [ "./cmd stderr stdout &>> tmp/#.txt && ./cmd stderr stdout &>> tmp/#.txt && ./cmd incr < tmp/#.txt" ],
218 | "stdout": ["==", "2122"],
219 | "stderr": ["==", ""],
220 | "return_code": ["==", 0]
221 | },
222 | {
223 | "description": "globs fmt *",
224 | "setup": "cd tmp; mkdir #; touch #/aa #/ab #/ac",
225 | "command": [ "./cmd comma tmp/#/a*"],
226 | "stdout": ["==", "tmp/#/aa,tmp/#/ab,tmp/#/ac"],
227 | "stderr": ["==", ""],
228 | "return_code": ["==", 0]
229 | },
230 | {
231 | "description": "globs fmt [abc]",
232 | "setup": "cd tmp; mkdir #; touch #/aa #/ab #/ac",
233 | "command": [ "./cmd comma tmp/#/a[abc]"],
234 | "stdout": ["==", "tmp/#/aa,tmp/#/ab,tmp/#/ac"],
235 | "stderr": ["==", ""],
236 | "return_code": ["==", 0]
237 | },
238 | {
239 | "description": "globs fmt ?",
240 | "setup": "cd tmp; mkdir #; touch #/aa #/ab #/ac",
241 | "command": [ "./cmd comma tmp/#/a?"],
242 | "stdout": ["==", "tmp/#/aa,tmp/#/ab,tmp/#/ac"],
243 | "stderr": ["==", ""],
244 | "return_code": ["==", 0]
245 | },
246 | {
247 | "description": "globs %*p, *",
248 | "setup": "cd tmp; mkdir #; touch #/aa #/ab #/ac",
249 | "command": [ "./cmd comma tmp/#/%*p", "a*" ],
250 | "stdout": ["==", "tmp/#/aa,tmp/#/ab,tmp/#/ac"],
251 | "stderr": ["==", ""],
252 | "return_code": ["==", 0]
253 | },
254 | {
255 | "description": "filename sandbox escape",
256 | "setup": "mkdir -p tmp/#/x; mkdir -p tmp/#/y",
257 | "command": [ "./cmd comma tmp/#/x/%*p", "../y" ],
258 | "stdout": ["==", ""],
259 | "stderr": ["contains", "systemf: Permission denied: sandboxing tmp/#/x/../y"],
260 | "return_code": ["==", -1]
261 | },
262 | {
263 | "description": "filename sandbox unescape",
264 | "setup": "mkdir -p tmp/#/x",
265 | "command": [ "./cmd comma tmp/#/x/%*p", "../../../tmp/#/x" ],
266 | "stdout": ["==", "tmp/#/x/../../../tmp/#/x"],
267 | "stderr": ["==", ""],
268 | "return_code": ["==", 0]
269 | },
270 | {
271 | "description": "true || not-run",
272 | "command": [ "./cmd true || ./cmd stdout" ],
273 | "stdout": ["==", ""],
274 | "stderr": ["==", ""],
275 | "return_code": ["==", 0]
276 | },
277 | {
278 | "description": "false || run",
279 | "command": [ "./cmd false || ./cmd stderr return 3" ],
280 | "stdout": ["==", ""],
281 | "stderr": ["==", "2"],
282 | "return_code": ["==", 3]
283 | },
284 | {
285 | "description": "false && not-run",
286 | "command": [ "./cmd false && ./cmd stdout" ],
287 | "stdout": ["==", ""],
288 | "stderr": ["==", ""],
289 | "return_code": ["==", 1]
290 | },
291 | {
292 | "description": "true && run",
293 | "command": [ "./cmd true && ./cmd stderr return 3" ],
294 | "stdout": ["==", ""],
295 | "stderr": ["==", "2"],
296 | "return_code": ["==", 3]
297 | },
298 | {
299 | "description": "pipe to subcommand exits when cmd closes",
300 | "command": [ "./cmd count | ./cmd incr" ],
301 | "stdout": ["==", "2"],
302 | "stderr": ["==", ""],
303 | "return_code": ["==", 0]
304 | },
305 | {
306 | "description": "multiple pipes run properly",
307 | "command": [ "./cmd count | ./cmd incr | ./cmd incr | ./cmd incr" ],
308 | "stdout": ["==", "4"],
309 | "stderr": ["==", ""],
310 | "return_code": ["==", 0]
311 | }
312 | ]
313 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/src/task.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include "systemf-internal.h"
13 |
14 | #define DEBUG 0
15 | #define VA_ARGS(...) , ##__VA_ARGS__
16 | #define DBG(fmt, ...) if (DEBUG) { printf("%s:%-3d:%24s: " fmt "\n", __FILE__, __LINE__, __FUNCTION__ VA_ARGS(__VA_ARGS__)); }
17 |
18 | typedef struct glob_list_ {
19 | struct glob_list_ *next;
20 | glob_t glob;
21 | } glob_list;
22 |
23 | _sf1_task *_sf1_task_create() {
24 | return calloc(1, sizeof(_sf1_task));
25 | }
26 |
27 | /*
28 | * _sf1_task_add_redirects - Adds a linked of redirections.
29 | *
30 | * task - Destination Task - Current redirects must be NULL,
31 | * redirects - A linked list of _sf1_redirects where:
32 | * each redirect is malloced
33 | * filename - Is NULL or the destination malloced filename (if appropriate)
34 | * trusted_path - Is the malloced verification path for filename where NULL is current directory.
35 | * append - Specifies if the redirect shoud append to the file.
36 | *
37 | * Note that all malloced memory passed in will be freed with _sf1_task_free().
38 | * Note that validation of parameters is delayed until that task start happens.
39 | */
40 | void _sf1_task_add_redirects (_sf1_task *task, _sf1_redirect *redirects)
41 | {
42 | assert(task->redirects == NULL);
43 | task->redirects = redirects;
44 | }
45 |
46 | /*
47 | * _sf1_task_add_arg - Adds the specified argument to the task.
48 | *
49 | * task - Destination Task
50 | * text - The text of the argument. Must be malloced.
51 | * trusted_path - If path verification is to be done this is the pre-realpath path. Either NULL or malloced.
52 | * is_glob - This is a glob expression that glob expansion will be needed for.
53 | *
54 | * Note that all malloced memory passed in will be freed with _sf1_task_free()
55 | */
56 | _sf1_task_arg *_sf1_task_add_arg (_sf1_task *task, char *text, char *trusted_path, int is_glob)
57 | {
58 | _sf1_task_arg *arg;
59 |
60 | arg = calloc(1, sizeof(_sf1_task_arg));
61 | if (arg == NULL) {
62 | return NULL;
63 | }
64 |
65 | arg->text = text;
66 | arg->trusted_path = trusted_path;
67 | arg->is_glob = is_glob;
68 |
69 | // Find the last item and append pp
70 | _sf1_task_arg **next_pp = &(task->args);
71 | while (*next_pp) {
72 | next_pp = &((*next_pp)->next);
73 | }
74 | *next_pp = arg;
75 |
76 | return arg;
77 | }
78 |
79 | /*
80 | * Maps a stream to a character string representation.
81 | */
82 | char *_sf1_stream_name(_sf1_stream stream) {
83 | switch (stream) {
84 | case _SF1_STDIN: return "stdin"; break;
85 | case _SF1_STDOUT: return "stdout"; break;
86 | case _SF1_SHARE: return "shared"; break;
87 | case _SF1_STDERR: return "stderr"; break;
88 | case _SF1_PIPE: return "pipe"; break;
89 | case _SF1_FILE: return "file"; break;
90 | default: assert(0);
91 | }
92 | }
93 |
94 | /*
95 | * Walks all of the redirects and verifies that they are entered in a sane way.
96 | * Returns 0 if not and 1 if good.
97 | */
98 | int _sf1_redirects_are_sane(_sf1_task *tasks)
99 | {
100 | for (_sf1_task *t = tasks; t; t = t->next) {
101 | int in = 0;
102 | int out = 0;
103 | int err = 0;
104 | for (_sf1_redirect *r = t->redirects; r; r = r ->next) {
105 | int count;
106 | switch (r->stream) {
107 | case _SF1_STDIN:
108 | count = in = in + 1;
109 | break;
110 | case _SF1_STDOUT:
111 | count = out = out + 1;
112 | break;
113 | case _SF1_STDERR:
114 | count = err = err + 1;
115 | break;
116 | default:
117 | assert(0);
118 | }
119 | if (count > 1) {
120 | fprintf(stderr, "ERROR: There should only be one %s per command.", _sf1_stream_name(r->stream));
121 | return 0;
122 | }
123 | if ((r->target == _SF1_FILE) && !(r->text)) {
124 | // A filename must be supplied.
125 | return 0;
126 | }
127 | }
128 | }
129 | return 1;
130 | }
131 |
132 | /*
133 | * Extracts all the globs from the task arguments.
134 | * task - The task.
135 | * returns:
136 | * 0 on success
137 | * GLOB_NOSPACE for running out of memory,
138 | * GLOB_ABORTED for a read error, and
139 | * GLOB_NOMATCH for when the number of matches doesn't match the specified allowed match count.
140 | */
141 | int _sf1_extract_glob(_sf1_task *task)
142 | {
143 | for (_sf1_task_arg *a = task->args; a != NULL; a = a->next) {
144 | if (a->is_glob) {
145 | int ret = glob(a->text, 0, NULL, &a->glob);
146 | // FIXME: do a bounds check.
147 | switch (ret) {
148 | case GLOB_NOSPACE:
149 | fprintf(stderr, "systemf: glob out of memory extracting: %s\n", a->text);
150 | return ENOMEM;
151 | case GLOB_ABORTED:
152 | fprintf(stderr, "systemf: glob aborted during extraction: %s\n", a->text);
153 | return EBADF;
154 | case GLOB_NOMATCH:
155 | fprintf(stderr, "systemf: no matches found: %s\n", a->text);
156 | return EINVAL;
157 | }
158 | }
159 | }
160 | return 0;
161 | }
162 |
163 | void _sf1_task_free(_sf1_task *task)
164 | {
165 | _sf1_task *next;
166 | for (; task != NULL; task = next) {
167 | _sf1_task_arg *anext;
168 | for (_sf1_task_arg *a = task->args; a != NULL; a = anext) {
169 | globfree(&a->glob);
170 | free(a->text);
171 | free(a->trusted_path);
172 | anext = a->next;
173 | free(a);
174 | }
175 |
176 | _sf1_redirect *rnext;
177 | for (_sf1_redirect *r = task->redirects; r != NULL; r = rnext) {
178 | free(r->text);
179 | free(r->trusted_path);
180 | rnext = r->next;
181 | free(r);
182 | }
183 |
184 | free(task->argv);
185 | next = task->next;
186 | free(task);
187 | }
188 | return;
189 | }
190 |
191 | /*
192 | * Fills in the files for the tasks.
193 | *
194 | * Returns -1 on failure and 0 on success.
195 | */
196 | int _sf1_populate_task_files(_sf1_task *task, _sf1_task_files *files) {
197 | int pipefd[2];
198 | _sf1_redirect *redirect;
199 | int rwrwrw = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
200 | int prev_out_rd_pipe = files->out_rd_pipe;
201 |
202 | files->in = 0;
203 | files->out = 1;
204 | files->err = 2;
205 | files->out_rd_pipe = 0;
206 |
207 | for (redirect = task->redirects; redirect; redirect = redirect->next) {
208 | if (redirect->stream == _SF1_STDIN) {
209 | if (redirect->target == _SF1_FILE) {
210 | files->in = open(redirect->text, O_RDONLY);
211 | if (files->in < 0) {
212 | fprintf(stderr, "systemf: %s: %s\n", strerror(errno), redirect->text);
213 | return -1;
214 | }
215 | } else { // _SF1_PIPE
216 | files->in = prev_out_rd_pipe;
217 | }
218 | } else if (redirect->stream == _SF1_STDOUT) {
219 | if (redirect->target == _SF1_FILE) {
220 | files->out = open(redirect->text, O_WRONLY | O_CREAT | (redirect->append ? O_APPEND : O_TRUNC), rwrwrw);
221 | if (files->out < 0) {
222 | fprintf(stderr, "systemf: %s: %s\n", strerror(errno), redirect->text);
223 | return -1;
224 | }
225 | } else if (redirect->target == _SF1_SHARE) {
226 | files->out = files->err;
227 | } else { // _SF1_PIPE
228 | if (pipe(pipefd)) {
229 | fprintf(stderr, "systemf: %s opening a pipe\n", strerror(errno));
230 | return -1;
231 | }
232 | files->out_rd_pipe = pipefd[0];
233 | files->out = pipefd[1];
234 | }
235 | } else { // _SF1_STDERR
236 | if (redirect->target == _SF1_FILE) {
237 | files->err = open(redirect->text, O_WRONLY | O_CREAT | (redirect->append ? O_APPEND : O_TRUNC), rwrwrw);
238 | if (files->err < 0) {
239 | fprintf(stderr, "systemf: %s: %s\n\n", strerror(errno), redirect->text);
240 | return -1;
241 | }
242 | } else if (redirect->target == _SF1_SHARE) {
243 | files->err = files->out;
244 | }
245 | }
246 | }
247 | return 0;
248 | }
249 |
250 | /*
251 | * Check all of the arguments for this task that the files are properly sandboxed.
252 | * On success, returns 0. On failure sets the errno and returns -1;
253 | */
254 | int _sf1_file_sandbox_check_args(_sf1_task *task) {
255 | _sf1_task_arg *arg;
256 | int ret;
257 |
258 | for (arg = task->args; arg != NULL; arg = arg->next) {
259 | if (arg->trusted_path) {
260 | if (arg->is_glob) {
261 | int i;
262 | for (i = 0, ret = 0; (i < arg->glob.gl_pathc) && !ret; i++) {
263 | ret = _sf1_file_sandbox_check(arg->trusted_path, arg->glob.gl_pathv[i]);
264 | }
265 | } else {
266 | ret = _sf1_file_sandbox_check(arg->trusted_path, arg->text);
267 | }
268 | if (ret) {
269 | errno = ret;
270 | return -1;
271 | }
272 | }
273 | }
274 | return 0;
275 | }
276 |
277 |
278 | /*
279 | * Close the child in out and error if they aren't shared with the parent.
280 | */
281 | void _sf1_close_child_files(_sf1_task_files *files) {
282 | if (files->in > 2) {
283 | close(files->in);
284 | files->in = 0;
285 | }
286 | if (files->out > 2) {
287 | close(files->out);
288 | files->out = 1;
289 | }
290 | if (files->err > 2) {
291 | close(files->err);
292 | files->err = 2;
293 | }
294 | }
295 |
296 | /*
297 | * Close the child in out, error, and pipe.
298 | */
299 | void _sf1_close_child_files_and_pipe(_sf1_task_files *files) {
300 | if (files->out_rd_pipe) {
301 | close(files->out_rd_pipe);
302 | files->out_rd_pipe = 0;
303 | }
304 | _sf1_close_child_files(files);
305 | }
306 |
307 | int _sf1_tasks_run(_sf1_task *tasks) {
308 | pid_t pid;
309 | _sf1_pid_chain_t *pid_chain = NULL;
310 | int stat;
311 | char **argv;
312 | _sf1_task_arg *arg;
313 | size_t argc = 1; // 1 for terminating NULL
314 | int ret;
315 | int retval = -1;
316 | _sf1_task_files files = {.in=0, .out=1, .err=2, .out_rd_pipe=0};
317 |
318 | if (!_sf1_redirects_are_sane(tasks)) {
319 | goto exit_error;
320 | }
321 |
322 | // We don't support tasks reuse, so argv MUST be null coming into this.
323 | assert(tasks->argv == NULL);
324 |
325 | for (_sf1_task *task = tasks; task; task = task->next) {
326 | ret = _sf1_extract_glob(task);
327 | if (ret) {
328 | errno = ret;
329 | goto exit_error;
330 | }
331 |
332 | // Count the arguments.
333 | for (arg = task->args; arg != NULL; arg = arg->next) {
334 | if (arg->is_glob) {
335 | argc += arg->glob.gl_pathc;
336 | } else {
337 | argc += 1;
338 | }
339 | }
340 |
341 | if (_sf1_file_sandbox_check_args(task)) {
342 | goto exit_error;
343 | }
344 |
345 | task->argv = malloc(argc * sizeof(char *));
346 | // FIXME: handle NULL
347 |
348 | argv = task->argv;
349 | for (arg = task->args; arg != NULL; arg = arg->next) {
350 | if (arg->is_glob) {
351 | memcpy(argv, arg->glob.gl_pathv, sizeof(char *) * arg->glob.gl_pathc);
352 | argv += arg->glob.gl_pathc;
353 | } else {
354 | *argv = arg->text;
355 | argv++;
356 | }
357 | }
358 | *argv = NULL;
359 | DBG("_____________________ err exi exs sig tsig\n");
360 |
361 | if (_sf1_populate_task_files(task, &files)) {
362 | goto exit_error;
363 | }
364 |
365 | // If we don't flush, both forks will send the buffered data and it will be seen twice.
366 | fflush(stdout);
367 | fflush(stderr);
368 |
369 | // FIXME: Determine if the file exists and it is executable before attempting
370 | // to fork which doesn't know how to handle results.
371 |
372 | pid = fork();
373 | if (pid == 0) {
374 | dup2(files.in, 0);
375 | dup2(files.out, 1);
376 | dup2(files.err, 2);
377 | _sf1_close_upper_fd();
378 |
379 | DBG("Running %s", task->argv[0]);
380 | stat = execv(*task->argv, task->argv);
381 | DBG("Execv returned with %3d %3d %3d %3d %3d\n", errno,
382 | WIFEXITED(stat), WEXITSTATUS(stat), WIFSIGNALED(stat), WTERMSIG(stat));
383 |
384 | // If execv exits, we don't have an easy way to send the results back other
385 | // than some extra socket. Instead we should try catching all exceptions
386 | // (file not found, file not executable) before forking. Kill thyself.
387 | kill(getpid(), SIGKILL);
388 | }
389 | _sf1_close_child_files(&files);
390 |
391 | pid_chain = _sf1_pid_chain_add(pid_chain, pid);
392 | if (!pid_chain) {
393 | fprintf(stderr, "systemf: pid_chain out of memory");
394 | goto exit_error;
395 | }
396 |
397 | // Only wait for completion if this is not piped.
398 | // I.E. "cat | grep" should run the grep before waiting for the cat to complete.
399 | if (files.out_rd_pipe == 0) {
400 | if (_sf1_pid_chain_waitpids(pid_chain, &stat, 0) == 0) {
401 | // FIXME: Make sure this is the right return value and better recover from this.
402 | fprintf(stderr, "waitpid unexpectedly returned %s", strerror(errno));
403 | goto exit_error;
404 | }
405 | _sf1_pid_chain_clear(pid_chain);
406 |
407 | retval = WEXITSTATUS(stat);
408 |
409 | if (WIFSIGNALED(stat)) {
410 | fprintf(stderr, "waipid exited with signal %s\n", strsignal(WTERMSIG(stat)));
411 | goto exit_error;
412 | }
413 |
414 | DBG("waitpid returned with %3d %3d %3d %3d %3d\n", errno,
415 | WIFEXITED(stat), WEXITSTATUS(stat), WIFSIGNALED(stat), WTERMSIG(stat));
416 |
417 | if (WIFSIGNALED(stat) || (WIFEXITED(stat) && WEXITSTATUS(stat))) {
418 | if (task->next && (task->next->run_if == _SF1_RUN_IF_PREV_SUCCEEDED)) {
419 | DBG("exiting because previous failed");
420 | break;
421 | }
422 | } else {
423 | if (task->next && (task->next->run_if == _SF1_RUN_IF_PREV_FAILED)) {
424 | DBG("exiting because previous succeeded");
425 | break;
426 | }
427 | }
428 | }
429 | }
430 | if (0) {
431 | exit_error:
432 | retval = -1;
433 | }
434 |
435 | // Clean up everything locally created.
436 | _sf1_close_child_files(&files);
437 | _sf1_pid_chain_free(pid_chain);
438 |
439 | return retval;
440 | }
441 |
442 |
--------------------------------------------------------------------------------
/m4/ltoptions.m4:
--------------------------------------------------------------------------------
1 | # Helper functions for option handling. -*- Autoconf -*-
2 | #
3 | # Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
4 | # Foundation, Inc.
5 | # Written by Gary V. Vaughan, 2004
6 | #
7 | # This file is free software; the Free Software Foundation gives
8 | # unlimited permission to copy and/or distribute it, with or without
9 | # modifications, as long as this notice is preserved.
10 |
11 | # serial 8 ltoptions.m4
12 |
13 | # This is to help aclocal find these macros, as it can't see m4_define.
14 | AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
15 |
16 |
17 | # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
18 | # ------------------------------------------
19 | m4_define([_LT_MANGLE_OPTION],
20 | [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
21 |
22 |
23 | # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
24 | # ---------------------------------------
25 | # Set option OPTION-NAME for macro MACRO-NAME, and if there is a
26 | # matching handler defined, dispatch to it. Other OPTION-NAMEs are
27 | # saved as a flag.
28 | m4_define([_LT_SET_OPTION],
29 | [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
30 | m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
31 | _LT_MANGLE_DEFUN([$1], [$2]),
32 | [m4_warning([Unknown $1 option '$2'])])[]dnl
33 | ])
34 |
35 |
36 | # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
37 | # ------------------------------------------------------------
38 | # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
39 | m4_define([_LT_IF_OPTION],
40 | [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
41 |
42 |
43 | # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
44 | # -------------------------------------------------------
45 | # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
46 | # are set.
47 | m4_define([_LT_UNLESS_OPTIONS],
48 | [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
49 | [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
50 | [m4_define([$0_found])])])[]dnl
51 | m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
52 | ])[]dnl
53 | ])
54 |
55 |
56 | # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
57 | # ----------------------------------------
58 | # OPTION-LIST is a space-separated list of Libtool options associated
59 | # with MACRO-NAME. If any OPTION has a matching handler declared with
60 | # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
61 | # the unknown option and exit.
62 | m4_defun([_LT_SET_OPTIONS],
63 | [# Set options
64 | m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
65 | [_LT_SET_OPTION([$1], _LT_Option)])
66 |
67 | m4_if([$1],[LT_INIT],[
68 | dnl
69 | dnl Simply set some default values (i.e off) if boolean options were not
70 | dnl specified:
71 | _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
72 | ])
73 | _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
74 | ])
75 | dnl
76 | dnl If no reference was made to various pairs of opposing options, then
77 | dnl we run the default mode handler for the pair. For example, if neither
78 | dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
79 | dnl archives by default:
80 | _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
81 | _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
82 | _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
83 | _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
84 | [_LT_ENABLE_FAST_INSTALL])
85 | _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
86 | [_LT_WITH_AIX_SONAME([aix])])
87 | ])
88 | ])# _LT_SET_OPTIONS
89 |
90 |
91 | ## --------------------------------- ##
92 | ## Macros to handle LT_INIT options. ##
93 | ## --------------------------------- ##
94 |
95 | # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
96 | # -----------------------------------------
97 | m4_define([_LT_MANGLE_DEFUN],
98 | [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
99 |
100 |
101 | # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
102 | # -----------------------------------------------
103 | m4_define([LT_OPTION_DEFINE],
104 | [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
105 | ])# LT_OPTION_DEFINE
106 |
107 |
108 | # dlopen
109 | # ------
110 | LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
111 | ])
112 |
113 | AU_DEFUN([AC_LIBTOOL_DLOPEN],
114 | [_LT_SET_OPTION([LT_INIT], [dlopen])
115 | AC_DIAGNOSE([obsolete],
116 | [$0: Remove this warning and the call to _LT_SET_OPTION when you
117 | put the 'dlopen' option into LT_INIT's first parameter.])
118 | ])
119 |
120 | dnl aclocal-1.4 backwards compatibility:
121 | dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
122 |
123 |
124 | # win32-dll
125 | # ---------
126 | # Declare package support for building win32 dll's.
127 | LT_OPTION_DEFINE([LT_INIT], [win32-dll],
128 | [enable_win32_dll=yes
129 |
130 | case $host in
131 | *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
132 | AC_CHECK_TOOL(AS, as, false)
133 | AC_CHECK_TOOL(DLLTOOL, dlltool, false)
134 | AC_CHECK_TOOL(OBJDUMP, objdump, false)
135 | ;;
136 | esac
137 |
138 | test -z "$AS" && AS=as
139 | _LT_DECL([], [AS], [1], [Assembler program])dnl
140 |
141 | test -z "$DLLTOOL" && DLLTOOL=dlltool
142 | _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
143 |
144 | test -z "$OBJDUMP" && OBJDUMP=objdump
145 | _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
146 | ])# win32-dll
147 |
148 | AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
149 | [AC_REQUIRE([AC_CANONICAL_HOST])dnl
150 | _LT_SET_OPTION([LT_INIT], [win32-dll])
151 | AC_DIAGNOSE([obsolete],
152 | [$0: Remove this warning and the call to _LT_SET_OPTION when you
153 | put the 'win32-dll' option into LT_INIT's first parameter.])
154 | ])
155 |
156 | dnl aclocal-1.4 backwards compatibility:
157 | dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
158 |
159 |
160 | # _LT_ENABLE_SHARED([DEFAULT])
161 | # ----------------------------
162 | # implement the --enable-shared flag, and supports the 'shared' and
163 | # 'disable-shared' LT_INIT options.
164 | # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
165 | m4_define([_LT_ENABLE_SHARED],
166 | [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
167 | AC_ARG_ENABLE([shared],
168 | [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
169 | [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
170 | [p=${PACKAGE-default}
171 | case $enableval in
172 | yes) enable_shared=yes ;;
173 | no) enable_shared=no ;;
174 | *)
175 | enable_shared=no
176 | # Look at the argument we got. We use all the common list separators.
177 | lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
178 | for pkg in $enableval; do
179 | IFS=$lt_save_ifs
180 | if test "X$pkg" = "X$p"; then
181 | enable_shared=yes
182 | fi
183 | done
184 | IFS=$lt_save_ifs
185 | ;;
186 | esac],
187 | [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
188 |
189 | _LT_DECL([build_libtool_libs], [enable_shared], [0],
190 | [Whether or not to build shared libraries])
191 | ])# _LT_ENABLE_SHARED
192 |
193 | LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
194 | LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
195 |
196 | # Old names:
197 | AC_DEFUN([AC_ENABLE_SHARED],
198 | [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
199 | ])
200 |
201 | AC_DEFUN([AC_DISABLE_SHARED],
202 | [_LT_SET_OPTION([LT_INIT], [disable-shared])
203 | ])
204 |
205 | AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
206 | AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
207 |
208 | dnl aclocal-1.4 backwards compatibility:
209 | dnl AC_DEFUN([AM_ENABLE_SHARED], [])
210 | dnl AC_DEFUN([AM_DISABLE_SHARED], [])
211 |
212 |
213 |
214 | # _LT_ENABLE_STATIC([DEFAULT])
215 | # ----------------------------
216 | # implement the --enable-static flag, and support the 'static' and
217 | # 'disable-static' LT_INIT options.
218 | # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
219 | m4_define([_LT_ENABLE_STATIC],
220 | [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
221 | AC_ARG_ENABLE([static],
222 | [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
223 | [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
224 | [p=${PACKAGE-default}
225 | case $enableval in
226 | yes) enable_static=yes ;;
227 | no) enable_static=no ;;
228 | *)
229 | enable_static=no
230 | # Look at the argument we got. We use all the common list separators.
231 | lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
232 | for pkg in $enableval; do
233 | IFS=$lt_save_ifs
234 | if test "X$pkg" = "X$p"; then
235 | enable_static=yes
236 | fi
237 | done
238 | IFS=$lt_save_ifs
239 | ;;
240 | esac],
241 | [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
242 |
243 | _LT_DECL([build_old_libs], [enable_static], [0],
244 | [Whether or not to build static libraries])
245 | ])# _LT_ENABLE_STATIC
246 |
247 | LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
248 | LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
249 |
250 | # Old names:
251 | AC_DEFUN([AC_ENABLE_STATIC],
252 | [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
253 | ])
254 |
255 | AC_DEFUN([AC_DISABLE_STATIC],
256 | [_LT_SET_OPTION([LT_INIT], [disable-static])
257 | ])
258 |
259 | AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
260 | AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
261 |
262 | dnl aclocal-1.4 backwards compatibility:
263 | dnl AC_DEFUN([AM_ENABLE_STATIC], [])
264 | dnl AC_DEFUN([AM_DISABLE_STATIC], [])
265 |
266 |
267 |
268 | # _LT_ENABLE_FAST_INSTALL([DEFAULT])
269 | # ----------------------------------
270 | # implement the --enable-fast-install flag, and support the 'fast-install'
271 | # and 'disable-fast-install' LT_INIT options.
272 | # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
273 | m4_define([_LT_ENABLE_FAST_INSTALL],
274 | [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
275 | AC_ARG_ENABLE([fast-install],
276 | [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
277 | [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
278 | [p=${PACKAGE-default}
279 | case $enableval in
280 | yes) enable_fast_install=yes ;;
281 | no) enable_fast_install=no ;;
282 | *)
283 | enable_fast_install=no
284 | # Look at the argument we got. We use all the common list separators.
285 | lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
286 | for pkg in $enableval; do
287 | IFS=$lt_save_ifs
288 | if test "X$pkg" = "X$p"; then
289 | enable_fast_install=yes
290 | fi
291 | done
292 | IFS=$lt_save_ifs
293 | ;;
294 | esac],
295 | [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
296 |
297 | _LT_DECL([fast_install], [enable_fast_install], [0],
298 | [Whether or not to optimize for fast installation])dnl
299 | ])# _LT_ENABLE_FAST_INSTALL
300 |
301 | LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
302 | LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
303 |
304 | # Old names:
305 | AU_DEFUN([AC_ENABLE_FAST_INSTALL],
306 | [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
307 | AC_DIAGNOSE([obsolete],
308 | [$0: Remove this warning and the call to _LT_SET_OPTION when you put
309 | the 'fast-install' option into LT_INIT's first parameter.])
310 | ])
311 |
312 | AU_DEFUN([AC_DISABLE_FAST_INSTALL],
313 | [_LT_SET_OPTION([LT_INIT], [disable-fast-install])
314 | AC_DIAGNOSE([obsolete],
315 | [$0: Remove this warning and the call to _LT_SET_OPTION when you put
316 | the 'disable-fast-install' option into LT_INIT's first parameter.])
317 | ])
318 |
319 | dnl aclocal-1.4 backwards compatibility:
320 | dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
321 | dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
322 |
323 |
324 | # _LT_WITH_AIX_SONAME([DEFAULT])
325 | # ----------------------------------
326 | # implement the --with-aix-soname flag, and support the `aix-soname=aix'
327 | # and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
328 | # is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
329 | m4_define([_LT_WITH_AIX_SONAME],
330 | [m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
331 | shared_archive_member_spec=
332 | case $host,$enable_shared in
333 | power*-*-aix[[5-9]]*,yes)
334 | AC_MSG_CHECKING([which variant of shared library versioning to provide])
335 | AC_ARG_WITH([aix-soname],
336 | [AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
337 | [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
338 | [case $withval in
339 | aix|svr4|both)
340 | ;;
341 | *)
342 | AC_MSG_ERROR([Unknown argument to --with-aix-soname])
343 | ;;
344 | esac
345 | lt_cv_with_aix_soname=$with_aix_soname],
346 | [AC_CACHE_VAL([lt_cv_with_aix_soname],
347 | [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
348 | with_aix_soname=$lt_cv_with_aix_soname])
349 | AC_MSG_RESULT([$with_aix_soname])
350 | if test aix != "$with_aix_soname"; then
351 | # For the AIX way of multilib, we name the shared archive member
352 | # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
353 | # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
354 | # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
355 | # the AIX toolchain works better with OBJECT_MODE set (default 32).
356 | if test 64 = "${OBJECT_MODE-32}"; then
357 | shared_archive_member_spec=shr_64
358 | else
359 | shared_archive_member_spec=shr
360 | fi
361 | fi
362 | ;;
363 | *)
364 | with_aix_soname=aix
365 | ;;
366 | esac
367 |
368 | _LT_DECL([], [shared_archive_member_spec], [0],
369 | [Shared archive member basename, for filename based shared library versioning on AIX])dnl
370 | ])# _LT_WITH_AIX_SONAME
371 |
372 | LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
373 | LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
374 | LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
375 |
376 |
377 | # _LT_WITH_PIC([MODE])
378 | # --------------------
379 | # implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
380 | # LT_INIT options.
381 | # MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
382 | m4_define([_LT_WITH_PIC],
383 | [AC_ARG_WITH([pic],
384 | [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
385 | [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
386 | [lt_p=${PACKAGE-default}
387 | case $withval in
388 | yes|no) pic_mode=$withval ;;
389 | *)
390 | pic_mode=default
391 | # Look at the argument we got. We use all the common list separators.
392 | lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
393 | for lt_pkg in $withval; do
394 | IFS=$lt_save_ifs
395 | if test "X$lt_pkg" = "X$lt_p"; then
396 | pic_mode=yes
397 | fi
398 | done
399 | IFS=$lt_save_ifs
400 | ;;
401 | esac],
402 | [pic_mode=m4_default([$1], [default])])
403 |
404 | _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
405 | ])# _LT_WITH_PIC
406 |
407 | LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
408 | LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
409 |
410 | # Old name:
411 | AU_DEFUN([AC_LIBTOOL_PICMODE],
412 | [_LT_SET_OPTION([LT_INIT], [pic-only])
413 | AC_DIAGNOSE([obsolete],
414 | [$0: Remove this warning and the call to _LT_SET_OPTION when you
415 | put the 'pic-only' option into LT_INIT's first parameter.])
416 | ])
417 |
418 | dnl aclocal-1.4 backwards compatibility:
419 | dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
420 |
421 | ## ----------------- ##
422 | ## LTDL_INIT Options ##
423 | ## ----------------- ##
424 |
425 | m4_define([_LTDL_MODE], [])
426 | LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
427 | [m4_define([_LTDL_MODE], [nonrecursive])])
428 | LT_OPTION_DEFINE([LTDL_INIT], [recursive],
429 | [m4_define([_LTDL_MODE], [recursive])])
430 | LT_OPTION_DEFINE([LTDL_INIT], [subproject],
431 | [m4_define([_LTDL_MODE], [subproject])])
432 |
433 | m4_define([_LTDL_TYPE], [])
434 | LT_OPTION_DEFINE([LTDL_INIT], [installable],
435 | [m4_define([_LTDL_TYPE], [installable])])
436 | LT_OPTION_DEFINE([LTDL_INIT], [convenience],
437 | [m4_define([_LTDL_TYPE], [convenience])])
438 |
--------------------------------------------------------------------------------
/src/derived-lexer.h:
--------------------------------------------------------------------------------
1 | #ifndef _sf1_yyHEADER_H
2 | #define _sf1_yyHEADER_H 1
3 | #define _sf1_yyIN_HEADER 1
4 |
5 | #line 6 "src/derived-lexer.h"
6 |
7 | #line 8 "src/derived-lexer.h"
8 |
9 | #define YY_INT_ALIGNED short int
10 |
11 | /* A lexical scanner generated by flex */
12 |
13 | #define FLEX_SCANNER
14 | #define YY_FLEX_MAJOR_VERSION 2
15 | #define YY_FLEX_MINOR_VERSION 6
16 | #define YY_FLEX_SUBMINOR_VERSION 4
17 | #if YY_FLEX_SUBMINOR_VERSION > 0
18 | #define FLEX_BETA
19 | #endif
20 |
21 | #ifdef yy_create_buffer
22 | #define _sf1_yy_create_buffer_ALREADY_DEFINED
23 | #else
24 | #define yy_create_buffer _sf1_yy_create_buffer
25 | #endif
26 |
27 | #ifdef yy_delete_buffer
28 | #define _sf1_yy_delete_buffer_ALREADY_DEFINED
29 | #else
30 | #define yy_delete_buffer _sf1_yy_delete_buffer
31 | #endif
32 |
33 | #ifdef yy_scan_buffer
34 | #define _sf1_yy_scan_buffer_ALREADY_DEFINED
35 | #else
36 | #define yy_scan_buffer _sf1_yy_scan_buffer
37 | #endif
38 |
39 | #ifdef yy_scan_string
40 | #define _sf1_yy_scan_string_ALREADY_DEFINED
41 | #else
42 | #define yy_scan_string _sf1_yy_scan_string
43 | #endif
44 |
45 | #ifdef yy_scan_bytes
46 | #define _sf1_yy_scan_bytes_ALREADY_DEFINED
47 | #else
48 | #define yy_scan_bytes _sf1_yy_scan_bytes
49 | #endif
50 |
51 | #ifdef yy_init_buffer
52 | #define _sf1_yy_init_buffer_ALREADY_DEFINED
53 | #else
54 | #define yy_init_buffer _sf1_yy_init_buffer
55 | #endif
56 |
57 | #ifdef yy_flush_buffer
58 | #define _sf1_yy_flush_buffer_ALREADY_DEFINED
59 | #else
60 | #define yy_flush_buffer _sf1_yy_flush_buffer
61 | #endif
62 |
63 | #ifdef yy_load_buffer_state
64 | #define _sf1_yy_load_buffer_state_ALREADY_DEFINED
65 | #else
66 | #define yy_load_buffer_state _sf1_yy_load_buffer_state
67 | #endif
68 |
69 | #ifdef yy_switch_to_buffer
70 | #define _sf1_yy_switch_to_buffer_ALREADY_DEFINED
71 | #else
72 | #define yy_switch_to_buffer _sf1_yy_switch_to_buffer
73 | #endif
74 |
75 | #ifdef yypush_buffer_state
76 | #define _sf1_yypush_buffer_state_ALREADY_DEFINED
77 | #else
78 | #define yypush_buffer_state _sf1_yypush_buffer_state
79 | #endif
80 |
81 | #ifdef yypop_buffer_state
82 | #define _sf1_yypop_buffer_state_ALREADY_DEFINED
83 | #else
84 | #define yypop_buffer_state _sf1_yypop_buffer_state
85 | #endif
86 |
87 | #ifdef yyensure_buffer_stack
88 | #define _sf1_yyensure_buffer_stack_ALREADY_DEFINED
89 | #else
90 | #define yyensure_buffer_stack _sf1_yyensure_buffer_stack
91 | #endif
92 |
93 | #ifdef yylex
94 | #define _sf1_yylex_ALREADY_DEFINED
95 | #else
96 | #define yylex _sf1_yylex
97 | #endif
98 |
99 | #ifdef yyrestart
100 | #define _sf1_yyrestart_ALREADY_DEFINED
101 | #else
102 | #define yyrestart _sf1_yyrestart
103 | #endif
104 |
105 | #ifdef yylex_init
106 | #define _sf1_yylex_init_ALREADY_DEFINED
107 | #else
108 | #define yylex_init _sf1_yylex_init
109 | #endif
110 |
111 | #ifdef yylex_init_extra
112 | #define _sf1_yylex_init_extra_ALREADY_DEFINED
113 | #else
114 | #define yylex_init_extra _sf1_yylex_init_extra
115 | #endif
116 |
117 | #ifdef yylex_destroy
118 | #define _sf1_yylex_destroy_ALREADY_DEFINED
119 | #else
120 | #define yylex_destroy _sf1_yylex_destroy
121 | #endif
122 |
123 | #ifdef yyget_debug
124 | #define _sf1_yyget_debug_ALREADY_DEFINED
125 | #else
126 | #define yyget_debug _sf1_yyget_debug
127 | #endif
128 |
129 | #ifdef yyset_debug
130 | #define _sf1_yyset_debug_ALREADY_DEFINED
131 | #else
132 | #define yyset_debug _sf1_yyset_debug
133 | #endif
134 |
135 | #ifdef yyget_extra
136 | #define _sf1_yyget_extra_ALREADY_DEFINED
137 | #else
138 | #define yyget_extra _sf1_yyget_extra
139 | #endif
140 |
141 | #ifdef yyset_extra
142 | #define _sf1_yyset_extra_ALREADY_DEFINED
143 | #else
144 | #define yyset_extra _sf1_yyset_extra
145 | #endif
146 |
147 | #ifdef yyget_in
148 | #define _sf1_yyget_in_ALREADY_DEFINED
149 | #else
150 | #define yyget_in _sf1_yyget_in
151 | #endif
152 |
153 | #ifdef yyset_in
154 | #define _sf1_yyset_in_ALREADY_DEFINED
155 | #else
156 | #define yyset_in _sf1_yyset_in
157 | #endif
158 |
159 | #ifdef yyget_out
160 | #define _sf1_yyget_out_ALREADY_DEFINED
161 | #else
162 | #define yyget_out _sf1_yyget_out
163 | #endif
164 |
165 | #ifdef yyset_out
166 | #define _sf1_yyset_out_ALREADY_DEFINED
167 | #else
168 | #define yyset_out _sf1_yyset_out
169 | #endif
170 |
171 | #ifdef yyget_leng
172 | #define _sf1_yyget_leng_ALREADY_DEFINED
173 | #else
174 | #define yyget_leng _sf1_yyget_leng
175 | #endif
176 |
177 | #ifdef yyget_text
178 | #define _sf1_yyget_text_ALREADY_DEFINED
179 | #else
180 | #define yyget_text _sf1_yyget_text
181 | #endif
182 |
183 | #ifdef yyget_lineno
184 | #define _sf1_yyget_lineno_ALREADY_DEFINED
185 | #else
186 | #define yyget_lineno _sf1_yyget_lineno
187 | #endif
188 |
189 | #ifdef yyset_lineno
190 | #define _sf1_yyset_lineno_ALREADY_DEFINED
191 | #else
192 | #define yyset_lineno _sf1_yyset_lineno
193 | #endif
194 |
195 | #ifdef yyget_column
196 | #define _sf1_yyget_column_ALREADY_DEFINED
197 | #else
198 | #define yyget_column _sf1_yyget_column
199 | #endif
200 |
201 | #ifdef yyset_column
202 | #define _sf1_yyset_column_ALREADY_DEFINED
203 | #else
204 | #define yyset_column _sf1_yyset_column
205 | #endif
206 |
207 | #ifdef yywrap
208 | #define _sf1_yywrap_ALREADY_DEFINED
209 | #else
210 | #define yywrap _sf1_yywrap
211 | #endif
212 |
213 | #ifdef yyget_lval
214 | #define _sf1_yyget_lval_ALREADY_DEFINED
215 | #else
216 | #define yyget_lval _sf1_yyget_lval
217 | #endif
218 |
219 | #ifdef yyset_lval
220 | #define _sf1_yyset_lval_ALREADY_DEFINED
221 | #else
222 | #define yyset_lval _sf1_yyset_lval
223 | #endif
224 |
225 | #ifdef yyalloc
226 | #define _sf1_yyalloc_ALREADY_DEFINED
227 | #else
228 | #define yyalloc _sf1_yyalloc
229 | #endif
230 |
231 | #ifdef yyrealloc
232 | #define _sf1_yyrealloc_ALREADY_DEFINED
233 | #else
234 | #define yyrealloc _sf1_yyrealloc
235 | #endif
236 |
237 | #ifdef yyfree
238 | #define _sf1_yyfree_ALREADY_DEFINED
239 | #else
240 | #define yyfree _sf1_yyfree
241 | #endif
242 |
243 | /* First, we deal with platform-specific or compiler-specific issues. */
244 |
245 | /* begin standard C headers. */
246 | #include
247 | #include
248 | #include
249 | #include
250 |
251 | /* end standard C headers. */
252 |
253 | /* flex integer type definitions */
254 |
255 | #ifndef FLEXINT_H
256 | #define FLEXINT_H
257 |
258 | /* C99 systems have . Non-C99 systems may or may not. */
259 |
260 | #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
261 |
262 | /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
263 | * if you want the limit (max/min) macros for int types.
264 | */
265 | #ifndef __STDC_LIMIT_MACROS
266 | #define __STDC_LIMIT_MACROS 1
267 | #endif
268 |
269 | #include
270 | typedef int8_t flex_int8_t;
271 | typedef uint8_t flex_uint8_t;
272 | typedef int16_t flex_int16_t;
273 | typedef uint16_t flex_uint16_t;
274 | typedef int32_t flex_int32_t;
275 | typedef uint32_t flex_uint32_t;
276 | #else
277 | typedef signed char flex_int8_t;
278 | typedef short int flex_int16_t;
279 | typedef int flex_int32_t;
280 | typedef unsigned char flex_uint8_t;
281 | typedef unsigned short int flex_uint16_t;
282 | typedef unsigned int flex_uint32_t;
283 |
284 | /* Limits of integral types. */
285 | #ifndef INT8_MIN
286 | #define INT8_MIN (-128)
287 | #endif
288 | #ifndef INT16_MIN
289 | #define INT16_MIN (-32767-1)
290 | #endif
291 | #ifndef INT32_MIN
292 | #define INT32_MIN (-2147483647-1)
293 | #endif
294 | #ifndef INT8_MAX
295 | #define INT8_MAX (127)
296 | #endif
297 | #ifndef INT16_MAX
298 | #define INT16_MAX (32767)
299 | #endif
300 | #ifndef INT32_MAX
301 | #define INT32_MAX (2147483647)
302 | #endif
303 | #ifndef UINT8_MAX
304 | #define UINT8_MAX (255U)
305 | #endif
306 | #ifndef UINT16_MAX
307 | #define UINT16_MAX (65535U)
308 | #endif
309 | #ifndef UINT32_MAX
310 | #define UINT32_MAX (4294967295U)
311 | #endif
312 |
313 | #ifndef SIZE_MAX
314 | #define SIZE_MAX (~(size_t)0)
315 | #endif
316 |
317 | #endif /* ! C99 */
318 |
319 | #endif /* ! FLEXINT_H */
320 |
321 | /* begin standard C++ headers. */
322 |
323 | /* TODO: this is always defined, so inline it */
324 | #define yyconst const
325 |
326 | #if defined(__GNUC__) && __GNUC__ >= 3
327 | #define yynoreturn __attribute__((__noreturn__))
328 | #else
329 | #define yynoreturn
330 | #endif
331 |
332 | /* An opaque pointer. */
333 | #ifndef YY_TYPEDEF_YY_SCANNER_T
334 | #define YY_TYPEDEF_YY_SCANNER_T
335 | typedef void* yyscan_t;
336 | #endif
337 |
338 | /* For convenience, these vars (plus the bison vars far below)
339 | are macros in the reentrant scanner. */
340 | #define yyin yyg->yyin_r
341 | #define yyout yyg->yyout_r
342 | #define yyextra yyg->yyextra_r
343 | #define yyleng yyg->yyleng_r
344 | #define yytext yyg->yytext_r
345 | #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
346 | #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
347 | #define yy_flex_debug yyg->yy_flex_debug_r
348 |
349 | /* Size of default input buffer. */
350 | #ifndef YY_BUF_SIZE
351 | #ifdef __ia64__
352 | /* On IA-64, the buffer size is 16k, not 8k.
353 | * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
354 | * Ditto for the __ia64__ case accordingly.
355 | */
356 | #define YY_BUF_SIZE 32768
357 | #else
358 | #define YY_BUF_SIZE 16384
359 | #endif /* __ia64__ */
360 | #endif
361 |
362 | #ifndef YY_TYPEDEF_YY_BUFFER_STATE
363 | #define YY_TYPEDEF_YY_BUFFER_STATE
364 | typedef struct yy_buffer_state *YY_BUFFER_STATE;
365 | #endif
366 |
367 | #ifndef YY_TYPEDEF_YY_SIZE_T
368 | #define YY_TYPEDEF_YY_SIZE_T
369 | typedef size_t yy_size_t;
370 | #endif
371 |
372 | #ifndef YY_STRUCT_YY_BUFFER_STATE
373 | #define YY_STRUCT_YY_BUFFER_STATE
374 | struct yy_buffer_state
375 | {
376 | FILE *yy_input_file;
377 |
378 | char *yy_ch_buf; /* input buffer */
379 | char *yy_buf_pos; /* current position in input buffer */
380 |
381 | /* Size of input buffer in bytes, not including room for EOB
382 | * characters.
383 | */
384 | int yy_buf_size;
385 |
386 | /* Number of characters read into yy_ch_buf, not including EOB
387 | * characters.
388 | */
389 | int yy_n_chars;
390 |
391 | /* Whether we "own" the buffer - i.e., we know we created it,
392 | * and can realloc() it to grow it, and should free() it to
393 | * delete it.
394 | */
395 | int yy_is_our_buffer;
396 |
397 | /* Whether this is an "interactive" input source; if so, and
398 | * if we're using stdio for input, then we want to use getc()
399 | * instead of fread(), to make sure we stop fetching input after
400 | * each newline.
401 | */
402 | int yy_is_interactive;
403 |
404 | /* Whether we're considered to be at the beginning of a line.
405 | * If so, '^' rules will be active on the next match, otherwise
406 | * not.
407 | */
408 | int yy_at_bol;
409 |
410 | int yy_bs_lineno; /**< The line count. */
411 | int yy_bs_column; /**< The column count. */
412 |
413 | /* Whether to try to fill the input buffer when we reach the
414 | * end of it.
415 | */
416 | int yy_fill_buffer;
417 |
418 | int yy_buffer_status;
419 |
420 | };
421 | #endif /* !YY_STRUCT_YY_BUFFER_STATE */
422 |
423 | void yyrestart ( FILE *input_file , yyscan_t yyscanner );
424 | void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
425 | YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
426 | void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
427 | void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
428 | void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
429 | void yypop_buffer_state ( yyscan_t yyscanner );
430 |
431 | YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
432 | YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
433 | YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
434 |
435 | void *yyalloc ( yy_size_t , yyscan_t yyscanner );
436 | void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
437 | void yyfree ( void * , yyscan_t yyscanner );
438 |
439 | /* Begin user sect3 */
440 |
441 | #define _sf1_yywrap(yyscanner) (/*CONSTCOND*/1)
442 | #define YY_SKIP_YYWRAP
443 |
444 | #define yytext_ptr yytext_r
445 |
446 | #ifdef YY_HEADER_EXPORT_START_CONDITIONS
447 | #define INITIAL 0
448 |
449 | #endif
450 |
451 | #ifndef YY_NO_UNISTD_H
452 | /* Special case for "unistd.h", since it is non-ANSI. We include it way
453 | * down here because we want the user's section 1 to have been scanned first.
454 | * The user has a chance to override it with an option.
455 | */
456 | #include
457 | #endif
458 |
459 | #ifndef YY_EXTRA_TYPE
460 | #define YY_EXTRA_TYPE void *
461 | #endif
462 |
463 | int yylex_init (yyscan_t* scanner);
464 |
465 | int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
466 |
467 | /* Accessor methods to globals.
468 | These are made visible to non-reentrant scanners for convenience. */
469 |
470 | int yylex_destroy ( yyscan_t yyscanner );
471 |
472 | int yyget_debug ( yyscan_t yyscanner );
473 |
474 | void yyset_debug ( int debug_flag , yyscan_t yyscanner );
475 |
476 | YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
477 |
478 | void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
479 |
480 | FILE *yyget_in ( yyscan_t yyscanner );
481 |
482 | void yyset_in ( FILE * _in_str , yyscan_t yyscanner );
483 |
484 | FILE *yyget_out ( yyscan_t yyscanner );
485 |
486 | void yyset_out ( FILE * _out_str , yyscan_t yyscanner );
487 |
488 | int yyget_leng ( yyscan_t yyscanner );
489 |
490 | char *yyget_text ( yyscan_t yyscanner );
491 |
492 | int yyget_lineno ( yyscan_t yyscanner );
493 |
494 | void yyset_lineno ( int _line_number , yyscan_t yyscanner );
495 |
496 | int yyget_column ( yyscan_t yyscanner );
497 |
498 | void yyset_column ( int _column_no , yyscan_t yyscanner );
499 |
500 | YYSTYPE * yyget_lval ( yyscan_t yyscanner );
501 |
502 | void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner );
503 |
504 | /* Macros after this point can all be overridden by user definitions in
505 | * section 1.
506 | */
507 |
508 | #ifndef YY_SKIP_YYWRAP
509 | #ifdef __cplusplus
510 | extern "C" int yywrap ( yyscan_t yyscanner );
511 | #else
512 | extern int yywrap ( yyscan_t yyscanner );
513 | #endif
514 | #endif
515 |
516 | #ifndef yytext_ptr
517 | static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
518 | #endif
519 |
520 | #ifdef YY_NEED_STRLEN
521 | static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
522 | #endif
523 |
524 | #ifndef YY_NO_INPUT
525 |
526 | #endif
527 |
528 | /* Amount of stuff to slurp up with each read. */
529 | #ifndef YY_READ_BUF_SIZE
530 | #ifdef __ia64__
531 | /* On IA-64, the buffer size is 16k, not 8k */
532 | #define YY_READ_BUF_SIZE 16384
533 | #else
534 | #define YY_READ_BUF_SIZE 8192
535 | #endif /* __ia64__ */
536 | #endif
537 |
538 | /* Number of entries by which start-condition stack grows. */
539 | #ifndef YY_START_STACK_INCR
540 | #define YY_START_STACK_INCR 25
541 | #endif
542 |
543 | /* Default declaration of generated scanner - a define so the user can
544 | * easily add parameters.
545 | */
546 | #ifndef YY_DECL
547 | #define YY_DECL_IS_OURS 1
548 |
549 | extern int yylex \
550 | (YYSTYPE * yylval_param , yyscan_t yyscanner);
551 |
552 | #define YY_DECL int yylex \
553 | (YYSTYPE * yylval_param , yyscan_t yyscanner)
554 | #endif /* !YY_DECL */
555 |
556 | /* yy_get_previous_state - get the state just before the EOB char was reached */
557 |
558 | #undef YY_NEW_FILE
559 | #undef YY_FLUSH_BUFFER
560 | #undef yy_set_bol
561 | #undef yy_new_buffer
562 | #undef yy_set_interactive
563 | #undef YY_DO_BEFORE_ACTION
564 |
565 | #ifdef YY_DECL_IS_OURS
566 | #undef YY_DECL_IS_OURS
567 | #undef YY_DECL
568 | #endif
569 |
570 | #ifndef _sf1_yy_create_buffer_ALREADY_DEFINED
571 | #undef yy_create_buffer
572 | #endif
573 | #ifndef _sf1_yy_delete_buffer_ALREADY_DEFINED
574 | #undef yy_delete_buffer
575 | #endif
576 | #ifndef _sf1_yy_scan_buffer_ALREADY_DEFINED
577 | #undef yy_scan_buffer
578 | #endif
579 | #ifndef _sf1_yy_scan_string_ALREADY_DEFINED
580 | #undef yy_scan_string
581 | #endif
582 | #ifndef _sf1_yy_scan_bytes_ALREADY_DEFINED
583 | #undef yy_scan_bytes
584 | #endif
585 | #ifndef _sf1_yy_init_buffer_ALREADY_DEFINED
586 | #undef yy_init_buffer
587 | #endif
588 | #ifndef _sf1_yy_flush_buffer_ALREADY_DEFINED
589 | #undef yy_flush_buffer
590 | #endif
591 | #ifndef _sf1_yy_load_buffer_state_ALREADY_DEFINED
592 | #undef yy_load_buffer_state
593 | #endif
594 | #ifndef _sf1_yy_switch_to_buffer_ALREADY_DEFINED
595 | #undef yy_switch_to_buffer
596 | #endif
597 | #ifndef _sf1_yypush_buffer_state_ALREADY_DEFINED
598 | #undef yypush_buffer_state
599 | #endif
600 | #ifndef _sf1_yypop_buffer_state_ALREADY_DEFINED
601 | #undef yypop_buffer_state
602 | #endif
603 | #ifndef _sf1_yyensure_buffer_stack_ALREADY_DEFINED
604 | #undef yyensure_buffer_stack
605 | #endif
606 | #ifndef _sf1_yylex_ALREADY_DEFINED
607 | #undef yylex
608 | #endif
609 | #ifndef _sf1_yyrestart_ALREADY_DEFINED
610 | #undef yyrestart
611 | #endif
612 | #ifndef _sf1_yylex_init_ALREADY_DEFINED
613 | #undef yylex_init
614 | #endif
615 | #ifndef _sf1_yylex_init_extra_ALREADY_DEFINED
616 | #undef yylex_init_extra
617 | #endif
618 | #ifndef _sf1_yylex_destroy_ALREADY_DEFINED
619 | #undef yylex_destroy
620 | #endif
621 | #ifndef _sf1_yyget_debug_ALREADY_DEFINED
622 | #undef yyget_debug
623 | #endif
624 | #ifndef _sf1_yyset_debug_ALREADY_DEFINED
625 | #undef yyset_debug
626 | #endif
627 | #ifndef _sf1_yyget_extra_ALREADY_DEFINED
628 | #undef yyget_extra
629 | #endif
630 | #ifndef _sf1_yyset_extra_ALREADY_DEFINED
631 | #undef yyset_extra
632 | #endif
633 | #ifndef _sf1_yyget_in_ALREADY_DEFINED
634 | #undef yyget_in
635 | #endif
636 | #ifndef _sf1_yyset_in_ALREADY_DEFINED
637 | #undef yyset_in
638 | #endif
639 | #ifndef _sf1_yyget_out_ALREADY_DEFINED
640 | #undef yyget_out
641 | #endif
642 | #ifndef _sf1_yyset_out_ALREADY_DEFINED
643 | #undef yyset_out
644 | #endif
645 | #ifndef _sf1_yyget_leng_ALREADY_DEFINED
646 | #undef yyget_leng
647 | #endif
648 | #ifndef _sf1_yyget_text_ALREADY_DEFINED
649 | #undef yyget_text
650 | #endif
651 | #ifndef _sf1_yyget_lineno_ALREADY_DEFINED
652 | #undef yyget_lineno
653 | #endif
654 | #ifndef _sf1_yyset_lineno_ALREADY_DEFINED
655 | #undef yyset_lineno
656 | #endif
657 | #ifndef _sf1_yyget_column_ALREADY_DEFINED
658 | #undef yyget_column
659 | #endif
660 | #ifndef _sf1_yyset_column_ALREADY_DEFINED
661 | #undef yyset_column
662 | #endif
663 | #ifndef _sf1_yywrap_ALREADY_DEFINED
664 | #undef yywrap
665 | #endif
666 | #ifndef _sf1_yyget_lval_ALREADY_DEFINED
667 | #undef yyget_lval
668 | #endif
669 | #ifndef _sf1_yyset_lval_ALREADY_DEFINED
670 | #undef yyset_lval
671 | #endif
672 | #ifndef _sf1_yyget_lloc_ALREADY_DEFINED
673 | #undef yyget_lloc
674 | #endif
675 | #ifndef _sf1_yyset_lloc_ALREADY_DEFINED
676 | #undef yyset_lloc
677 | #endif
678 | #ifndef _sf1_yyalloc_ALREADY_DEFINED
679 | #undef yyalloc
680 | #endif
681 | #ifndef _sf1_yyrealloc_ALREADY_DEFINED
682 | #undef yyrealloc
683 | #endif
684 | #ifndef _sf1_yyfree_ALREADY_DEFINED
685 | #undef yyfree
686 | #endif
687 | #ifndef _sf1_yytext_ALREADY_DEFINED
688 | #undef yytext
689 | #endif
690 | #ifndef _sf1_yyleng_ALREADY_DEFINED
691 | #undef yyleng
692 | #endif
693 | #ifndef _sf1_yyin_ALREADY_DEFINED
694 | #undef yyin
695 | #endif
696 | #ifndef _sf1_yyout_ALREADY_DEFINED
697 | #undef yyout
698 | #endif
699 | #ifndef _sf1_yy_flex_debug_ALREADY_DEFINED
700 | #undef yy_flex_debug
701 | #endif
702 | #ifndef _sf1_yylineno_ALREADY_DEFINED
703 | #undef yylineno
704 | #endif
705 | #ifndef _sf1_yytables_fload_ALREADY_DEFINED
706 | #undef yytables_fload
707 | #endif
708 | #ifndef _sf1_yytables_destroy_ALREADY_DEFINED
709 | #undef yytables_destroy
710 | #endif
711 | #ifndef _sf1_yyTABLES_NAME_ALREADY_DEFINED
712 | #undef yyTABLES_NAME
713 | #endif
714 |
715 | #line 84 "src/lexer.l"
716 |
717 |
718 | #line 719 "src/derived-lexer.h"
719 | #undef _sf1_yyIN_HEADER
720 | #endif /* _sf1_yyHEADER_H */
721 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/yonhan3/systemf/actions)
2 | | Please Note: Systemf() is 0.9 Beta |
3 | | ------------------------------------- |
4 | | Systemf() is at the maturity level that it can be tested by interested parties. See [Future Work](#future-work) below for more information as to what the MVP for version 1.0 would be. |
5 |
6 | # systemf
7 | Prepared statement support for the system command.
8 |
9 |
10 | ## synopsis
11 |
12 | #include
13 |
14 | int systemf1(const char *fmt, ...);
15 |
16 | ## Features
17 |
18 | 1. Calls directly to execv() instead of /bin/sh
19 | 2. Uses a format string to break arguments into parameters.
20 | 3. Uses printf like parameters to build the command.
21 | 4. Support for limited shell capabilities like piping, redirecting, and running multiple commands in one call.
22 | 5. File globbing support.
23 | 6. File sandboxing.
24 | 7. Output capture (still in development).
25 |
26 | ## Example,
27 |
28 | Consider a simple command that takes user input and calls system with it. Without systemf() you would have to do this:
29 |
30 | ```
31 | int example_func(char *user_input) {
32 | char fmt[] = "/bin/mymagicfunc %s";
33 | char *buf = malloc(sizeof(fmt) + strlen(user_input));
34 | int result;
35 | sprintf(buf, fmt, user_input);
36 |
37 | if (buf == NULL) {
38 | return -1;
39 | }
40 | result = system(buf);
41 | free(buf);
42 | return result;
43 | }
44 | ```
45 | With systemf, all you would have to do is this:
46 | ```
47 | int example_func(char *user_input) {
48 | return systemf1("/bin/mymagicfunc %s", user_input);
49 | }
50 | ```
51 |
52 | But that isn't the reason systemf() was created (but it is a great advantage).
53 | There is a big security advantage. With `systemf()`,
54 | user_input is sent as a single argument and since `systemf()` has its own limit
55 | parsing capabilities, `/bin/sh` is not involved at all. So if they did something
56 | like, `user_input = "goodbye ; rm -rf /"`, the first example would try to execute
57 | the `rm -rf /` while the second would just send the whole string as a single
58 | argument to /bin/mymagicfunc.
59 |
60 | This doesn't solve everything. If `/bin/mymagicfunc` had an injection issue, it might still cause the code to be run, but you can't prevent everything.
61 |
62 | ## Quick Tour of Through Examples
63 |
64 | The easiest way to explain the basics how `systemf1()` works is through a few examples.
65 |
66 | **Example 1: Basics**
67 |
68 | * `systemf1("/bin/echo The cat%s %d tail%s.", "'', tails, tails == 1 ? "" : "s");`
69 |
70 | In the above example, `systemf1()` takes the format input, breaks it into parameters
71 | by the spaces in each command, and sends it to execv. For example if `tails`
72 | were 2, it would call execv with these arguments:
73 | `["/bin/echo", "The", "cat's", "2", "tails."]`. Also, note that `systemf1()`
74 | doesn't support `'` in the format string. This is becuase `systemf1()` supports
75 | no quoting or escaping.
76 |
77 | **Example 2: Parameter Splitting**
78 |
79 | Now take the following call to `systemf1()` into account:
80 |
81 | `systemf1("/bin/echo %s", "this line has spaces");`
82 |
83 | `systemf1()` only breaks lines into parameters by spaces and glob expansion. So in the above case, the arguments to execv will be: `["/bin/echo", "this line has spaces"]` and it would **not** break the %s into spaces.
84 |
85 | **Example 3: File Globbing**
86 |
87 | `systemf1()` also supports file globbing in the format string and as a glob paramerter, but not as a string. Consider these three variations:
88 |
89 | 1. `systemf1("/bin/echo *.c");`
90 | 2. `systemf1("/bin/echo %*p", "*.c");`
91 | 3. `systemf1("/bin/echo %s", "*.c");`
92 |
93 | The first two will find every `c` file in the current directory and pass those as individual parameters to execv. `["/bin/echo", "a.c", "b.c", "c.c"]`. While the third will send the text in verbatim: `["/bin/echo", "*.c"]` and since `echo` does not do glob expansion, literally `*.c` will be printed.
94 |
95 | There are a caveats to the above. If the glob pattern matches nothing, the
96 | processing will stop, an error message will be printed, and `-1` will be returned.
97 |
98 | Also, note that `systemf1()` supports filename sandboxing. That is a more advanced
99 | subject than this introduction. For more information see [Filename Sandboxing](#filename-sandboxing) below.
100 |
101 |
102 |
103 |
104 | ## Format String and Argument Parsing
105 |
106 | The `fmt` argument to `systemf1()` specifies the how the code will be called and
107 | allows a convenient way to bring in parameterized user input. Think of it as
108 | limited shell with most of what you would need when calling out to system, but
109 | protections from the common mistakes when calling system. The below table
110 | summarizes which characters are allowed in the format string and their meanings.
111 |
112 | | Token | Meaning |
113 | |:------------:| ------- |
114 | | `a-z` `A-Z` ` 0-9` `.` `-` | Nonspecial characters allowed in `fmt`. (0) |
115 | | *space tab* | *Spaces* and *tabs* are interpreted as parameter separators. |
116 | | `%s` | Replace this with the string in the next available argument. (5) |
117 | | `%d` | Replace this with the integer in the next available argument. (5) |
118 | | `%p` | Like `%s`, but also [filename sandboxed](#filename-sandboxing) |
119 | | `%*p` | Interpret the supplied parameter as a file glob. |
120 | | `%!p` | Like `%s`, but a trusted parameter for [filename sandboxing](#filename-sandboxing) |
121 | | `;` | Command separator run if previous command exits cleanly. |
122 | | `|` | Command separator like `;` but also pipes stdout from prev into stdin |
123 | | `&&` | Command separator run if previous command exits cleanly with zero status. |
124 | | `||` | Command separator run if previous command exits cleanly with nonzero status. |
125 | | `<`*file* | Supply the stdin from the specified *file*. (1)(2) |
126 | | `>`*file* | Redirect the stdout into the specified *file*. (1)(2) |
127 | | `>>`*file* | Append the stdout into the specified *file*. (1)(3) |
128 | | `2>`*file* | Redirect the stderr into the specified *file*. (1)(2) |
129 | | `2>>`*file* | Append the stderr into the specified *file*. (1)(3) |
130 | | `>&2` | Redirect the stdout into the stderr. (4) |
131 | | `2>&1` | Redirect stderr into stdout. (4) |
132 | | `&>`*file* | Redirect stderr and stdout into the specified *file*. (1)(2) |
133 | | `&>>`*file* | Append stderr and stdout into the specified *file*. (1)(2) |
134 |
135 | - (0) All tokens below in the table take precedence during parsing.
136 | - (1) There is an optional space between the redirect and the filename.
137 | - (2) Replace the file if it exists.
138 | - (3) Create the file if it does not exist.
139 | - (4) `systemf1()` currently has no support of swapping the stdout and stderr.
140 | - (5) Currently, no formatting specifiers are supported (like `%5d` or `%-10s`)
141 |
142 | ### File Sandboxing
143 |
144 | `systemf` has a non-obtrusive filename sandboxing system. Before each process is executed, all arguments for that command are run through the following steps.
145 |
146 | 1. Determine which arguments are known file references with untrusted data.
147 | 2. Determine the trusted part of that argument's path.
148 | 3. Verify that the final paths are contained in the trusted path.
149 |
150 | #### 1. Determine which arguments are file references with untrusted data.
151 |
152 | Systemf looks at each argument before the commands is launched and determines
153 | if they are candidates for filename sandboxing. An argument is a candidate if it contains any of the following:
154 |
155 | 1. Wildcards in the format string. (`[]`, `*`, `?`)
156 | 2. Any `%p` parameter. (`%p`, `%!p`, `%*p`)
157 |
158 | The target must also include untrusted data. That can be any of `%d`, `%s`, `%p`, `%*p`. `%!p`, on the other hand, is always trusted.
159 |
160 | #### 2. Determine the trusted path of that argument.
161 |
162 | If an argument is deemed a file reference, it is next scanned up for the longest
163 | trusted path not including wildcards. A path is trusted if it is a part of the
164 | `fmt` string, or if it is a %!p. This is best shown through examples.
165 |
166 | | # | Command | Trusted Path |
167 | | - | ------- | ------------ |
168 | | 1 | `systemf1("cmd ./a/b/c*/%s", "untrusted");` | `./a/b/` |
169 | | 2 | `systemf1("cmd ./a/b/c%s/*", "untrusted");` | `./a/b/` |
170 | | 3 | `systemf1("cmd %p/x/y", "untrusted");` | `./` |
171 | | 4 | `systemf1("cmd %!p/x/y", "trusted");` | *NA* |
172 | | 5 | `systemf1("cmd %s/x/y/%!p", "untrusted", "trusted");` | `./` |
173 | | 6 | `systemf1("cmd %s/x/y", "untrusted");` | *NA* |
174 | | 7 | `systemf1("./a/%s %s/x/y", "untrusted");` | `./a/` |
175 |
176 | 1. Since the path at `/c*` contains a wildcard it is not included in the trusted path.
177 | 2. Since `/c%s` contains untrusted data, it is not included in the path.
178 | 3. Since `%p` is untrusted and at the very beginning, the current directory is assumed.
179 | 4. Since `%!p` is trusted, this contains no untrusted data and is not sandboxed.
180 | 5. Even though there is a `%!p` at the end, the untrusted `%s` at the beginning forces a trusted path to the current directory.
181 | 6. Since there are no wildcards or `%p` variants, this is not considered a path even if it probably is.
182 | 7. This demonstrates sandboxing also happens on the command itself. The command must live in a subdirectory of `a`.
183 |
184 | #### 3. Verify that the final paths are contained in the trusted path.
185 |
186 | The final step is to verify that all generated paths are contained in the trusted path. This is done by collapsing all `..` patterns in the paths and verfying that
187 | the trusted path and the beginning of each path matches.
188 |
189 | Consider the following code:
190 | ```
191 | systemf1("mkdir -p a/b/c/%p", "../../b/c/f");
192 | ```
193 | the underlying `systemf` code will have to decide if `a/b/c/../../b/c/f` exists in the trusted path, `a/b/c/`. I collapses the latter to `a/b/c/f` and determines that it matches the trusted path.
194 |
195 | There were some consideration of preventing symbolic links from causing an escape of the sandbox, but ultimately the confusion added by such a change was greater than the security benefilts. See [No Plan for Chroot Jail Equivalence for Filename Sandboxing](#no-plan-for-chroot-jail-equivalence-for-filename-sandboxing) for more details.
196 |
197 |
198 | ## Return Values
199 |
200 | The base systemf1() will have the same return values as the system() function.
201 |
202 | The following is copied from http://man7.org/linux/man-pages/man3/system.3.html :
203 |
204 | * If any child process could not be created, or its status could not
205 | be retrieved, the return value is -1 and errno is set to indicate
206 | the error.
207 |
208 | * If all spawned child processes succeed, then the return value is the
209 | termination status of the last spawned child process.
210 |
211 | ## Why is There a "1" in the Systemf1 Name?
212 |
213 | The driving force behind developing `systemf` was to have a more secure system. It seems that as a tool becomes more popular, it gets more security scrutiny. The developers of `systemf` expect that they will have missed something fundamental that will require a non-backward compatible change to the code. When that day comes, they will have to choose between breaking code and creating new functions.
214 |
215 | By making the systemf versioned in its name, it is more obvious the version that is being run and gives for a more graceful transition. Perhaps one day, when the implementation is considered rock-solid, the final version can drop the numbering system altogether. (Any bets how long it will take to detect a new fatal security vulnerability in the design after that step is taken?)
216 |
217 | ## Future Work
218 |
219 | Systemf is now a minimal viable product with the release of 1.0. The following feature enhancement
220 | are planned for future minor version releases.
221 |
222 | | Title | Description |
223 | | ----- | ----------- |
224 | | [PATH Support](#path-support) | Currently all executables must include a path. This will add limited path searching and updating. |
225 | | [Capture Support](#capture-support) | Functions that allow for capturing the standard output and standard error to strings. |
226 | | [STDIN String & File Support](#stdin-string-and-file-support) | Functions that allow for a string or buffers to be suppled for the standard input. |
227 | | [Error Message Redirection](#error-message-redirection) | Redirect stderr messages from `systemf` itself. |
228 |
229 | ### Features not currently planned.
230 |
231 | These features require more discussion and some highly needed use cases to be added.
232 |
233 | | Title | Description |
234 | | ----- | ----------- |
235 | | [Background Support](#no-plan-for-background-support) | Running commands in the background. |
236 | | [variables](#no-plan-for-variable-support) | Variable expansion like $HOME or ~ may not be supported. |
237 | | [variable cleaning](#no-plan-for-variable-cleaning) | Other than PATH, no other environment variables will be reset (like IFS). |
238 | | [chroot equivalence](#no-plan-for-chroot-jail-equivalence-for-filename-sandboxing) | No plan for chroot jail equivalence for filename sandboxing
239 |
240 |
241 |
242 | ### PATH Support
243 | **Still being developed.**
244 |
245 | `Systemf` protects against [CWE-426: Untrusted Search Path](https://cwe.mitre.org/data/definitions/426.html) by ignoring the PATH environment variable and trusting a limit path. For systems that supply it, `confstr(_CS_PATH, ...)` will be used. For other systems, the `./configure` will need to determine this.
246 |
247 | Each executable will have the ability to augment this path for that executable with the command `systemf1_update_path(path, location)` where `path` is a colon separated list of directories and location can be one of `SYSTEMF1_PATH_PREPEND`, `SYSTEMF1_PATH_APPEND`, and `SYSTEMF1_PATH_REPLACE`.
248 | Systemf by default only allows absolute paths and a very limited PATH parsing.
249 |
250 | ### Capture Support
251 | **Still being developed.**
252 |
253 | `Systemf` will support capturing the standard output to strings. These strings may either be supplied or allocated. There are two base commands for this:
254 |
255 | ```
256 | systemf1_capture_rtn systemf1_capture(
257 | char *stdout_buf,
258 | size_t max_stdout_buf_len,
259 | char *stderr_buf,
260 | size_t max_stderr_buf_len,
261 | fmt,
262 | ...);
263 |
264 | systemf1_capture_rtn *systemf1_capture_a(
265 | size_t max_stdout_buf_len,
266 | size_t max_stderr_buf_len,
267 | fmt,
268 | ...);
269 | ```
270 |
271 | `systemf1_capture_rtn` contains information about the captured results. For the former, it will be passed back by value and the latter, it will be allocated and returned. A single `free()` of the latter will be required because the captured data will be opaquely appended to the structure. The structure will contain the following fields:
272 |
273 | | Field | Description |
274 | | ----- | ----------- |
275 | | stdout | A buffer pointer containing the standard output. `out_buf[out_buf_len] will always contain the nul terminator. |
276 | | stdout_len | The number of characters written to out_buf excluding the nul terminator. This will never be greater than max_out_buf_len - 1 |
277 | | stdout_total | The number of total bytes received from the stdout if `max_stdout_buf_len` were infinite. |
278 | | stderr | Similar to `stdout` but for stderr |
279 | | stderr_len | Similar to `stdout_len` but for `stderr` |
280 | | stderr_total | Similar to `stdout_total` but for `stderr` |
281 | | retval | The same return value as `systemf1()` would normally return. |
282 |
283 | There are some corner cases for `systemf1_capture()`.
284 | * If `stdout_buf` is NULL, `max_stdout_buf_len` will be ignored. The returned stdout will be NULL and `stdout_len` will be zero, but `stdout_total` will be accurate.
285 | * If `max_stdout_buf_len` is 0, the code will act as if `stdout_buf` were NULL.
286 | * The same corner cases exist for `stderr_buf` and `max_stderr_buf_len`.
287 |
288 | There are some corner cases for `systemf1_capture_a()`.
289 | * A `max_stdout_buf_len` of 0 considered to be equivalent to a length of 1. A one byte buffer will be allocated and returned filled with a nul value. Infinite buffer size is not supported.
290 | * The same corner cases exist for `max_stderr_buf_len`.
291 |
292 | ### Stdin String and File Support
293 | **Still being developed.**
294 |
295 | Varients of the systemf1 suite will take either a string, a buffer, or a FILE pointer and use that as the standard input. These variants will take one of the above as their first argument and in the case of the buffer, a length argument.
296 | This will create a wide varienty of new functions:
297 |
298 | ```
299 | systemf1_sin(char *string, ...);
300 | systemf1_bin(char *buf, buflen,...);
301 | systemf1_fin(FILE *file, ...);
302 |
303 | systemf1_sin_capture(char *string, ...);
304 | systemf1_bin_capture(char *buf, buflen, ...);
305 | systemf1_fin_capture1(FILE *file, ...);
306 |
307 | systemf1_sin_capture_a(char *string, ...);
308 | systemf1_bin_capture_a(char *buf, buflen, ...);
309 | systemf1_fin_capture_a(FILE *file, ...);
310 | ```
311 |
312 | ### Error Message Redirection
313 | **Still being developed.**
314 |
315 | `systemf` will print error messages to the standard error in some situations. These include invalid format strings, sandboxing violations, commands not found, and file globbing problems. Global setting command `systemf1_log_to(FILE *file)` will be added. It will return the current log. Supplying `file=NULL` completely disables logging. This does not affect the normal stderr and stdout processing of the commands themselves.
316 |
317 | ### No Plan for Background Support
318 |
319 | The standard shell contains facilities to run commands in the background with the
320 | `&` parameter. The main reason this is not currently planned is that a much better understanding of how the shell handles the processing in needed. This support is likely to be the most requested of all the current "No Plan" items.
321 |
322 | ### No Plan for Variable Support
323 |
324 | `Systemf` does not support variable expansion in the format string. Thus,
325 | `systemf1("/bin/echo $HOME")` will cause a parse error because `$` is not supported
326 | in the format string. There were two reasons not to support this. The first was
327 | that it simple added complexity to the system when the developers were uncertain
328 | how needed such a capability was. The second was that if such a capability is
329 | added, all security ramification should be considered and discussed first.
330 |
331 | For now, there is no variable support. In the future, there may be.
332 |
333 | ### No Plan for Variable Cleaning
334 |
335 | The SEI CERT C Coding Standard includes [ENV03-C Sanitize the environment when invoking external programs](https://wiki.sei.cmu.edu/confluence/display/c/ENV03-C.+Sanitize+the+environment+when+invoking+external+programs) and its examples include such items as resetting environment variables such as the PATH. Because it is such a common vulnerability, `Systemf` does sanitize the PATH. But because the library isn't able to guess every variation of needs of environment variables, it does not sanitize other variables.
336 |
337 | ### No Plan for Chroot Jail Equivalence for Filename Sandboxing
338 |
339 | Filename sandboxing is a feature in `systemf` that prevents user input from escaping outside of the current file path. It works well for the most basic operations, but it lacks the protections that a chroot jail might have. As an example, consider a chroot located in `/jail/`. Unless mounted within the jail, the path `/somewhere-else/` is not accessable.
340 |
341 | If someone instead simply launched code with `systemf` in it from `/newpath/` and
342 | in this contrived example, there was a `/newpath/escape/` which was a symbolic link
343 | to `/somewhere-else/` (`cd /newpath; ln -s /somewhere-else/ escape`) a call to
344 | `systemf1("./%p", "escape/bin/bam");`, the symbolic link would be followed and
345 | the executable in `/somewhere-else/bin/bam` could be run.
346 |
347 | In the chroot jail case, the symbolic link couldn't be followed because it would be considered to point to the external path: `/newpath/somewhere-else` that does not
348 | exist.
349 |
350 | It was considered that to get around this with filename sandboxing, the realpaths of
351 | the trusted path and the full path could be compared. It was decided that this
352 | would add too much complexity, be hard to explain, confuse the users, and prevent real needs to follow symbolic links. For these reasons, this approach for such
353 | a corner case was abandoned.
354 |
355 |
356 | ## Building
357 |
358 | To build, run `./configure && make build`.
359 |
360 | If you are a developer of `systemf`, see the [developer instructions](DEVELOP.md).
361 |
362 | ## Issues and feature requests.
363 |
364 | Systemf is currently in a [temporary location](https://github.com/yonhan3/systemf).
365 | Issues may be raised [there](https://github.com/yonhan3/systemf/issues), but may
366 | not get transferred to its [permanant home](https://github.com/cisco/systemf).
367 |
--------------------------------------------------------------------------------