├── .gitignore ├── .travis.yml ├── GNUmakefile ├── LICENSE.txt ├── Makefile.am ├── README.md ├── build-aux ├── Makefile.am ├── yuck-coru.c.m4 ├── yuck-coru.h.m4 ├── yuck-scmver.c ├── yuck-scmver.h ├── yuck-scmver.m4 ├── yuck.am ├── yuck.c ├── yuck.m4 ├── yuck.man.m4 ├── yuck.mk └── yuck.yuck ├── configure.ac ├── info ├── Makefile.am └── author.h2m ├── m4 ├── clitoris.m4 ├── sxe-compiler.m4 ├── sxe-linker.m4 └── yuck.m4 ├── snap └── snapcraft.yaml ├── src ├── Makefile.am ├── nifty.h ├── sample.c ├── sample.yuck ├── version.c.in └── version.h ├── test ├── Makefile.am ├── clitoris.am ├── clitoris.c ├── clitoris.yuck ├── sample_01.clit ├── sample_02.clit ├── sample_03.clit ├── sample_04.clit ├── sample_05.clit ├── sample_06.clit ├── sample_07.clit ├── sample_08.clit ├── sample_09.clit ├── sample_10.clit ├── sample_11.clit ├── sample_12.clit ├── sample_13.clit ├── sample_14.clit ├── sample_15.clit ├── sample_16.clit ├── sample_17.clit ├── sample_18.clit ├── sample_19.clit ├── sample_20.clit ├── sample_21.clit ├── sample_22.clit ├── sample_23.clit ├── sample_24.clit ├── sample_25.clit ├── sample_26.clit ├── sample_27.clit └── sample_28.clit └── version.mk.in /.gitignore: -------------------------------------------------------------------------------- 1 | aclocal.m4 2 | autom4te.cache 3 | configure 4 | ltmain.sh 5 | m4/libtool.m4 6 | m4/lt*.m4 7 | Makefile.in 8 | config.guess 9 | config.sub 10 | install-sh 11 | missing 12 | src/config.h.in 13 | *~ 14 | =build*/ 15 | build/ 16 | depcomp 17 | ++* 18 | Makefile 19 | .version 20 | config.log 21 | config.status 22 | libtool 23 | *.o 24 | config.h 25 | stamp-h1 26 | .deps 27 | .libs 28 | test-driver 29 | compile 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | compiler: 8 | - gcc 9 | - clang 10 | 11 | before_script: 12 | - autoreconf -vfi 13 | 14 | script: 15 | - ./configure || { cat config.log; false; } 16 | - make 17 | - if test "${TRAVIS_OS_NAME}" = "linux"; then 18 | make distcheck; 19 | else 20 | make check; 21 | fi 22 | || { cat test/test-suite.log sample-*/_build/test/test-suite.log; false; } 23 | 24 | deploy: 25 | provider: releases 26 | api_key: 27 | secure: "OTjY5FuRKj3EGC9Cw7Wi+DllGN8odQWfpcyuN7f9RVJW/wYUfpuZ2mXnXYCi0KtgmSMvQffCH5nLv9jlwbtup+ChWRuscftUtnMkmEo03l03k6q5uO5Avi1m9numCL9tAo0zuWweCrR2pNZMarvKl3IMSym88E+PTqubWD/vFx/QVQhXtuZ36U+kDoZS9Dh4E8C98QoJp3dYalweF0FjUSR1emC3YFEuKgNfrgEzA/+IXkOj671326p65mZ2UCwOU3tziSn/QsF6+c6dO8rcTTVYu00SZxO6laQU6y5WT6fcRkIlyGbGgw+xM/Ad7gdQk1StJV0MS06Wqc/zt02SFnAT5QEcC3v1ZjLr6Jw8HELw0LMISootY7N88KgTzTcXcj2020PNqRH438v/r71A1csKAxpfi0hUwEohVsBu+XbPynmWKb47O1OY2B9g+PiwJKwks4AwkRo9oGD8/6hwqe0YtJXYhbpwNw//A284ChB2nciLlgCTWb1Z6oECXckCctNDlFXytmH8oHnCokLMbC3w8lB1oCvcXiMU2A7Wol8qi1JN+uMXxJ2V6KV5yrgO5vzakR2wkxvWlqXRVjEmi+WZshGqUWProE2SxntRSonsjN6iZb3Yl5gk6P5FYn/3XybJ9WIkXiL5o5J8UVpIafLRcAPJD0NyW8T4u1VbuG8=" 28 | file_glob: true 29 | file: sample-*.tar.xz 30 | skip_cleanup: true 31 | on: 32 | tags: true 33 | 34 | notifications: 35 | email: 36 | - devel@fresse.org 37 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # This GNUmakefile is used only if you run GNU Make. 2 | 3 | # If the user runs GNU make but has not yet run ./configure, 4 | # give them a diagnostic. 5 | _gl-Makefile := $(wildcard [M]akefile) 6 | ifneq ($(_gl-Makefile),) 7 | 8 | _dist-target_p ?= $(filter-out %clean, $(filter dist%,$(MAKECMDGOALS))) 9 | 10 | include Makefile 11 | 12 | # update the included makefile snippet which sets VERSION variables 13 | version.mk: .version version.mk.in FORCE 14 | -$(AM_V_GEN) \ 15 | if test "$(MAKECMDGOALS)" != "am--refresh"; then \ 16 | $(MAKE) -C "$(top_builddir)/build-aux" $(MAKECMDGOALS); \ 17 | PATH="$(top_builddir)/build-aux:$${PATH}" \ 18 | yuck scmver --ignore-noscm -o $@ --reference $^; \ 19 | if test $$? -eq 3 -a -n "$(_dist-target_p)"; then \ 20 | exec $(MAKE) $(MAKECMDGOALS); \ 21 | fi; \ 22 | fi 23 | 24 | else 25 | 26 | .DEFAULT_GOAL := abort-due-to-no-makefile 27 | $(MAKECMDGOALS): abort-due-to-no-makefile 28 | 29 | abort-due-to-no-makefile: 30 | @echo There seems to be no Makefile in this directory. 1>&2 31 | @echo "You must run ./configure before running 'make'." 1>&2 32 | exit 1 33 | 34 | endif 35 | 36 | .PHONY: FORCE 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Sebastian Freundt 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | include $(top_builddir)/version.mk 2 | 3 | LANG = C 4 | LC_ALL = C 5 | 6 | ACLOCAL_AMFLAGS = -I m4 7 | 8 | SUBDIRS = 9 | EXTRA_DIST = $(DISTCLEANFILES) $(doc_DATA) 10 | DISTCLEANFILES = 11 | CLEANFILES = 12 | doc_DATA = 13 | 14 | SUBDIRS += build-aux 15 | SUBDIRS += src 16 | SUBDIRS += test 17 | SUBDIRS += info 18 | 19 | DISTCLEANFILES += version.mk 20 | DISTCLEANFILES += .version 21 | EXTRA_DIST += version.mk.in 22 | 23 | ## make sure .version is read-only in the dist 24 | dist-hook: 25 | chmod ugo-w $(distdir)/.version 26 | 27 | ## Makefile.am ends here 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sample 2 | ====== 3 | | Status | Conda Downloads | Conda Version | Platforms | 4 | | --- | --- | --- | --- | 5 | | [![Build Status](https://secure.travis-ci.org/hroptatyr/sample.png?branch=master)](http://travis-ci.org/hroptatyr/sample) | [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/sample.svg)](https://anaconda.org/conda-forge/sample) | [![Conda Version](https://img.shields.io/conda/vn/conda-forge/sample.svg)](https://anaconda.org/conda-forge/sample) | [![Conda Platforms](https://img.shields.io/conda/pn/conda-forge/sample.svg)](https://anaconda.org/conda-forge/sample) | 6 | 7 | 8 | Produce a sample of lines from files. The sample size is either fixed 9 | or proportional to the size of the file. Additionally, the header and 10 | footer can be included in the sample. 11 | 12 | 13 | Red tape 14 | -------- 15 | 16 | - no dependencies other than a POSIX system and a C99 compiler. 17 | - licensed under [BSD3c][1] 18 | 19 | 20 | Features 21 | -------- 22 | 23 | - proportional sampling of streams and files 24 | - header and footer can be included in the sample 25 | - reservoir sampling (fixed sample size) of streams and files 26 | - stable reservoir sampling (i.e. the order is preserved) 27 | 28 | 29 | Motivation 30 | ---------- 31 | 32 | Practically ubiquitous, there's `shuf -n` of [GNU coreutils][5], a tool 33 | that, in principle, solves the problem at hand. However, `shuf` buffers 34 | all input and is therefore useless for files that don't fit in memory. 35 | 36 | So, looking for alternatives one may come across [paulgb's subsample][2] 37 | or [earino's fast_sample][3]. They usually do the trick and everyone 38 | seems to agree (judged by github stars). However, both tools have 39 | short-comings: they try to make sense of the line data semantically, and 40 | secondly, they are slow! 41 | 42 | The first issue is such a major problem that their bug trackers are 43 | full of reports. `subsample` needs lines to be UTF-8 strings and 44 | `fast_sample` wants CSV files whose correctness is checked along the 45 | way. This project's tool, `sample`, on the other hand does not care 46 | about the line's content, all it needs are those line breaks at the 47 | end. 48 | 49 | The speed issue is addressed by 50 | 51 | - using the most appropriate programming language for the problem 52 | - using radix sort 53 | - using the [PCG family][4] to obtain randomness 54 | - oversampling 55 | 56 | 57 | Examples 58 | -------- 59 | 60 | To get 10 random words from the `words` file: 61 | 62 | $ sample -n 10 -H 0 /usr/share/dict/words 63 | ... 64 | benzopyrene 65 | calamondins 66 | cephalothorax 67 | copulate 68 | garbology's 69 | Kewadin 70 | Peter's 71 | reassembly 72 | Vienna's 73 | Wagnerism's 74 | ... 75 | 76 | The `-H 0` produces 0 lines of header output which defaults to 5. 77 | 78 | For proportional sampling use `-r|--rate`: 79 | 80 | $ wc -l /usr/share/dict/words 81 | 305089 82 | $ sample -r 1% /usr/share/dict/words | wc -l 83 | 3080 84 | 85 | which is close to the true result bearing in mind that by default the 86 | header and footer of the file is printed as well. 87 | 88 | Sampling with a rate of 0 replaces awkward scripts that use multios 89 | and `head` and `tail` to produce the same result. 90 | 91 | $ sample -r 0 /usr/share/dict/words 92 | A 93 | AA 94 | AAA 95 | Aachen 96 | aah 97 | ... 98 | Zyuganov 99 | Zyuganov's 100 | zyzzyva 101 | zyzzyvas 102 | ZZZ 103 | 104 | 105 | Similar projects 106 | ================ 107 | 108 | In no particular order and without any claim to completeness: 109 | 110 | + subsample: 111 | + fast_sample: 112 | + shuf: 113 | 114 | 115 | [1]: http://opensource.org/licenses/BSD-3-Clause 116 | [2]: https://github.com/paulgb/subsample 117 | [3]: https://github.com/earino/fast_sample 118 | [4]: http://www.pcg-random.org/ 119 | [5]: https://www.gnu.org/software/coreutils/coreutils.html 120 | -------------------------------------------------------------------------------- /build-aux/Makefile.am: -------------------------------------------------------------------------------- 1 | LANG = C 2 | LC_ALL = C 3 | 4 | AM_CFLAGS = $(EXTRA_CFLAGS) 5 | 6 | noinst_PROGRAMS = 7 | BUILT_SOURCES = 8 | EXTRA_DIST = 9 | DISTCLEANFILES = 10 | 11 | if !HAVE_YUCK 12 | include yuck.am 13 | endif !HAVE_YUCK 14 | 15 | ## Makefile.am ends here 16 | -------------------------------------------------------------------------------- /build-aux/yuck-coru.c.m4: -------------------------------------------------------------------------------- 1 | /* -*- c -*- */ 2 | changequote`'changequote([,])dnl 3 | changecom([#])dnl 4 | #if defined HAVE_CONFIG_H 5 | # include "config.h" 6 | #endif /* HAVE_CONFIG_H */ 7 | #if defined HAVE_VERSION_H 8 | # include "version.h" 9 | #endif /* HAVE_VERSION_H */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | changecom([])dnl 16 | ifdef([YUCK_HEADER], [dnl 17 | #include "YUCK_HEADER" 18 | ])dnl 19 | changecom([#])dnl 20 | 21 | #if defined __INTEL_COMPILER 22 | # pragma warning (push) 23 | # pragma warning (disable:177) 24 | # pragma warning (disable:111) 25 | # pragma warning (disable:3280) 26 | #elif defined __GNUC__ 27 | # if __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6 28 | # pragma GCC diagnostic push 29 | # endif /* GCC version */ 30 | # pragma GCC diagnostic ignored "-Wunused-label" 31 | # pragma GCC diagnostic ignored "-Wunused-variable" 32 | # pragma GCC diagnostic ignored "-Wunused-function" 33 | # pragma GCC diagnostic ignored "-Wshadow" 34 | #endif /* __INTEL_COMPILER */ 35 | pushdef([DEFUN], ifdef([YUCK_HEADER], [], [static]))[]dnl 36 | 37 | 38 | static inline bool 39 | yuck_streqp(const char *s1, const char *s2) 40 | { 41 | return !strcmp(s1, s2); 42 | } 43 | 44 | /* for multi-args */ 45 | static inline char** 46 | yuck_append(char **array, size_t n, char *val) 47 | { 48 | if (!(n % 16U)) { 49 | /* resize */ 50 | void *tmp = realloc(array, (n + 16U) * sizeof(*array)); 51 | if (tmp == NULL) { 52 | free(array); 53 | return NULL; 54 | } 55 | /* otherwise make it persistent */ 56 | array = tmp; 57 | } 58 | array[[n]] = val; 59 | return array; 60 | } 61 | 62 | static enum yuck_cmds_e yuck_parse_cmd(const char *cmd) 63 | { 64 | if (0) { 65 | ; 66 | }foreachq([__CMD], yuck_cmds(), [ else if (yuck_streqp(cmd, "yuck_cmd_string(defn([__CMD]))")) { 67 | return yuck_cmd(defn([__CMD])); 68 | }]) else { 69 | /* error here? */ 70 | fprintf(stderr, "YUCK_UMB_STR: invalid command `%s'\n\ 71 | Try `--help' for a list of commands.\n", cmd); 72 | } 73 | return (enum yuck_cmds_e)-1; 74 | } 75 | 76 | 77 | DEFUN int yuck_parse(yuck_t tgt[[static 1U]], int argc, char *argv[[]]) 78 | { 79 | char *op; 80 | int i; 81 | 82 | /* we'll have at most this many args */ 83 | memset(tgt, 0, sizeof(*tgt)); 84 | if ((tgt->args = calloc(argc, sizeof(*tgt->args))) == NULL) { 85 | return -1; 86 | } 87 | ifdef([YUCK_MAX_POSARGS], [], [define([YUCK_MAX_POSARGS], [(size_t)-1])])dnl 88 | for (i = 1; i < argc && tgt->nargs < YUCK_MAX_POSARGS; i++) { 89 | op = argv[[i]]; 90 | 91 | switch (*op) { 92 | case '-': 93 | /* could be an option */ 94 | switch (*++op) { 95 | default: 96 | /* could be glued into one */ 97 | for (; *op; op++) { 98 | yield(shortopt, *op); 99 | } 100 | break; 101 | case '-': 102 | if (*++op == '\0') { 103 | i++; 104 | yield(dashdash); 105 | break; 106 | } 107 | yield(longopt, op); 108 | break; 109 | case '\0': 110 | goto plain_dash; 111 | } 112 | break; 113 | default: 114 | plain_dash: 115 | yield(arg, op); 116 | break; 117 | } 118 | } 119 | if (i < argc) { 120 | op = argv[[i]]; 121 | 122 | if (*op++ == '-' && *op++ == '-' && !*op) { 123 | /* another dashdash, filter out */ 124 | i++; 125 | } 126 | } 127 | /* has to be here as the max_pargs condition might drive us here */ 128 | coroutine(dashdash) 129 | { 130 | /* dashdash loop, pile everything on tgt->args 131 | * don't check for subcommands either, this is in accordance to 132 | * the git tool which won't accept commands after -- */ 133 | for (; i < argc; i++) { 134 | tgt->args[[tgt->nargs++]] = argv[[i]]; 135 | } 136 | } 137 | return 0; 138 | 139 | coroutine(longopt) 140 | { 141 | /* split into option and arg part */ 142 | char *arg; 143 | 144 | if ((arg = strchr(op, '=')) != NULL) { 145 | /* \nul this one out */ 146 | *arg++ = '\0'; 147 | } 148 | 149 | switch (tgt->cmd) { 150 | default: 151 | yield(yuck_cmd()[_longopt]); 152 | break; 153 | foreachq([__CMD], yuck_cmds(), [case yuck_cmd(defn([__CMD])): 154 | yield(yuck_cmd(defn([__CMD]))[_longopt]); 155 | break; 156 | ])} 157 | resume; 158 | 159 | dnl TYPE actions 160 | pushdef([yuck_flag_action], [tgt->yuck_slot([$1], [$2])++; goto xtra_chk])dnl 161 | pushdef([yuck_arg_action], [tgt->yuck_slot([$1], [$2]) = arg ?: argv[[++i]]])dnl 162 | pushdef([yuck_arg_opt_action], [tgt->yuck_slot([$1], [$2]) = arg ?: YUCK_OPTARG_NONE])dnl 163 | pushdef([yuck_arg_mul_action], [tgt->yuck_slot([$1], [$2]) = 164 | yuck_append( 165 | tgt->yuck_slot([$1], [$2]), tgt->yuck_cnt_slot([$1], [$2])++, 166 | arg ?: argv[[++i]]); 167 | if (tgt->yuck_slot([$1], [$2]) == NULL) { 168 | return -1; 169 | }])dnl 170 | pushdef([yuck_arg_mul_opt_action], [tgt->yuck_slot([$1], [$2]) = 171 | yuck_append( 172 | tgt->yuck_slot([$1], [$2]), tgt->yuck_cnt_slot([$1], [$2])++, 173 | arg ?: YUCK_OPTARG_NONE); 174 | if (tgt->yuck_slot([$1], [$2]) == NULL) { 175 | return -1; 176 | }])dnl 177 | pushdef([yuck_auto_action], [/* invoke auto action and exit */ 178 | yuck_auto_[]yuck_canon([$1], [$2])(tgt); 179 | resume_at(success)])dnl 180 | 181 | foreachq([__CMD], yuck_umbcmds(), [coroutine(yuck_cmd(defn([__CMD]))[_longopt]) 182 | { 183 | if (0) { 184 | ; 185 | }dnl 186 | foreachq([__IDN], yuck_idents(defn([__CMD])), [ dnl 187 | pushdef([long], yuck_long(defn([__IDN]), defn([__CMD])))[]dnl 188 | ifelse(defn([long]), [], [divert(-1)])dnl 189 | else if (yuck_streqp(op, "defn([long])")) { 190 | popdef([long])[]dnl 191 | dnl now simply expand yuck_foo_action: 192 | yuck_option_action(defn([__IDN]), defn([__CMD])); 193 | }dnl 194 | divert[]dnl 195 | ]) else { 196 | ifelse(defn([__CMD]), [], [dnl 197 | ifdef([YOPT_ALLOW_UNKNOWN_DASHDASH], [dnl 198 | /* just treat it as argument then */ 199 | resume_at(arg); 200 | ], [dnl 201 | /* grml */ 202 | fprintf(stderr, "YUCK_UMB_STR: unrecognized option `--%s'\n", op); 203 | resume_at(failure); 204 | xtra_chk: 205 | if (arg != NULL) { 206 | fprintf(stderr, "YUCK_UMB_STR: option `--%s' doesn't allow an argument\n", op); 207 | resume_at(failure); 208 | } 209 | ])dnl 210 | ], [dnl 211 | resume_at(yuck_cmd()[_longopt]); 212 | ])dnl 213 | } 214 | if (i >= argc) { 215 | fprintf(stderr, "YUCK_UMB_STR: option `--%s' requires an argument\n", op); 216 | resume_at(failure); 217 | } 218 | resume; 219 | } 220 | ]) 221 | popdef([yuck_flag_action])dnl 222 | popdef([yuck_arg_action])dnl 223 | popdef([yuck_arg_mul_action])dnl 224 | popdef([yuck_arg_opt_action])dnl 225 | popdef([yuck_arg_mul_opt_action])dnl 226 | popdef([yuck_auto_action])dnl 227 | } 228 | 229 | coroutine(shortopt) 230 | { 231 | char *arg = op + 1U; 232 | 233 | switch (tgt->cmd) { 234 | default: 235 | yield(yuck_cmd()[_shortopt]); 236 | break; 237 | foreachq([__CMD], yuck_cmds(), [case yuck_cmd(defn([__CMD])): 238 | yield(yuck_cmd(defn([__CMD]))[_shortopt]); 239 | break; 240 | ])} 241 | resume; 242 | 243 | dnl TYPE actions 244 | pushdef([yuck_flag_action], [tgt->yuck_slot([$1], [$2])++])dnl 245 | pushdef([yuck_arg_action], [tgt->yuck_slot([$1], [$2]) = *arg 246 | ? (op += strlen(arg), arg) 247 | : argv[[++i]]])dnl 248 | pushdef([yuck_arg_opt_action], [tgt->yuck_slot([$1], [$2]) = *arg 249 | ? (op += strlen(arg), arg) 250 | : YUCK_OPTARG_NONE])dnl 251 | pushdef([yuck_arg_mul_action], [tgt->yuck_slot([$1], [$2]) = 252 | yuck_append( 253 | tgt->yuck_slot([$1], [$2]), 254 | tgt->yuck_cnt_slot([$1], [$2])++, 255 | *arg ? (op += strlen(arg), arg) : argv[[++i]]); 256 | if (tgt->yuck_slot([$1], [$2]) == NULL) { 257 | return -1; 258 | }])dnl 259 | pushdef([yuck_arg_mul_opt_action], [tgt->yuck_slot([$1], [$2]) = 260 | yuck_append( 261 | tgt->yuck_slot([$1], [$2]), 262 | tgt->yuck_cnt_slot([$1], [$2])++, 263 | *arg ? (op += strlen(arg), arg) : YUCK_OPTARG_NONE); 264 | if (tgt->yuck_slot([$1], [$2]) == NULL) { 265 | return -1; 266 | }])dnl 267 | pushdef([yuck_auto_action], [/* invoke auto action and exit */ 268 | yuck_auto_[]yuck_canon([$1], [$2])(tgt); 269 | resume_at(success)])dnl 270 | 271 | foreachq([__CMD], yuck_umbcmds(), [coroutine(yuck_cmd(defn([__CMD]))[_shortopt]) 272 | { 273 | switch (*op) { 274 | default: 275 | /* again for clarity */ 276 | switch (*op) { 277 | case '0': 278 | case '1': 279 | case '2': 280 | case '3': 281 | case '4': 282 | case '5': 283 | case '6': 284 | case '7': 285 | case '8': 286 | case '9': 287 | if (op[[-1]] == '-') { 288 | /* literal treatment of numeral */ 289 | resume_at(arg); 290 | } 291 | /* fallthrough */ 292 | default: 293 | break; 294 | } 295 | divert(1); 296 | ifdef([YOPT_ALLOW_UNKNOWN_DASH], [dnl 297 | resume_at(arg); 298 | ], [dnl 299 | fprintf(stderr, "YUCK_UMB_STR: invalid option -%c\n", *op); 300 | resume_at(failure); 301 | ])dnl 302 | 303 | ifdef([YUCK_SHORTS_HAVE_NUMERALS], [ 304 | /* [yuck_shorts()] (= yuck_shorts()) 305 | * has numerals as shortopts 306 | * don't allow literal treatment of numerals */divert(-1)]) 307 | 308 | divert(2); 309 | resume_at(yuck_cmd()[_shortopt]); 310 | 311 | divert(0); 312 | ifelse(defn([__CMD]), [], [select_divert(1)], [select_divert(2)])dnl 313 | divert[]dnl 314 | 315 | foreachq([__IDN], yuck_idents(defn([__CMD])), [dnl 316 | pushdef([short], yuck_short(defn([__IDN]), defn([__CMD])))dnl 317 | ifelse(defn([short]), [], [divert(-1)])dnl 318 | case 'defn([short])': 319 | popdef([short])dnl 320 | dnl 321 | dnl now simply expand yuck_foo_action: 322 | yuck_option_action(defn([__IDN]), defn([__CMD])); 323 | break; 324 | divert[]dnl 325 | ])dnl 326 | } 327 | if (i >= argc) { 328 | fprintf(stderr, "YUCK_UMB_STR: option `--%s' requires an argument\n", op); 329 | resume_at(failure); 330 | } 331 | resume; 332 | } 333 | ]) 334 | popdef([yuck_flag_action])dnl 335 | popdef([yuck_arg_action])dnl 336 | popdef([yuck_arg_opt_action])dnl 337 | popdef([yuck_arg_mul_action])dnl 338 | popdef([yuck_arg_mul_opt_action])dnl 339 | popdef([yuck_auto_action])dnl 340 | } 341 | 342 | coroutine(arg) 343 | { 344 | if (tgt->cmd || YUCK_NCMDS == 0U) { 345 | tgt->args[[tgt->nargs++]] = argv[[i]]; 346 | } else { 347 | /* ah, might be an arg then */ 348 | if ((tgt->cmd = yuck_parse_cmd(op)) > YUCK_NCMDS) { 349 | return -1; 350 | } 351 | } 352 | resume; 353 | } 354 | 355 | coroutine(failure) 356 | { 357 | exit(EXIT_FAILURE); 358 | } 359 | 360 | coroutine(success) 361 | { 362 | exit(EXIT_SUCCESS); 363 | } 364 | } 365 | 366 | DEFUN void yuck_free(yuck_t tgt[[static 1U]]) 367 | { 368 | if (tgt->args != NULL) { 369 | /* free despite const qualifier */ 370 | free(tgt->args); 371 | } 372 | /* free mulargs */ 373 | switch (tgt->cmd) { 374 | void *ptr; 375 | default: 376 | break; 377 | pushdef([action], [dnl 378 | ptr = tgt->yuck_slot([$1], [$2]); 379 | if (ptr != NULL) { 380 | free(ptr); 381 | } 382 | ])dnl 383 | dnl TYPE actions 384 | pushdef([yuck_flag_action], [])dnl 385 | pushdef([yuck_arg_action], [])dnl 386 | pushdef([yuck_arg_opt_action], [])dnl 387 | pushdef([yuck_arg_mul_action], defn([action]))dnl 388 | pushdef([yuck_arg_mul_opt_action], defn([action]))dnl 389 | pushdef([yuck_auto_action], [])dnl 390 | foreachq([__CMD], yuck_umbcmds(), [dnl 391 | case yuck_cmd(defn([__CMD])): 392 | foreachq([__IDN], yuck_idents(defn([__CMD])), [dnl 393 | yuck_option_action(defn([__IDN]), defn([__CMD])); 394 | ])[]dnl 395 | break; 396 | ])[]dnl 397 | popdef([action])dnl 398 | popdef([yuck_flag_action])dnl 399 | popdef([yuck_arg_action])dnl 400 | popdef([yuck_arg_opt_action])dnl 401 | popdef([yuck_arg_mul_action])dnl 402 | popdef([yuck_arg_mul_opt_action])dnl 403 | popdef([yuck_auto_opt_action])dnl 404 | } 405 | return; 406 | } 407 | 408 | DEFUN void yuck_auto_usage(const yuck_t src[[static 1U]]) 409 | { 410 | switch (src->cmd) { 411 | default: 412 | YUCK_NOCMD: 413 | puts("Usage: YUCK_UMB_STR [[OPTION]]...dnl 414 | ifelse(yuck_cmds(), [], [], [ COMMAND])[]dnl 415 | ifelse(defn([YUCK_UMB_POSARG]), [], [], [ defn([YUCK_UMB_POSARG])])\n\ 416 | ifelse(yuck_umb_desc(), [], [], [dnl 417 | \n\ 418 | yuck_C_literal(yuck_umb_desc())\n\ 419 | ])dnl 420 | "); 421 | break; 422 | foreachq([__CMD], yuck_cmds(), [ 423 | case yuck_cmd(defn([__CMD])): 424 | puts("Usage: YUCK_UMB_STR dnl 425 | yuck_cmd_string(defn([__CMD]))[]dnl 426 | ifelse(yuck_idents(defn([__CMD])), [], [], [ [[OPTION]]...])[]dnl 427 | ifelse(yuck_cmd_posarg(defn([__CMD])), [], [], [ yuck_cmd_posarg(defn([__CMD]))])\n\ 428 | ifelse(yuck_cmd_desc(defn([__CMD])), [], [], [dnl 429 | \n\ 430 | yuck_C_literal(yuck_cmd_desc(defn([__CMD])))\n\ 431 | ])dnl 432 | "); 433 | break; 434 | ]) 435 | } 436 | 437 | #if defined yuck_post_usage 438 | yuck_post_usage(src); 439 | #endif /* yuck_post_usage */ 440 | return; 441 | } 442 | 443 | DEFUN void yuck_auto_help(const yuck_t src[[static 1U]]) 444 | { 445 | yuck_auto_usage(src); 446 | 447 | ifelse(yuck_cmds(), [], [], [dnl 448 | if (src->cmd == YUCK_NOCMD) { 449 | /* also output a list of commands */ 450 | puts("COMMAND may be one of:\n\ 451 | foreachq([__CMD], yuck_cmds(), [yuck_C_literal(yuck_cmd_line(defn([__CMD])))\n\ 452 | ])"); 453 | } 454 | ])dnl 455 | 456 | /* leave a not about common options */ 457 | if (src->cmd == YUCK_NOCMD) { 458 | ifelse(yuck_cmds(), [], [dnl 459 | ; 460 | ], [dnl 461 | puts("\ 462 | Options accepted by all commands:"); 463 | ])dnl 464 | ifelse(yuck_cmds(), [], [], [dnl 465 | } else { 466 | puts("\ 467 | Common options:\n\ 468 | foreachq([__IDN], yuck_idents([]), [dnl 469 | yuck_C_literal(backquote([yuck_option_help_line(defn([__IDN]), [])]))[]dnl 470 | ])dnl 471 | "); 472 | ])dnl 473 | } 474 | 475 | switch (src->cmd) { 476 | default:foreachq([__CMD], yuck_umbcmds(), [ 477 | case yuck_cmd(defn([__CMD])): 478 | puts("\ 479 | ifelse(defn([__CMD]), [], [], [dnl 480 | ifelse(yuck_idents(defn([__CMD])), [], [], [dnl 481 | Command-specific options:\n\ 482 | ])dnl 483 | ])dnl 484 | foreachq([__IDN], yuck_idents(defn([__CMD])), [dnl 485 | yuck_C_literal(backquote([yuck_option_help_line(defn([__IDN]), defn([__CMD]))]))[]dnl 486 | ])dnl 487 | "); 488 | break; 489 | ]) 490 | } 491 | 492 | #if defined yuck_post_help 493 | yuck_post_help(src); 494 | #endif /* yuck_post_help */ 495 | 496 | #if defined PACKAGE_BUGREPORT 497 | puts("\n\ 498 | Report bugs to " PACKAGE_BUGREPORT); 499 | #endif /* PACKAGE_BUGREPORT */ 500 | return; 501 | } 502 | 503 | DEFUN void yuck_auto_version(const yuck_t src[[static 1U]]) 504 | { 505 | switch (src->cmd) { 506 | default: 507 | ifdef([YUCK_VERSION], [dnl 508 | puts("YUCK_UMB_STR YUCK_VERSION"); 509 | ], [dnl 510 | #if 0 511 | 512 | #elif defined package_string 513 | puts(package_string); 514 | #elif defined package_version 515 | printf("YUCK_UMB_STR %s\n", package_version); 516 | #elif defined PACKAGE_STRING 517 | puts(PACKAGE_STRING); 518 | #elif defined PACKAGE_VERSION 519 | puts("YUCK_UMB_STR " PACKAGE_VERSION); 520 | #elif defined VERSION 521 | puts("YUCK_UMB_STR " VERSION); 522 | #else /* !PACKAGE_VERSION, !VERSION */ 523 | puts("YUCK_UMB_STR unknown version"); 524 | #endif /* PACKAGE_VERSION */ 525 | ])dnl 526 | break; 527 | } 528 | 529 | #if defined yuck_post_version 530 | yuck_post_version(src); 531 | #endif /* yuck_post_version */ 532 | return; 533 | } 534 | popdef([DEFUN])dnl 535 | 536 | #if defined __INTEL_COMPILER 537 | # pragma warning (pop) 538 | #elif defined __GNUC__ 539 | # if __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6 540 | # pragma GCC diagnostic pop 541 | # endif /* GCC version */ 542 | #endif /* __INTEL_COMPILER */ 543 | changequote`'dnl 544 | -------------------------------------------------------------------------------- /build-aux/yuck-coru.h.m4: -------------------------------------------------------------------------------- 1 | /* -*- c -*- */ 2 | changequote`'changequote([,])dnl 3 | changecom([#])dnl 4 | #if !defined INCLUDED_yuck_h_ 5 | #define INCLUDED_yuck_h_ 6 | 7 | #include 8 | 9 | #define YUCK_OPTARG_NONE ((void*)0x1U) 10 | 11 | enum yuck_cmds_e { 12 | pushdef([last], yuck_cmd())pushdef([first], defn([last]))[]dnl 13 | /* value used when no command was specified */ 14 | first = 0U, 15 | 16 | /* actual commands */ 17 | foreachq([cmd], yuck_cmds(), [define([last], yuck_cmd(defn([cmd])))[]last, 18 | ]) 19 | /* convenience identifiers */ 20 | YUCK_NOCMD = first, 21 | YUCK_NCMDS = last 22 | popdef([last])popdef([first])[]dnl 23 | }; 24 | 25 | define([yuck_slot_predecl], [dnl 26 | yuck_iftype([$1], [$2], 27 | [arg,mul], [size_t ]yuck_canon([$1], [$2])[_nargs], 28 | [arg,mul,opt], [size_t ]yuck_canon([$1], [$2])[_nargs], 29 | )dnl 30 | ])dnl 31 | 32 | define([yuck_slot_decl], [dnl 33 | pushdef([pre], [yuck_slot_predecl([$1], [$2])])dnl 34 | pushdef([ident], [yuck_slot_identifier([$1], [$2])])dnl 35 | yuck_iftype([$1], [$2], 36 | [flag], [unsigned int ident;], 37 | [arg], [char *ident;], 38 | [arg,opt], [char *ident;], 39 | [arg,mul], [pre; char **ident;], 40 | [arg,mul,opt], [pre; char **ident;], 41 | [auto], [/* $1 is handled automatically */])dnl 42 | popdef([pre])dnl 43 | popdef([ident])dnl 44 | ])dnl 45 | 46 | ifelse(yuck_cmds(), [], [dnl 47 | typedef struct yuck_s yuck_t; 48 | ], [dnl 49 | typedef union yuck_u yuck_t; 50 | ])dnl 51 | 52 | foreachq([cmd], yuck_cmds(), [ 53 | /* convenience structure for `cmd' */ 54 | struct yuck_cmd_[]defn([cmd])[]_s { 55 | enum yuck_cmds_e [cmd]; 56 | 57 | /* left-over arguments, the command string is never a part of this */ 58 | size_t nargs; 59 | char **args; 60 | foreachq([slot], yuck_idents(), [ 61 | yuck_slot_decl(defn([slot]))[]dnl 62 | ]) 63 | foreachq([slot], yuck_idents(defn([cmd])), [ 64 | yuck_slot_decl(defn([slot]), defn([cmd]))[]dnl 65 | ]) 66 | }; 67 | ]) 68 | 69 | ifelse(yuck_cmds(), [], [dnl 70 | /* generic struct */ 71 | struct yuck_s { 72 | enum yuck_cmds_e cmd; 73 | 74 | /* left-over arguments, 75 | * the command string is never a part of this */ 76 | size_t nargs; 77 | char **args; 78 | 79 | /* slots common to all commands */ 80 | foreachq([slot], yuck_idents(), [ 81 | yuck_slot_decl(defn([slot]))[]dnl 82 | ]) 83 | }; 84 | ], [dnl else 85 | union yuck_u { 86 | /* generic struct */ 87 | struct { 88 | enum yuck_cmds_e cmd; 89 | 90 | /* left-over arguments, 91 | * the command string is never a part of this */ 92 | size_t nargs; 93 | char **args; 94 | 95 | /* slots common to all commands */ 96 | foreachq([slot], yuck_idents(), [dnl 97 | yuck_slot_decl(defn([slot])) 98 | ])dnl 99 | }; 100 | 101 | /* depending on CMD at most one of the following structs is filled in 102 | * if CMD is YUCK_NONE no slots of this union must be accessed */ 103 | foreachq([cmd], yuck_cmds(), [dnl 104 | struct yuck_cmd_[]defn([cmd])[]_s defn([cmd]); 105 | ])dnl 106 | }; 107 | ]) 108 | 109 | pushdef([DECLF], ifdef([YUCK_HEADER], [extern], [static]))[]dnl 110 | DECLF __attribute__((nonnull(1))) int 111 | yuck_parse(yuck_t*, int argc, char *argv[[]]); 112 | DECLF __attribute__((nonnull(1))) void yuck_free(yuck_t*); 113 | 114 | DECLF __attribute__((nonnull(1))) void yuck_auto_help(const yuck_t*); 115 | DECLF __attribute__((nonnull(1))) void yuck_auto_usage(const yuck_t*); 116 | DECLF __attribute__((nonnull(1))) void yuck_auto_version(const yuck_t*); 117 | 118 | /* some hooks */ 119 | #if defined yuck_post_help 120 | DECLF __attribute__((nonnull(1))) void yuck_post_help(const yuck_t*); 121 | #endif /* yuck_post_help */ 122 | 123 | #if defined yuck_post_usage 124 | DECLF __attribute__((nonnull(1))) void yuck_post_usage(const yuck_t*); 125 | #endif /* yuck_post_usage */ 126 | 127 | #if defined yuck_post_version 128 | DECLF __attribute__((nonnull(1))) void yuck_post_version(const yuck_t*); 129 | #endif /* yuck_post_version */ 130 | popdef([DECLF])[]dnl 131 | 132 | #endif /* INCLUDED_yuck_h_ */ 133 | changequote`'dnl 134 | -------------------------------------------------------------------------------- /build-aux/yuck-scmver.c: -------------------------------------------------------------------------------- 1 | /*** yuck-scmver.c -- snarf versions off project cwds 2 | * 3 | * Copyright (C) 2013-2016 Sebastian Freundt 4 | * 5 | * Author: Sebastian Freundt 6 | * 7 | * This file is part of yuck. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the author nor the names of any contributors 21 | * may be used to endorse or promote products derived from this 22 | * software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 25 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 31 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 32 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 34 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | ***/ 37 | #if defined HAVE_CONFIG_H 38 | # include "config.h" 39 | #endif /* HAVE_CONFIG_H */ 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include "yuck-scmver.h" 52 | 53 | #if !defined LIKELY 54 | # define LIKELY(_x) __builtin_expect((_x), 1) 55 | #endif /* !LIKELY */ 56 | #if !defined UNLIKELY 57 | # define UNLIKELY(_x) __builtin_expect((_x), 0) 58 | #endif /* UNLIKELY */ 59 | #if !defined UNUSED 60 | # define UNUSED(_x) _x __attribute__((unused)) 61 | #endif /* !UNUSED */ 62 | 63 | #if !defined countof 64 | # define countof(x) (sizeof(x) / sizeof(*x)) 65 | #endif /* !countof */ 66 | 67 | #define _paste(x, y) x ## y 68 | #define paste(x, y) _paste(x, y) 69 | #if !defined with 70 | # define with(args...) \ 71 | for (args, *paste(__ep, __LINE__) = (void*)1; \ 72 | paste(__ep, __LINE__); paste(__ep, __LINE__) = 0) 73 | #endif /* !with */ 74 | 75 | #define DEBUG(args...) 76 | 77 | /* globals */ 78 | const char *const yscm_strs[] = { 79 | [YUCK_SCM_TARBALL] = "tarball", 80 | [YUCK_SCM_GIT] = "git", 81 | [YUCK_SCM_BZR] = "bzr", 82 | [YUCK_SCM_HG] = "hg", 83 | }; 84 | 85 | 86 | static __attribute__((format(printf, 1, 2))) void 87 | error(const char *fmt, ...) 88 | { 89 | va_list vap; 90 | va_start(vap, fmt); 91 | vfprintf(stderr, fmt, vap); 92 | va_end(vap); 93 | if (errno) { 94 | fputc(':', stderr); 95 | fputc(' ', stderr); 96 | fputs(strerror(errno), stderr); 97 | } 98 | fputc('\n', stderr); 99 | return; 100 | } 101 | 102 | static inline __attribute__((const, pure, always_inline)) char* 103 | deconst(const char *s) 104 | { 105 | union { 106 | const char *c; 107 | char *p; 108 | } x = {s}; 109 | return x.p; 110 | } 111 | 112 | static __attribute__((unused)) size_t 113 | xstrlcpy(char *restrict dst, const char *src, size_t dsz) 114 | { 115 | size_t ssz; 116 | 117 | if (UNLIKELY(dsz == 0U)) { 118 | return 0U; 119 | } 120 | if ((ssz = strlen(src)) > dsz) { 121 | ssz = dsz - 1U; 122 | } 123 | memcpy(dst, src, ssz); 124 | dst[ssz] = '\0'; 125 | return ssz; 126 | } 127 | 128 | static __attribute__((unused)) size_t 129 | xstrlncpy(char *restrict dst, size_t dsz, const char *src, size_t ssz) 130 | { 131 | if (UNLIKELY(dsz == 0U)) { 132 | return 0U; 133 | } 134 | if (ssz > dsz) { 135 | ssz = dsz - 1U; 136 | } 137 | memcpy(dst, src, ssz); 138 | dst[ssz] = '\0'; 139 | return ssz; 140 | } 141 | 142 | static char* 143 | xdirname(char *restrict fn, const char *fp) 144 | { 145 | /* find next dir in FN from FP backwards */ 146 | if (fp == NULL) { 147 | fp = fn + strlen(fn); 148 | } else if (fp <= fn) { 149 | return NULL; 150 | } 151 | 152 | for (--fp; fp >= fn && *fp != '/'; fp--); 153 | while (fp >= fn && *--fp == '/'); 154 | if (fp >= fn) { 155 | /* replace / by \nul and return pointer */ 156 | char *dp = fn + (++fp - fn); 157 | *dp = '\0'; 158 | return dp; 159 | } 160 | /* return \nul */ 161 | return NULL; 162 | } 163 | 164 | static char* 165 | xmemmem(const char *hay, const size_t hayz, const char *ndl, const size_t ndlz) 166 | { 167 | const char *const eoh = hay + hayz; 168 | const char *const eon = ndl + ndlz; 169 | const char *hp; 170 | const char *np; 171 | const char *cand; 172 | unsigned int hsum; 173 | unsigned int nsum; 174 | unsigned int eqp; 175 | 176 | /* trivial checks first 177 | * a 0-sized needle is defined to be found anywhere in haystack 178 | * then run strchr() to find a candidate in HAYSTACK (i.e. a portion 179 | * that happens to begin with *NEEDLE) */ 180 | if (ndlz == 0UL) { 181 | return deconst(hay); 182 | } else if ((hay = memchr(hay, *ndl, hayz)) == NULL) { 183 | /* trivial */ 184 | return NULL; 185 | } 186 | 187 | /* First characters of haystack and needle are the same now. Both are 188 | * guaranteed to be at least one character long. Now computes the sum 189 | * of characters values of needle together with the sum of the first 190 | * needle_len characters of haystack. */ 191 | for (hp = hay + 1U, np = ndl + 1U, hsum = *hay, nsum = *hay, eqp = 1U; 192 | hp < eoh && np < eon; 193 | hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++); 194 | 195 | /* HP now references the (NZ + 1)-th character. */ 196 | if (np < eon) { 197 | /* haystack is smaller than needle, :O */ 198 | return NULL; 199 | } else if (eqp) { 200 | /* found a match */ 201 | return deconst(hay); 202 | } 203 | 204 | /* now loop through the rest of haystack, 205 | * updating the sum iteratively */ 206 | for (cand = hay; hp < eoh; hp++) { 207 | hsum ^= *cand++; 208 | hsum ^= *hp; 209 | 210 | /* Since the sum of the characters is already known to be 211 | * equal at that point, it is enough to check just NZ - 1 212 | * characters for equality, 213 | * also CAND is by design < HP, so no need for range checks */ 214 | if (hsum == nsum && memcmp(cand, ndl, ndlz - 1U) == 0) { 215 | return deconst(cand); 216 | } 217 | } 218 | return NULL; 219 | } 220 | 221 | static unsigned int 222 | hextou(const char *sp, char **ep) 223 | { 224 | register unsigned int res = 0U; 225 | size_t i; 226 | 227 | if (UNLIKELY(sp == NULL)) { 228 | goto out; 229 | } else if (*sp == '\0') { 230 | goto out; 231 | } 232 | for (i = 0U; i < sizeof(res) * 8U / 4U - 1U; sp++, i++) { 233 | register unsigned int this; 234 | switch (*sp) { 235 | case '0' ... '9': 236 | this = *sp - '0'; 237 | break; 238 | case 'a' ... 'f': 239 | this = *sp - 'a' + 10U; 240 | break; 241 | case 'A' ... 'F': 242 | this = *sp - 'A' + 10U; 243 | break; 244 | default: 245 | goto fucked; 246 | } 247 | 248 | res <<= 4U; 249 | res |= this; 250 | } 251 | fucked: 252 | res <<= 4U; 253 | res |= i; 254 | 255 | /* keep reading the hexstring as long as it lasts */ 256 | for (;; sp++) { 257 | switch (*sp) { 258 | case '0' ... '9': 259 | case 'a' ... 'f': 260 | case 'A' ... 'F': 261 | continue; 262 | default: 263 | goto out; 264 | } 265 | } 266 | out: 267 | if (ep != NULL) { 268 | *ep = (char*)1U + (sp - (char*)1U); 269 | } 270 | return res; 271 | } 272 | 273 | 274 | /* version snarfers */ 275 | static __attribute__((noinline)) pid_t 276 | run(int *fd, ...) 277 | { 278 | static char *cmdline[16U]; 279 | va_list vap; 280 | pid_t p; 281 | /* to snarf off traffic from the child */ 282 | int intfd[2]; 283 | 284 | va_start(vap, fd); 285 | for (size_t i = 0U; 286 | i < countof(cmdline) && 287 | (cmdline[i] = va_arg(vap, char*)) != NULL; i++); 288 | va_end(vap); 289 | assert(*cmdline); 290 | 291 | if (pipe(intfd) < 0) { 292 | error("pipe setup to/from %s failed", cmdline[0U]); 293 | return -1; 294 | } 295 | 296 | switch ((p = vfork())) { 297 | case -1: 298 | /* i am an error */ 299 | error("vfork for %s failed", cmdline[0U]); 300 | return -1; 301 | 302 | default: 303 | /* i am the parent */ 304 | close(intfd[1]); 305 | if (fd != NULL) { 306 | *fd = intfd[0]; 307 | } else { 308 | close(intfd[0]); 309 | } 310 | return p; 311 | 312 | case 0: 313 | /* i am the child */ 314 | break; 315 | } 316 | 317 | /* child code here */ 318 | close(intfd[0]); 319 | dup2(intfd[1], STDOUT_FILENO); 320 | 321 | execvp(cmdline[0U], cmdline); 322 | error("execvp(%s) failed", cmdline[0U]); 323 | _exit(EXIT_FAILURE); 324 | } 325 | 326 | static int 327 | fin(pid_t p) 328 | { 329 | int rc = 2; 330 | int st; 331 | 332 | while (waitpid(p, &st, 0) != p); 333 | if (WIFEXITED(st)) { 334 | rc = WEXITSTATUS(st); 335 | } 336 | return rc; 337 | } 338 | 339 | static yuck_scm_t 340 | find_scm(char *restrict fn, size_t fz, const char *path) 341 | { 342 | struct stat st[1U]; 343 | char *restrict dp = fn; 344 | 345 | /* make a copy so we can fiddle with it */ 346 | if (UNLIKELY(path == NULL)) { 347 | cwd: 348 | /* just use "." then */ 349 | *dp++ = '.'; 350 | *dp = '\0'; 351 | } else if ((dp += xstrlcpy(fn, path, fz)) == fn) { 352 | goto cwd; 353 | } 354 | again: 355 | if (stat(fn, st) < 0) { 356 | return YUCK_SCM_ERROR; 357 | } else if (UNLIKELY((size_t)(dp - fn) + 5U >= fz)) { 358 | /* not enough space */ 359 | return YUCK_SCM_ERROR; 360 | } else if (!S_ISDIR(st->st_mode)) { 361 | /* not a directory, get the dir bit and start over */ 362 | if ((dp = xdirname(fn, dp)) == NULL) { 363 | dp = fn; 364 | goto cwd; 365 | } 366 | goto again; 367 | } 368 | 369 | scm_chk: 370 | /* now check for .git, .bzr, .hg */ 371 | xstrlcpy(dp, "/.git", fz - (dp - fn)); 372 | DEBUG("trying %s ...\n", fn); 373 | if (stat(fn, st) == 0 && S_ISDIR(st->st_mode)) { 374 | /* yay it's a .git */ 375 | *dp = '\0'; 376 | return YUCK_SCM_GIT; 377 | } 378 | 379 | xstrlcpy(dp, "/.bzr", fz - (dp - fn)); 380 | DEBUG("trying %s ...\n", fn); 381 | if (stat(fn, st) == 0 && S_ISDIR(st->st_mode)) { 382 | /* yay it's a .git */ 383 | *dp = '\0'; 384 | return YUCK_SCM_BZR; 385 | } 386 | 387 | xstrlcpy(dp, "/.hg", fz - (dp - fn)); 388 | DEBUG("trying %s ...\n", fn); 389 | if (stat(fn, st) == 0 && S_ISDIR(st->st_mode)) { 390 | /* yay it's a .git */ 391 | *dp = '\0'; 392 | return YUCK_SCM_HG; 393 | } 394 | /* nothing then, traverse upwards */ 395 | if (*fn != '/') { 396 | /* make sure we don't go up indefinitely 397 | * comparing the current inode to ./.. */ 398 | with (ino_t curino) { 399 | *dp = '\0'; 400 | if (stat(fn, st) < 0) { 401 | return YUCK_SCM_ERROR; 402 | } 403 | /* memorise inode */ 404 | curino = st->st_ino; 405 | /* go upwards by appending /.. */ 406 | dp += xstrlcpy(dp, "/..", fz - (dp - fn)); 407 | /* check inode again */ 408 | if (stat(fn, st) < 0) { 409 | return YUCK_SCM_ERROR; 410 | } else if (st->st_ino == curino) { 411 | break; 412 | } 413 | goto scm_chk; 414 | } 415 | } else if ((dp = xdirname(fn, dp)) != NULL) { 416 | goto scm_chk; 417 | } 418 | return YUCK_SCM_TARBALL; 419 | } 420 | 421 | 422 | static int 423 | rd_version(struct yuck_version_s *restrict v, const char *buf, size_t bsz) 424 | { 425 | /* reads a normalised version string vX.Y.Z-DIST-SCM RVSN[-dirty] */ 426 | static const char dflag[] = "dirty"; 427 | const char *vtag = NULL; 428 | const char *eov; 429 | const char *dist = NULL; 430 | const char *eod; 431 | const char *bp = buf; 432 | const char *const ep = buf + bsz; 433 | 434 | /* parse buf */ 435 | switch (*bp) { 436 | case 'v': 437 | case 'V': 438 | bp++; 439 | case '0': 440 | case '1': 441 | case '2': 442 | case '3': 443 | case '4': 444 | case '5': 445 | case '6': 446 | case '7': 447 | case '8': 448 | case '9': 449 | break; 450 | default: 451 | /* weird, we req'd v-tags */ 452 | return -1; 453 | } 454 | 455 | if ((eov = memchr(vtag = bp, '-', ep - bp)) == NULL) { 456 | /* last field */ 457 | eov = ep; 458 | } else { 459 | dist = eov + 1U; 460 | } 461 | /* just for the fun of it, look for .git, .hg and .bzr as well */ 462 | with (const char *altp) { 463 | if ((altp = xmemmem(vtag, ep - vtag, ".git", 4U))) { 464 | v->scm = YUCK_SCM_GIT; 465 | eov = altp; 466 | dist = altp + 4U; 467 | } else if ((altp = xmemmem(vtag, ep - vtag, ".bzr", 4U))) { 468 | /* oooh looks like the alternative version 469 | * vX.Y.Z.gitDD.HASH */ 470 | v->scm = YUCK_SCM_BZR; 471 | eov = altp; 472 | dist = altp + 4U; 473 | } else if ((altp = xmemmem(vtag, ep - vtag, ".hg", 3U))) { 474 | /* oooh looks like the alternative version 475 | * vX.Y.Z.hgDD.HASH */ 476 | v->scm = YUCK_SCM_HG; 477 | eov = altp; 478 | dist = altp + 3U; 479 | } 480 | } 481 | 482 | /* bang vtag */ 483 | xstrlncpy(v->vtag, sizeof(v->vtag), vtag, eov - vtag); 484 | 485 | /* snarf distance */ 486 | if (dist == NULL) { 487 | return 0; 488 | } 489 | /* read distance */ 490 | with (char *on) { 491 | v->dist = strtoul(dist, &on, 10); 492 | eod = on; 493 | } 494 | 495 | switch (*eod) { 496 | default: 497 | case '\0': 498 | return 0; 499 | case '.': 500 | if (v->scm <= YUCK_SCM_TARBALL) { 501 | /* huh? */ 502 | return -1; 503 | } 504 | /*@fallthrough@*/ 505 | case '-': 506 | /* the show is going on, like it must */ 507 | bp = eod + 1U; 508 | break; 509 | } 510 | switch (*bp++) { 511 | case 'g': 512 | /* git repo */ 513 | v->scm = YUCK_SCM_GIT; 514 | break; 515 | case 'h': 516 | /* hg repo */ 517 | v->scm = YUCK_SCM_HG; 518 | break; 519 | case 'b': 520 | if (v->scm <= YUCK_SCM_TARBALL) { 521 | v->scm = YUCK_SCM_BZR; 522 | break; 523 | } 524 | /* else probably git or hg hash starting with b */ 525 | /*@fallthrough@*/ 526 | default: 527 | /* could have been set already then */ 528 | if (v->scm > YUCK_SCM_TARBALL) { 529 | /* rewind bp and continue */ 530 | bp--; 531 | break; 532 | } 533 | /* otherwise we simply don't know */ 534 | return 0; 535 | } 536 | /* read scm revision */ 537 | with (char *on) { 538 | v->rvsn = hextou(bp, &on); 539 | bp = on; 540 | } 541 | 542 | if (bp >= ep) { 543 | ; 544 | } else if (*bp != '-' && *bp != '.') { 545 | ; 546 | } else if (bp + sizeof(dflag) > ep) { 547 | /* too short to fit `dirty' */ 548 | ; 549 | } else if (!memcmp(++bp, dflag, sizeof(dflag) - 1U)) { 550 | v->dirty = 1U; 551 | } 552 | return 0; 553 | } 554 | 555 | static ssize_t 556 | wr_version(char *restrict buf, size_t bsz, const struct yuck_version_s *v) 557 | { 558 | static const char yscm_abbr[] = "tgbh"; 559 | const char *const ep = buf + bsz; 560 | char *bp = buf; 561 | 562 | if (UNLIKELY(buf == NULL || bsz == 0U)) { 563 | return -1; 564 | } 565 | *bp++ = 'v'; 566 | bp += xstrlcpy(bp, v->vtag, ep - bp); 567 | if (!v->dist) { 568 | goto out; 569 | } else if (bp + 1U >= ep) { 570 | /* not enough space */ 571 | return -1; 572 | } 573 | /* get the dist bit on the wire */ 574 | *bp++ = '-'; 575 | bp += snprintf(bp, ep - bp, "%u", v->dist); 576 | if (!v->rvsn || v->scm <= YUCK_SCM_TARBALL) { 577 | goto out; 578 | } else if (bp + 2U + 8U >= ep) { 579 | /* not enough space */ 580 | return -1; 581 | } 582 | *bp++ = '-'; 583 | *bp++ = yscm_abbr[v->scm]; 584 | bp += snprintf(bp, ep - bp, "%0*x", 585 | (int)(v->rvsn & 0x07U), v->rvsn >> 4U); 586 | if (!v->dirty) { 587 | goto out; 588 | } else if (bp + 1U + 5U >= ep) { 589 | /* not enough space */ 590 | return -1; 591 | } 592 | bp += xstrlcpy(bp, "-dirty", ep - bp); 593 | out: 594 | return bp - buf; 595 | } 596 | 597 | static int 598 | git_version(struct yuck_version_s v[static 1U]) 599 | { 600 | pid_t chld; 601 | int fd[1U]; 602 | int rc = 0; 603 | 604 | if ((chld = run(fd, "git", "describe", 605 | "--tags", "--match=v[0-9]*", 606 | "--abbrev=8", "--dirty", NULL)) < 0) { 607 | return -1; 608 | } 609 | /* shouldn't be heaps, so just use a single read */ 610 | with (char buf[256U]) { 611 | const char *vtag; 612 | const char *dist; 613 | char *bp; 614 | ssize_t nrd; 615 | 616 | if ((nrd = read(*fd, buf, sizeof(buf))) <= 0) { 617 | /* no version then aye */ 618 | rc = -1; 619 | break; 620 | } 621 | buf[nrd - 1U/* for \n*/] = '\0'; 622 | /* parse buf */ 623 | bp = buf; 624 | if (*bp++ != 'v') { 625 | /* weird, we req'd v-tags though */ 626 | rc = -1; 627 | break; 628 | } else if ((bp = strchr(vtag = bp, '-')) != NULL) { 629 | /* tokenise sting */ 630 | *bp++ = '\0'; 631 | } 632 | /* bang vtag */ 633 | xstrlcpy(v->vtag, vtag, sizeof(v->vtag)); 634 | 635 | /* snarf distance */ 636 | if (bp == NULL) { 637 | break; 638 | } else if ((bp = strchr(dist = bp, '-')) != NULL) { 639 | /* tokenize */ 640 | *bp++ = '\0'; 641 | } 642 | /* read distance */ 643 | v->dist = strtoul(dist, &bp, 10); 644 | 645 | if (*++bp == 'g') { 646 | bp++; 647 | /* read scm revision */ 648 | v->rvsn = hextou(bp, &bp); 649 | } 650 | if (*bp == '\0') { 651 | break; 652 | } else if (*bp == '-') { 653 | bp++; 654 | } 655 | if (!strcmp(bp, "dirty")) { 656 | v->dirty = 1U; 657 | } 658 | } 659 | close(*fd); 660 | if (fin(chld) != 0) { 661 | rc = -1; 662 | } 663 | return rc; 664 | } 665 | 666 | static int 667 | hg_version(struct yuck_version_s v[static 1U]) 668 | { 669 | pid_t chld; 670 | int fd[1U]; 671 | int rc = 0; 672 | 673 | if ((chld = run(fd, "hg", "log", 674 | "--rev", ".", 675 | "--template", 676 | "{latesttag}\t{latesttagdistance}\t{node|short}\n", 677 | NULL)) < 0) { 678 | return -1; 679 | } 680 | /* shouldn't be heaps, so just use a single read */ 681 | with (char buf[256U]) { 682 | const char *vtag; 683 | const char *dist; 684 | char *bp; 685 | ssize_t nrd; 686 | 687 | if ((nrd = read(*fd, buf, sizeof(buf))) <= 0) { 688 | /* no version then aye */ 689 | rc = -1; 690 | break; 691 | } 692 | buf[nrd - 1U/* for \n*/] = '\0'; 693 | /* parse buf */ 694 | bp = buf; 695 | if (*bp++ != 'v') { 696 | /* technically we could request the latest v-tag 697 | * but i'm no hg buff so fuck it */ 698 | rc = -1; 699 | break; 700 | } else if ((bp = strchr(vtag = bp, '\t')) != NULL) { 701 | /* tokenise */ 702 | *bp++ = '\0'; 703 | } 704 | /* bang vtag */ 705 | xstrlcpy(v->vtag, vtag, sizeof(v->vtag)); 706 | 707 | if (UNLIKELY(bp == NULL)) { 708 | /* huh? */ 709 | rc = -1; 710 | break; 711 | } else if ((bp = strchr(dist = bp, '\t')) != NULL) { 712 | /* tokenise */ 713 | *bp++ = '\0'; 714 | } 715 | /* bang distance */ 716 | v->dist = strtoul(dist, NULL, 10); 717 | 718 | /* bang revision */ 719 | v->rvsn = hextou(bp, NULL); 720 | } 721 | close(*fd); 722 | if (fin(chld) != 0) { 723 | rc = -1; 724 | } 725 | return rc; 726 | } 727 | 728 | static int 729 | bzr_version(struct yuck_version_s v[static 1U]) 730 | { 731 | pid_t chld; 732 | int fd[1U]; 733 | int rc = 0; 734 | 735 | /* first get current revision number */ 736 | if ((chld = run(fd, "bzr", "revno", NULL)) < 0) { 737 | return -1; 738 | } 739 | /* shouldn't be heaps, so just use a single read */ 740 | with (char buf[256U]) { 741 | ssize_t nrd; 742 | 743 | if ((nrd = read(*fd, buf, sizeof(buf))) <= 0) { 744 | /* no version then aye */ 745 | break; 746 | } 747 | with (char *on) { 748 | v->rvsn = strtoul(buf, &on, 10); 749 | if (LIKELY(on != NULL)) { 750 | v->rvsn <<= 4U; 751 | v->rvsn |= on - buf; 752 | } 753 | } 754 | } 755 | close(*fd); 756 | if (fin(chld) != 0) { 757 | return -1; 758 | } 759 | 760 | if ((chld = run(fd, "bzr", "tags", 761 | "--sort=time", NULL)) < 0) { 762 | return -1; 763 | } 764 | /* could be a lot, we only need the last line though */ 765 | with (char buf[4096U]) { 766 | const char *vtag; 767 | size_t bz; 768 | char *bp; 769 | ssize_t nrd; 770 | 771 | bp = buf; 772 | bz = sizeof(buf); 773 | while ((nrd = read(*fd, bp, bz)) == (ssize_t)bz) { 774 | /* find last line */ 775 | while (bz-- > 0 && buf[bz] != '\n'); 776 | /* reassess bz */ 777 | bz++; 778 | /* reassess bp */ 779 | bp = buf + (sizeof(buf) - bz); 780 | if (LIKELY(bz < sizeof(buf))) { 781 | memmove(buf, buf + bz, sizeof(buf) - bz); 782 | } 783 | } 784 | if (nrd <= 0) { 785 | /* no version then aye */ 786 | break; 787 | } 788 | bp[nrd - 1U/* for \n*/] = '\0'; 789 | /* find last line */ 790 | bp += nrd; 791 | while (--bp >= buf && *bp != '\n'); 792 | 793 | /* parse buf */ 794 | if (*++bp != 'v') { 795 | /* we want v tags, we could go back and see if 796 | * there are any */ 797 | rc = -1; 798 | break; 799 | } else if ((bp = strchr(vtag = ++bp, ' ')) != NULL) { 800 | /* tokenise */ 801 | *bp++ = '\0'; 802 | } 803 | /* bang vtag */ 804 | xstrlcpy(v->vtag, vtag, sizeof(v->vtag)); 805 | 806 | if (bp == NULL) { 807 | break; 808 | } 809 | /* read over all the whitespace to find the tag's revno */ 810 | with (unsigned int rno = strtoul(bp, NULL, 10)) { 811 | v->dist = v->rvsn - rno; 812 | } 813 | } 814 | close(*fd); 815 | if (fin(chld) != 0) { 816 | rc = -1; 817 | } 818 | return rc; 819 | } 820 | 821 | 822 | /* public api */ 823 | #if !defined PATH_MAX 824 | # define PATH_MAX (256U) 825 | #endif /* !PATH_MAX */ 826 | 827 | int 828 | yuck_version(struct yuck_version_s *restrict v, const char *path) 829 | { 830 | char cwd[PATH_MAX]; 831 | char fn[PATH_MAX]; 832 | int rc = -1; 833 | 834 | /* initialise result structure */ 835 | memset(v, 0, sizeof(*v)); 836 | 837 | if (getcwd(cwd, sizeof(cwd)) == NULL) { 838 | return -1; 839 | } 840 | 841 | switch ((v->scm = find_scm(fn, sizeof(fn), path))) { 842 | case YUCK_SCM_ERROR: 843 | case YUCK_SCM_TARBALL: 844 | default: 845 | /* can't determine version numbers in tarball, can we? */ 846 | return -1; 847 | case YUCK_SCM_GIT: 848 | case YUCK_SCM_BZR: 849 | case YUCK_SCM_HG: 850 | if (chdir(fn) < 0) { 851 | break; 852 | } 853 | switch (v->scm) { 854 | case YUCK_SCM_GIT: 855 | rc = git_version(v); 856 | break; 857 | case YUCK_SCM_BZR: 858 | rc = bzr_version(v); 859 | break; 860 | case YUCK_SCM_HG: 861 | rc = hg_version(v); 862 | break; 863 | default: 864 | break; 865 | } 866 | if (chdir(cwd) < 0) { 867 | /* oh big cluster fuck */ 868 | rc = -1; 869 | } 870 | break; 871 | } 872 | return rc; 873 | } 874 | 875 | int 876 | yuck_version_read(struct yuck_version_s *restrict ref, const char *fn) 877 | { 878 | int rc = 0; 879 | int fd; 880 | 881 | /* initialise result structure */ 882 | memset(ref, 0, sizeof(*ref)); 883 | 884 | if (fn[0U] == '-' && fn[1U] == '\0') { 885 | fd = STDIN_FILENO; 886 | } else if ((fd = open(fn, O_RDONLY)) < 0) { 887 | return -1; 888 | } 889 | /* otherwise read and parse the string */ 890 | with (char buf[256U]) { 891 | ssize_t nrd; 892 | char *bp; 893 | 894 | if ((nrd = read(fd, buf, sizeof(buf))) <= 0) { 895 | /* no version then aye */ 896 | rc = -1; 897 | break; 898 | } else if ((bp = memchr(buf, '\n', nrd)) != NULL) { 899 | /* just go with the first line */ 900 | *bp = '\0'; 901 | nrd = bp - buf; 902 | } else if ((size_t)nrd < sizeof(buf)) { 903 | /* finalise with \nul */ 904 | buf[nrd] = '\0'; 905 | } else { 906 | /* finalise with \nul, cutting off the last byte */ 907 | buf[--nrd] = '\0'; 908 | } 909 | /* otherwise just read him */ 910 | rc = rd_version(ref, buf, nrd); 911 | } 912 | close(fd); 913 | return rc; 914 | } 915 | 916 | ssize_t 917 | yuck_version_write_fd(int fd, const struct yuck_version_s *ref) 918 | { 919 | char buf[256U]; 920 | ssize_t nwr; 921 | 922 | if ((nwr = wr_version(buf, sizeof(buf), ref)) <= 0) { 923 | return -1; 924 | } 925 | /* otherwise write */ 926 | buf[nwr++] = '\n'; 927 | return write(fd, buf, nwr); 928 | } 929 | 930 | int 931 | yuck_version_write(const char *fn, const struct yuck_version_s *ref) 932 | { 933 | int rc = 0; 934 | int fd; 935 | 936 | if (fn[0U] == '-' && fn[1U] == '\0') { 937 | fd = STDOUT_FILENO; 938 | } else if ((fd = open(fn, O_RDWR | O_CREAT | O_TRUNC, 0666)) < 0) { 939 | return -1; 940 | } 941 | if (yuck_version_write_fd(fd, ref) < 0) { 942 | rc = -1; 943 | } 944 | close(fd); 945 | return rc; 946 | } 947 | 948 | int 949 | yuck_version_cmp(yuck_version_t v1, yuck_version_t v2) 950 | { 951 | if (v1->dist == 0U && v2->dist == 0U) { 952 | /* must be a tag then, innit? */ 953 | return memcmp(v1->vtag, v2->vtag, sizeof(v1->vtag)); 954 | } 955 | /* just brute force the comparison, consider -dirty > -clean */ 956 | return memcmp(v1, v2, sizeof(*v1)); 957 | } 958 | 959 | 960 | #if defined BOOTSTRAP 961 | int 962 | main(int argc, char *argv[]) 963 | { 964 | /* usage would be yuck-scmver SCMDIR [REFERENCE] */ 965 | static struct yuck_version_s v[1U]; 966 | int rc = 0; 967 | 968 | /* prefer reference file */ 969 | if (argc > 2 && (rc = yuck_version_read(v, argv[2U])) == 0) { 970 | /* just use this one */ 971 | ; 972 | } else { 973 | rc = yuck_version(v, argv[1U]); 974 | } 975 | if (rc == 0) { 976 | fputs("define(YUCK_SCMVER_VERSION, ", stdout); 977 | fputs(v->vtag, stdout); 978 | if (v->scm > YUCK_SCM_TARBALL && v->dist) { 979 | fputc('.', stdout); 980 | fputs(yscm_strs[v->scm], stdout); 981 | fprintf(stdout, "%u.%0*x", 982 | v->dist, 983 | (int)(v->rvsn & 0x07U), v->rvsn >> 4U); 984 | } 985 | if (v->dirty) { 986 | fputs(".dirty", stdout); 987 | } 988 | fputs(")\n", stdout); 989 | } 990 | return -rc; 991 | } 992 | #endif /* BOOTSTRAP */ 993 | 994 | 995 | #if defined CONFIGURE 996 | int 997 | main(int argc, char *argv[]) 998 | { 999 | /* usage would be yuck-scmver [REFERENCE] */ 1000 | static struct yuck_version_s v[1U]; 1001 | int rc = 0; 1002 | 1003 | if (argc > 1) { 1004 | rc = yuck_version_read(v, argv[1U]); 1005 | #if defined VERSION_FILE 1006 | } else if ((rc = yuck_version_read(v, VERSION_FILE)) == 0) { 1007 | ; 1008 | #endif /* VERSION_FILE */ 1009 | } else { 1010 | rc = yuck_version(v, NULL); 1011 | } 1012 | /* print if successful */ 1013 | if (rc == 0) { 1014 | fputs(v->vtag, stdout); 1015 | if (v->scm > YUCK_SCM_TARBALL && v->dist) { 1016 | fputc('.', stdout); 1017 | fputs(yscm_strs[v->scm], stdout); 1018 | fprintf(stdout, "%u.%0*x", 1019 | v->dist, 1020 | (int)(v->rvsn & 0x07U), v->rvsn >> 4U); 1021 | } 1022 | if (v->dirty) { 1023 | fputs(".dirty", stdout); 1024 | } 1025 | fputc('\n', stdout); 1026 | } 1027 | return -rc; 1028 | } 1029 | #endif /* CONFIGURE */ 1030 | 1031 | /* yuck-scmver.c ends here */ 1032 | -------------------------------------------------------------------------------- /build-aux/yuck-scmver.h: -------------------------------------------------------------------------------- 1 | /*** yuck-version.h -- snarf versions off project cwds 2 | * 3 | * Copyright (C) 2013-2016 Sebastian Freundt 4 | * 5 | * Author: Sebastian Freundt 6 | * 7 | * This file is part of yuck. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the author nor the names of any contributors 21 | * may be used to endorse or promote products derived from this 22 | * software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 25 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 31 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 32 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 34 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | ***/ 37 | #if !defined INCLUDED_yuck_version_h_ 38 | #define INCLUDED_yuck_version_h_ 39 | 40 | #include "stdlib.h" 41 | 42 | typedef const struct yuck_version_s *yuck_version_t; 43 | 44 | typedef enum { 45 | YUCK_SCM_ERROR = -1, 46 | YUCK_SCM_TARBALL, 47 | YUCK_SCM_GIT, 48 | YUCK_SCM_BZR, 49 | YUCK_SCM_HG, 50 | } yuck_scm_t; 51 | 52 | struct yuck_version_s { 53 | yuck_scm_t scm; 54 | unsigned int dirty:1U; 55 | char vtag[16U]; 56 | unsigned int dist; 57 | /* up to 28bits of revision id (hash for git), 58 | * the lower 4bits denote the length */ 59 | unsigned int rvsn; 60 | }; 61 | 62 | extern const char *const yscm_strs[]; 63 | 64 | 65 | /* public api */ 66 | /** 67 | * Determine SCM version of file(s) in PATH. */ 68 | extern int yuck_version(struct yuck_version_s *restrict v, const char *path); 69 | 70 | /** 71 | * Read a reference file FN and return scm version information. */ 72 | extern int yuck_version_read(struct yuck_version_s *restrict, const char *fn); 73 | 74 | /** 75 | * Write scm version information in V to reference file FN. */ 76 | extern int yuck_version_write(const char *fn, const struct yuck_version_s *v); 77 | 78 | /** 79 | * Write scm version into buffer. */ 80 | extern ssize_t yuck_version_write_fd(int fd, const struct yuck_version_s *v); 81 | 82 | /** 83 | * Compare two version objects, return <0 if V1 < V2, >0 if V1 > V2 and 84 | * 0 if V1 and V2 are considered equal. */ 85 | extern int yuck_version_cmp(yuck_version_t v1, yuck_version_t v2); 86 | 87 | #endif /* INCLUDED_yuck_version_h_ */ 88 | -------------------------------------------------------------------------------- /build-aux/yuck-scmver.m4: -------------------------------------------------------------------------------- 1 | changequote`'changequote([,])dnl 2 | divert([-1]) 3 | changecom([##]) 4 | 5 | ## we used to define YUCK_SCMVER_VERSION in m4 terms here 6 | ## however in C it's so much easier to come by with the same thing 7 | ## so we reserve this file for future (m4-based) goodness 8 | 9 | divert[]dnl 10 | changequote()dnl 11 | -------------------------------------------------------------------------------- /build-aux/yuck.am: -------------------------------------------------------------------------------- 1 | ## automake rules for yuck 2 | ## set the following variables in the including Makefile.am beforehand 3 | ## noinst_PROGRAMS 4 | ## BUILT_SOURCES 5 | ## EXTRA_DIST 6 | ## DISTCLEANFILES 7 | 8 | yuck_CPPFLAGS = -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -D_BSD_SOURCE 9 | yuck_CPPFLAGS += -D_ALL_SOURCE 10 | yuck_CPPFLAGS += -D_DARWIN_C_SOURCE 11 | yuck_CPPFLAGS += -D_NETBSD_SOURCE 12 | 13 | noinst_PROGRAMS += yuck-bootstrap 14 | yuck_bootstrap_SOURCES = yuck.c 15 | yuck_bootstrap_CPPFLAGS = $(yuck_CPPFLAGS) 16 | yuck_bootstrap_CPPFLAGS += -DBOOTSTRAP 17 | yuck_bootstrap_CPPFLAGS += -DWITH_SCMVER 18 | 19 | noinst_PROGRAMS += yuck 20 | yuck_SOURCES = yuck.c yuck.yuck 21 | yuck_SOURCES += yuck-scmver.c yuck-scmver.h yuck-scmver.m4 22 | yuck_SOURCES += yuck.m4 23 | yuck_SOURCES += yuck-coru.c.m4 yuck-coru.h.m4 24 | yuck_SOURCES += yuck.man.m4 25 | yuck_CPPFLAGS += -DYUCK_TEMPLATE_PATH='"$(abs_srcdir)"' 26 | yuck_CPPFLAGS += -DWITH_SCMVER 27 | BUILT_SOURCES += yuck.yucc 28 | 29 | ## aix make wants all prereq's to exist 30 | EXTRA_DIST += yuck.m4i 31 | EXTRA_DIST += yuck.yucc 32 | 33 | DISTCLEANFILES += yuck.m4i yuck.yucc 34 | 35 | ## also include custom Makefile recipes 36 | include $(srcdir)/yuck.mk 37 | -------------------------------------------------------------------------------- /build-aux/yuck.m4: -------------------------------------------------------------------------------- 1 | changequote`'changequote([,])dnl 2 | divert([-1]) 3 | 4 | ## this is a little domain language for the yuck processor 5 | 6 | # foreachq(x, [item_1, item_2, ..., item_n], stmt) 7 | # quoted list, alternate improved version 8 | define([foreachq], [ifelse([$2], [], [], 9 | [pushdef([$1])_$0([$1], [$3], [], $2)popdef([$1])])]) 10 | define([_foreachq], [ifelse([$#], [3], [], 11 | [define([$1], [$4])$2[]$0([$1], [$2], 12 | shift(shift(shift($@))))])]) 13 | 14 | #### append $2 to $1, separated by $3. 15 | define([append], [define([$1], ifdef([$1], [defn([$1])[$3]])[$2])]) 16 | ## like append, but append only non-empty arguments 17 | define([append_ne], [ifelse([$2], [], [], [append([$1], [$2], [$3])])]) 18 | ## like append_ne, but append only non-existing arguments 19 | define([append_nene], [ifelse(index([$3]defn([$1])[$3], [$3$2$3]), [-1], 20 | [append_ne([$1], [$2], [$3])])]) 21 | 22 | define([appendq], [define([$1], ifdef([$1], [defn([$1])[$3]])dquote([$2]))]) 23 | ## like appendq, but append only non-empty arguments 24 | define([appendq_ne], [ifelse([$2], [], [], [append([$1], dquote([$2]), [$3])])]) 25 | ## like appendq_ne, but append only non-existing arguments 26 | define([appendq_nene], [ifelse(index([$3]defn([$1])[$3], [$3]dquote($2)[$3]), [-1], 27 | [appendq_ne([$1], [$2], [$3])])]) 28 | 29 | define([first_nonnil], [ifelse([$#], [0], [], [$1], [], 30 | [first_nonnil(shift($@))], [], [], [$1])]) 31 | define([first], [_first($*)]) 32 | define([_first], [$1]) 33 | define([second], [_second($*)]) 34 | define([_second], [$2]) 35 | define([thirds], [_thirds($*)]) 36 | define([_thirds], [quote(shift(shift($@)))]) 37 | 38 | define([quote], [ifelse([$#], [0], [], [[$*]])]) 39 | define([dquote], [[$@]]) 40 | define([equote], [dquote($*)]) 41 | 42 | define([backquote], [_$0([$1], [(], -=<{($1)}>=-, [}>=-])]) 43 | define([_backquote], [dnl 44 | ifelse([$4], [}>=-], [dnl 45 | changequote([-=<{$2], [)}>=-])$3changequote([, ])], [dnl else 46 | $0([$1], [($2], -=<{($2$1)}>=-, [}>=-])_ignore$2])]) 47 | 48 | define([_ignore]) 49 | 50 | define([_splice], [ifelse(eval([$#] > [3]), [0], [[$1], [$2], [$3]], [[$1], [$2], [$3], _splice([$1], shift(shift(shift($@))))])]) 51 | 52 | define([cond], [ifelse([$#], [0], [], [$#], [1], [$1], [_$0($@)])]) 53 | define([_cond], [dnl 54 | ifelse([$1], [$2], [$3], 55 | [$#], [3], [], 56 | [$#], [4], [$4], 57 | [$0([$1], shift(shift(shift($@))))])]) 58 | 59 | define([downcase], [dnl 60 | translit([$1], [ABCDEFGHIJKLMNOPQRSTUVWXYZ], [abcdefghijklmnopqrstuvwxyz])[]dnl 61 | ]) 62 | 63 | define([upcase], [dnl 64 | translit([$1], [abcdefghijklmnopqrstuvwxyz], [ABCDEFGHIJKLMNOPQRSTUVWXYZ])[]dnl 65 | ]) 66 | 67 | ## select a diversion, clearing all other diversions 68 | define([select_divert], [divert[]undivert($1)[]divert(-1)[]undivert[]divert(0)]) 69 | 70 | 71 | define([yuck_set_version], [dnl 72 | define([YUCK_VERSION], [$1]) 73 | ]) 74 | 75 | ## yuck_set_umbrella([ident], [umbrella], [[posarg]]) 76 | define([yuck_set_umbrella], [dnl 77 | define([YUCK_UMB], [$1]) 78 | define([YUCK_UMB_STR], [$2]) 79 | define([YUCK_UMB_POSARG], [$3]) 80 | ]) 81 | 82 | ## yuck_set_umbrella_desc([ident], [desc]) 83 | define([yuck_set_umbrella_desc], [dnl 84 | define([YUCK_UMB_$1_desc], [$2]) 85 | 86 | ## define yuck_add_inter here, corresponding to IDENT 87 | define([YUCK_INTER_CMD], [YUCK_UMB_$1_desc]) 88 | ]) 89 | 90 | ## yuck_set_umbrella_max_posargs([ident], [N]) 91 | define([yuck_set_umbrella_max_posargs], [dnl 92 | define([YUCK_UMB_$1_max_posargs], [$2]) 93 | define([YUCK_MAX_POSARGS], [$2]) 94 | ]) 95 | 96 | ## yuck_add_command([ident], [command], [[posarg]]) 97 | define([yuck_add_command], [dnl 98 | define([YUCK_CMD], [$1]) 99 | append_nene([YUCK_ALL_CMDS], [$1], [,]) 100 | define([YUCK_$1_STR], [$2]) 101 | define([YUCK_POSARG_$1], [$3]) 102 | ]) 103 | 104 | ## yuck_set_command_desc([ident], [desc]) 105 | define([yuck_set_command_desc], [dnl 106 | define([YUCK_CMD_$1_desc], [$2]) 107 | 108 | ## define yuck_add_inter here, corresponding to IDENT 109 | define([YUCK_INTER_CMD], [YUCK_CMD_$1_desc]) 110 | ]) 111 | 112 | ## yuck_set_umbrella_max_posargs([ident], [N]) 113 | define([yuck_set_command_max_posargs], [dnl 114 | define([YUCK_CMD_$1_max_posargs], [$2]) 115 | ]) 116 | 117 | ## yuck_add_inter([desc]) 118 | #### append interim DESC to last command desc 119 | define([yuck_add_inter], [dnl 120 | append(defn([YUCK_INTER_CMD]), [$1], [ 121 | 122 | ]) 123 | ]) 124 | 125 | ## yuck_add_option([ident], [short], [long], [type]) 126 | define([yuck_add_option], [dnl 127 | ## quote the elements of the type arg first 128 | ## before any possible expansion is in scope 129 | pushdef([type], equote([$4])) 130 | pushdef([ident], [$1]) 131 | pushdef([cmd], defn([YUCK_CMD])) 132 | 133 | ifelse([$2], [], [], 134 | index([0123456789], [$2]), [-1], [], 135 | [dnl else 136 | define([YUCK_SHORTS_HAVE_NUMERALS], [1]) 137 | ]) 138 | 139 | ifdef([YUCK_]defn([cmd])[_]defn([ident])[_canon], [], [dnl 140 | ## process only if new 141 | appendq_ne([YUCK_]defn([cmd])[_I], defn([ident]), [,]) 142 | 143 | ## forward maps 144 | define([YUCK_]defn([cmd])[_]defn([ident])[_canon], defn([ident])) 145 | define([YUCK_]defn([cmd])[_]defn([ident])[_type], defn([type])) 146 | 147 | ## reverse maps 148 | define([YUCK_]defn([cmd])[_]defn([ident])[_short], [$2]) 149 | define([YUCK_]defn([cmd])[_]defn([ident])[_long], [$3]) 150 | ]) 151 | 152 | popdef([ident]) 153 | popdef([cmd]) 154 | popdef([type]) 155 | ]) 156 | 157 | ## yuck_set_option_desc([ident], [desc]) 158 | define([yuck_set_option_desc], [dnl 159 | define([YUCK_]defn([YUCK_CMD])[_$1_desc], [$2]) 160 | ]) 161 | 162 | ## some yopts here, mostly flags 163 | define([yuck_setopt_allow_unknown_dash], [dnl 164 | define([YOPT_ALLOW_UNKNOWN_DASH]) 165 | ]) 166 | 167 | define([yuck_setopt_allow_unknown_dashdash], [dnl 168 | define([YOPT_ALLOW_UNKNOWN_DASHDASH]) 169 | ]) 170 | 171 | 172 | ## helpers for the m4c and m4h 173 | 174 | ## yuck_canon([opt], [[cmd]]) 175 | define([yuck_canon], [defn([YUCK_$2_$1_canon])]) 176 | 177 | ## yuck_option_type([opt], [[cmd]]) 178 | define([yuck_option_type], [defn([YUCK_$2_$1_type])]) 179 | 180 | ## yuck_type([type-spec]) 181 | define([yuck_type], [first([$1])]) 182 | 183 | ## yuck_type_name([type-spec]) 184 | define([yuck_type_name], [second([$1])]) 185 | 186 | ## yuck_type_sufx([type-spec]) 187 | define([yuck_type_sufx], [thirds([$1])]) 188 | 189 | ## yuck_slot_identifier([option], [[cmd]]) 190 | define([yuck_slot_identifier], [dnl 191 | pushdef([canon], yuck_canon([$1], [$2]))dnl 192 | pushdef([type], yuck_option_type([$1], [$2]))dnl 193 | dnl 194 | defn([canon])[_]yuck_type(defn([type]))[]dnl 195 | cond(yuck_type_sufx(defn([type])), [mul], [s], [mul,opt], [s])[]dnl 196 | dnl 197 | popdef([canon])dnl 198 | popdef([type])dnl 199 | ]) 200 | 201 | ## yuck_cnt_slot([option], [[cmd]]) 202 | define([yuck_cnt_slot], [dnl 203 | pushdef([type], yuck_option_type([$1], [$2]))dnl 204 | ifelse(yuck_type(defn([type])), [arg], [dnl 205 | ifelse(first(yuck_type_sufx(defn([type]))), [mul], [dnl 206 | pushdef([idn], [yuck_canon([$1], [$2])[_nargs]])dnl 207 | ifelse([$2], [], [idn], [$2.idn])[]dnl 208 | popdef([idn])dnl 209 | ])[]dnl 210 | ])[]dnl 211 | popdef([type])dnl 212 | ]) 213 | 214 | ## yuck_slot([option], [[cmd]]) 215 | define([yuck_slot], [dnl 216 | pushdef([idn], yuck_slot_identifier([$1], [$2]))dnl 217 | dnl 218 | ifelse([$2], [], defn([idn]), [$2.]defn([idn]))[]dnl 219 | dnl 220 | popdef([idn])dnl 221 | ]) 222 | 223 | ## yuck_iftype([opt], [cmd], [type], [body], [[type], [body]]...) 224 | define([yuck_iftype], [dnl 225 | pushdef([type], yuck_option_type([$1], [$2]))dnl 226 | pushdef([tsuf], yuck_type_sufx(defn([type])))dnl 227 | pushdef([res], yuck_type(defn([type])))dnl 228 | append_ne([res], defn([tsuf]), [,])[]dnl 229 | []ifelse(_splice(defn([res]), shift(shift($@))))[]dnl 230 | popdef([tsuf])dnl 231 | popdef([type])dnl 232 | popdef([res])dnl 233 | ]) 234 | 235 | ## yuck_umbcmds(), umbrella + commands 236 | define([yuck_umbcmds], [ifdef([YUCK_ALL_CMDS], [[,]defn([YUCK_ALL_CMDS])], dquote([[]]))]) 237 | 238 | ## yuck_cmds(), just the commands 239 | define([yuck_cmds], [defn([YUCK_ALL_CMDS])]) 240 | 241 | ## yuck_cmd([command]) 242 | define([yuck_cmd], [upcase(defn([YUCK_UMB]))[_CMD_]ifelse([$1], [], [NONE], [upcase([$1])])]) 243 | 244 | ## yuck_cmd_string 245 | define([yuck_cmd_string], [defn([YUCK_]$1[_STR])]) 246 | 247 | ## yuck_cmd_posarg 248 | define([yuck_cmd_posarg], [defn([YUCK_POSARG_]$1)]) 249 | 250 | ## yuck_umb_desc([[umb]]) getter for the umbrella description 251 | define([yuck_umb_desc], [defn([YUCK_UMB_]ifelse([$1], [], defn([YUCK_UMB]), [$1])[_desc])]) 252 | 253 | ## yuck_cmd_desc([cmd]) getter for the command description 254 | define([yuck_cmd_desc], [defn([YUCK_CMD_$1_desc])]) 255 | 256 | ## yuck_idents([cmd]) 257 | define([yuck_idents], [defn([YUCK_$1_I])]) 258 | 259 | ## yuck_short([ident], [[cmd]]) 260 | define([yuck_short], [defn([YUCK_$2_$1_short])]) 261 | 262 | ## yuck_long([ident], [[cmd]]) 263 | define([yuck_long], [defn([YUCK_$2_$1_long])]) 264 | 265 | ## yuck_option_desc([ident], [[cmd]]) 266 | define([yuck_option_desc], [defn([YUCK_$2_$1_desc])]) 267 | 268 | ## type actions 269 | define([_yuck_option_action], [dnl 270 | pushdef([type], yuck_option_type([$1], [$2]))dnl 271 | pushdef([prim], yuck_type(defn([type])))dnl 272 | pushdef([sufx], yuck_type_sufx(defn([type])))dnl 273 | quote([yuck_]defn([prim])ifelse(defn([sufx]), [], [], [_]translit(defn([sufx]), [,], [_]))[_action][(quote([$1]), quote([$2]))])dnl 274 | popdef([type])dnl 275 | popdef([prim])dnl 276 | popdef([sufx])dnl 277 | ])dnl 278 | define([yuck_expand], [$1]) 279 | define([yuck_option_action], [yuck_expand(_$0([$1], [$2]))]) 280 | 281 | ## yuck_option_help_lhs([ident], [[cmd]]) 282 | define([yuck_option_help_lhs], [dnl 283 | pushdef([s], [backquote([yuck_short([$1], [$2])])])dnl 284 | pushdef([l], [backquote([yuck_long([$1], [$2])])])dnl 285 | pushdef([type], yuck_option_type([$1], [$2]))dnl 286 | pushdef([prel], ifelse(l, [], [], [=]))dnl 287 | pushdef([yuck_arg_action], [defn([prel])[]yuck_type_name(yuck_option_type([$1], [$2]))])dnl 288 | pushdef([yuck_arg_opt_action], [defn([prel])[]yuck_type_name(yuck_option_type([$1], [$2]))])dnl 289 | pushdef([yuck_arg_mul_action], [defn([prel])[]yuck_type_name(yuck_option_type([$1], [$2]))...])dnl 290 | pushdef([yuck_arg_mul_opt_action], [defn([prel])[]yuck_type_name(yuck_option_type([$1], [$2]))...])dnl 291 | [ ]ifelse(s, [], [ ], [-s[]ifelse(l, [], [], [[, ]])])[]dnl 292 | ifelse(l, [], [], [--l])[]dnl 293 | ifelse(yuck_type(defn([type])), [arg], [dnl 294 | ifelse(l, [], [ ], [])backquote([yuck_option_action([$1], [$2])])[]dnl 295 | ])[]dnl 296 | popdef([type])dnl 297 | popdef([prel])dnl 298 | popdef([s])dnl 299 | popdef([l])dnl 300 | popdef([yuck_arg_action])dnl 301 | popdef([yuck_arg_opt_action])dnl 302 | popdef([yuck_arg_mul_action])dnl 303 | popdef([yuck_arg_mul_opt_action])dnl 304 | ]) 305 | 306 | define([xleft], [_$0([$1], 0, [$2])]) 307 | define([_xleft], [dnl 308 | ifelse(eval(incr(incr([$2])) <= [$3]), [1], [dnl 309 | substr([$1], [$2], 2)[]$0([$1], incr(incr([$2])), [$3])[]dnl 310 | ], eval(incr([$2]) <= [$3]), [1], [dnl 311 | substr([$1], [$2], 1)[]dnl 312 | ])[]dnl 313 | ]) 314 | 315 | define([xright], [_$0([$1], [$2])]) 316 | define([_xright], [dnl 317 | ifelse(eval([$2] >= len([$1])), [0], [dnl 318 | substr([$1], [$2], 2)[]$0([$1], incr(incr([$2])))[]dnl 319 | ])[]dnl 320 | ]) 321 | 322 | define([yuck_esc], [backquote([_$0([$1], [$2], [$3], [$4])])]) 323 | define([_yuck_esc], [dnl 324 | pushdef([__next_sep], index([$1], [$2]))[]dnl 325 | ifelse([$1], [], [], defn([__next_sep]), [-1], [$4[$1]], [dnl 326 | [$4]xleft([$1], defn([__next_sep]))[$3]dnl 327 | $0(backquote([xright([$1], eval(defn([__next_sep]) + len([$2])))]), [$2], [$3], [$4])[]dnl 328 | ])[]dnl 329 | popdef([__next_sep])dnl 330 | ])dnl 331 | 332 | define([yuck_indent_line], [yuck_esc([$1], [ 333 | ], [ 334 | ], [ ])]) 335 | 336 | ## yuck_option_help_line([ident], [[cmd]]) 337 | define([yuck_option_help_line], [dnl 338 | pushdef([lhs], [backquote([yuck_option_help_lhs([$1], [$2])])])dnl 339 | pushdef([desc], [yuck_option_desc([$1], [$2])])dnl 340 | pushdef([indesc], [yuck_indent_line(backquote([desc]))])dnl 341 | pushdef([lenlhs], len(lhs))dnl 342 | ifelse(indesc, [], [lhs], 343 | eval(lenlhs >= 23), [0], [dnl 344 | lhs[]backquote([xright(indesc, lenlhs)])[]dnl 345 | ], eval(lenlhs >= 24), [0], [dnl 346 | lhs[]backquote([xright(indesc, decr(lenlhs))])[]dnl 347 | ], [dnl 348 | lhs[ 349 | ]backquote([indesc])[]dnl 350 | ]) 351 | popdef([lenlhs])dnl 352 | popdef([indesc])dnl 353 | popdef([desc])dnl 354 | popdef([lhs])dnl 355 | ]) 356 | 357 | ## yuck_first_line([string]) 358 | define([yuck_first_line], [dnl 359 | pushdef([lnlen], [index([$1], [ 360 | ])])dnl 361 | backquote([ifelse(lnlen, -1, [$1], [xleft([$1], lnlen)])])[]dnl 362 | popdef([lnlen])dnl 363 | ]) 364 | 365 | ## yuck_cmd_line([cmd]) 366 | define([yuck_cmd_line], [dnl 367 | pushdef([lhs], [backquote([yuck_cmd_string([$1])])])dnl 368 | pushdef([indesc], [dnl 369 | yuck_first_line(backquote([yuck_cmd_desc([$1])]))])dnl 370 | pushdef([lenlhs], len(lhs))dnl 371 | ifelse(indesc, [], [lhs], 372 | eval(lenlhs >= 11), [0], [dnl 373 | lhs[]backquote([xright(indesc, lenlhs)])[]dnl 374 | ], eval(lenlhs >= 12), [0], [dnl 375 | lhs[]backquote([xright(indesc, decr(lenlhs))])[]dnl 376 | ], [dnl 377 | lhs 378 | backquote([indesc])[]dnl 379 | ])dnl 380 | popdef([lenlhs])dnl 381 | popdef([indesc])dnl 382 | popdef([lhs])dnl 383 | ]) 384 | 385 | ## \n -> \\n\\ 386 | define([yuck_esc_newline], [yuck_esc([$1], [ 387 | ], [\n\ 388 | ])]) 389 | 390 | ## " -> \" 391 | define([yuck_esc_quote], [yuck_esc([$1], ["], [\"])])dnl " 392 | 393 | ## \ -> \\ 394 | define([yuck_esc_backslash], [yuck_esc([$1], [\], [\\])])dnl 395 | 396 | define([yuck_C_literal], [dnl 397 | yuck_esc_newline(yuck_esc_quote(yuck_esc_backslash([$1])))[]dnl 398 | ])dnl 399 | 400 | 401 | ## coroutine stuff 402 | define([yield], [goto $1; back_from_$1:]) 403 | define([coroutine], [define([this_coru], [$1])$1:]) 404 | define([resume], [goto back_from_[]this_coru]) 405 | define([resume_at], [goto $1]) 406 | define([quit], [goto out]) 407 | 408 | divert[]dnl 409 | changequote`'dnl 410 | -------------------------------------------------------------------------------- /build-aux/yuck.man.m4: -------------------------------------------------------------------------------- 1 | .\" auto generated by yuck -*- nroff -*- 2 | changequote`'changequote([,])dnl 3 | ifdef([YUCK_VERSION], [], [dnl else (!YUCK_VERSION) 4 | ifdef([YUCK_SCMVER_VERSION], [dnl then 5 | define([YUCK_VERSION], [YUCK_SCMVER_VERSION])[]dnl 6 | ], [dnl else (!YUCK_SCMVER_VERSION) 7 | define([YUCK_VERSION], [unknown])dnl 8 | ])dnl 9 | ])dnl 10 | define([ucase], [translit([$1], [abcdefghijklmnopqrstuvwxyz], [ABCDEFGHIJKLMNOPQRSTUVWXYZ])])dnl 11 | ifdef([YUCK_PKG_STR], [], [define([YUCK_PKG_STR], [YUCK_UMB_STR])])dnl 12 | .TH ucase(YUCK_UMB_STR) "1" "YUCK_MAN_DATE" "YUCK_PKG_STR YUCK_VERSION" "User Commands" 13 | .SH NAME 14 | pushdef([umb_desc], [yuck_first_line(yuck_umb_desc())])dnl 15 | pushdef([alt_desc], [manual page for YUCK_PKG_STR YUCK_VERSION])dnl 16 | YUCK_UMB_STR - first_nonnil(umb_desc, alt_desc) 17 | popdef([umb_desc])dnl 18 | popdef([alt_desc])dnl 19 | .SH SYNOPSIS 20 | .B YUCK_UMB_STR 21 | [[\fIOPTION\fR]]...[ ]dnl 22 | ifelse(yuck_cmds(), [], [], [\fICOMMAND\fR]) 23 | []dnl 24 | yuck_esc(dnl 25 | yuck_esc(dnl 26 | yuck_esc(dnl 27 | yuck_esc(dnl 28 | yuck_esc(translit(defn([YUCK_UMB_POSARG]), [[]], []), 29 | [ -], [\fR -]), 30 | [ ], [ \fI]), 31 | [], [\fR\fI]), 32 | [], [\fR]), 33 | [...], [\fR...]) 34 | .SH DESCRIPTION 35 | yuck_umb_desc() 36 | ifelse(yuck_cmds(), [], [], [dnl 37 | 38 | .PP 39 | \fICOMMAND\fR may be one of: 40 | foreachq([__CMD__], yuck_cmds(), [dnl 41 | .TP 42 | .B yuck_cmd_string(__CMD__) 43 | . 44 | yuck_cmd_desc(__CMD__) 45 | ])dnl 46 | ])dnl 47 | 48 | .PP 49 | define([yuck_man_option], [dnl 50 | pushdef([lhs], [yuck_option_help_lhs([$1], [$2])])dnl 51 | yuck_esc(dnl 52 | yuck_esc(dnl 53 | yuck_esc(dnl 54 | yuck_esc(dnl 55 | yuck_esc(dnl 56 | yuck_esc(dnl 57 | yuck_esc(lhs, 58 | [,], [\fR,]), 59 | [ -], [ \fB-]), 60 | [], [\fR]), 61 | [=], [\fR=]), 62 | [=], [=\fI]), 63 | [], [\fR]), 64 | [...], [\fR...])dnl 65 | popdef([lhs])dnl 66 | ]) 67 | Recognized \fIOPTION\fRs: 68 | foreachq([__IDN__], yuck_idents(), [dnl 69 | .TP 70 | .B yuck_man_option(defn([__IDN__]), []) 71 | yuck_option_desc(defn([__IDN__]), []) 72 | ])dnl 73 | dnl 74 | ifelse(yuck_cmds(), [], [], [dnl 75 | .SH COMMANDS 76 | ])dnl 77 | foreachq([__CMD__], yuck_cmds(), [ 78 | .P 79 | .B YUCK_UMB_STR yuck_cmd_string(__CMD__) 80 | [[\fIOPTION\fR]]... 81 | yuck_esc(dnl 82 | yuck_esc(dnl 83 | yuck_esc(dnl 84 | yuck_esc(dnl 85 | yuck_esc(translit(yuck_cmd_posarg(__CMD__), [[]], []), 86 | [ -], [\fR -]), 87 | [ ], [ \fI]), 88 | [], [\fR\fI]), 89 | [], [\fR]), 90 | [...], [\fR...]) 91 | .br 92 | yuck_cmd_desc(C) 93 | .P 94 | \fIOPTION\fRs specific to the \fB[]yuck_cmd_string(__CMD__)\fR command: 95 | foreachq([__IDN__], yuck_idents(__CMD__), [dnl 96 | .TP 97 | .B yuck_option_help_lhs(defn([__IDN__]), defn([__CMD__])) 98 | yuck_option_desc(defn([__IDN__]), defn([__CMD__])) 99 | ])dnl 100 | ])dnl 101 | foreachq([__INC__], defn([YUCK_INCLUDES]), [dnl 102 | include(__INC__) 103 | ]) 104 | ifdef([YUCK_NFO_STR], [ 105 | .SH "SEE ALSO" 106 | The full documentation for 107 | .B YUCK_UMB_STR 108 | is maintained as a Texinfo manual. If the 109 | .B info 110 | and 111 | .B YUCK_UMB_STR 112 | programs are properly installed at your site, the command 113 | .IP 114 | .B info ifelse(YUCK_NFO_STR, YUCK_UMB_STR, [YUCK_NFO_STR], [(YUCK_NFO_STR)YUCK_UMB_STR]) 115 | .PP 116 | should give you access to the complete manual. 117 | ])dnl 118 | .\" yuck.m4man ends here 119 | changequote`'dnl 120 | -------------------------------------------------------------------------------- /build-aux/yuck.mk: -------------------------------------------------------------------------------- 1 | ## bootstrapping rules, made for inclusion in your own Makefiles 2 | yuck.m4i: yuck.yuck 3 | $(MAKE) $(AM_MAKEFLAGS) yuck-bootstrap 4 | $(AM_V_GEN) $(builddir)/yuck-bootstrap$(EXEEXT) $(srcdir)/yuck.yuck > $@ \ 5 | || { rm -f -- $@; false; } 6 | 7 | yuck.yucc: yuck.m4i yuck.m4 yuck-coru.h.m4 yuck-coru.c.m4 8 | $(AM_V_GEN) $(M4) $(srcdir)/yuck.m4 yuck.m4i \ 9 | $(srcdir)/yuck-coru.h.m4 $(srcdir)/yuck-coru.c.m4 | \ 10 | tr '\002\003\016\017' '[]()' > $@ \ 11 | || { rm -f -- $@; false; } 12 | -------------------------------------------------------------------------------- /build-aux/yuck.yuck: -------------------------------------------------------------------------------- 1 | Usage: yuck COMMAND [ARG]... 2 | 3 | Generate command line option parsers for umbrella commands. 4 | 5 | -k, --keep Keep intermediary files. 6 | -o, --output=FILE Output goes into FILE instead of stdout. 7 | 8 | 9 | ## Usages of the single commands 10 | Usage: yuck [OPTION]... gen [FILE]... 11 | 12 | Generate a parser from definitions in FILE, or stdin if omitted. 13 | 14 | -H, --header=FILE Also generate a header file. 15 | --no-auto-flags Do not add auto flags (--help, --version). 16 | --no-auto-actions Do not automatically handle auto flags. 17 | --version=VERSION Hardwire version number. 18 | --custom=FILE Include custom macros from FILE. 19 | 20 | 21 | ## GENMAN 22 | Usage: yuck [OPTION]... genman [FILE]... 23 | 24 | Generate a man page from definitions in FILE, or stdin if omitted. 25 | 26 | --version-string=STRING Hardwire version number as STRING. 27 | --version-file=FILE Hardwire version number from FILE. 28 | --package=STRING Use package STRING instead of umbrella. 29 | -i, --include=FILE... Include sections from FILE 30 | --info-page[=NAME] Include reference to Texinfo manual. 31 | 32 | 33 | ## GENDSL 34 | Usage: yuck gendsl [FILE]... 35 | 36 | Generate the intermediary description of definitions from FILE, or stdin 37 | if omitted. 38 | 39 | --no-auto-flags Do not add auto flags (--help, --version). 40 | --no-auto-actions Do not automatically handle auto flags. 41 | --version=VERSION Hardwire version number. 42 | 43 | 44 | ## VER 45 | Usage: yuck scmver [PATH] 46 | 47 | Guess version number for SCM controlled PATH. 48 | If PATH is a file run a transformation. 49 | If PATH is a directory just output the version number. 50 | 51 | Transformations are done through the m4 processor: 52 | YUCK_SCMVER_VTAG will expand to the tag name. 53 | YUCK_SCMVER_SCM will expand to the name of the SCM used. 54 | YUCK_SCMVER_DIST will expand to the distance, that is the number of 55 | commits since the last tag. 56 | YUCK_SCMVER_RVSN will expand to the current commit number. 57 | YUCK_SCMVER_FLAG_DIRTY is set for changes in the source tree that have 58 | not yet been committed. 59 | 60 | Definitions in the template yuck-scmver.m4 are prepended, and by 61 | default this defines the YUCK_SCMVER_VERSION macro that expands to the 62 | full version number. 63 | 64 | 65 | -v, --verbose Print additional information on stderr. 66 | --reference=FILE Store version reference in FILE and don't touch 67 | the output file if nothing has changed. 68 | -f, --force Force operation even if the current scm version 69 | coincides with the reference version. 70 | -n, --use-reference Use version number provided in the reference 71 | file instead of determining it. 72 | --ignore-noscm Don't treat no scm as error. 73 | 74 | 75 | Usage: yuck config 76 | 77 | Output config values for yuck. 78 | 79 | --m4 Print m4 value used during yuck build. 80 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl ------------------------------------------------------------------------- 2 | dnl Autoconf startup. 3 | dnl ------------------------------------------------------------------------- 4 | 5 | AC_PREREQ([2.63]) 6 | AC_INIT([sample], [0.1.2.GIT], [freundt@ga-group.nl]) 7 | AC_CONFIG_AUX_DIR([build-aux]) 8 | AC_CONFIG_HEADERS([src/config.h]) 9 | AC_CONFIG_MACRO_DIR([m4]) 10 | 11 | dnl ------------------------------------------------------------------------- 12 | dnl Local copyright notices. 13 | dnl ------------------------------------------------------------------------- 14 | 15 | AC_COPYRIGHT([dnl 16 | #### Configuration script for sample 17 | #### Copyright (C) 2010-2022 Sebastian Freundt 18 | 19 | ### Don't edit this script! 20 | ### This script was automatically generated by the `autoconf' program 21 | ### from the file `./configure.ac'. 22 | ### To rebuild it, execute the command 23 | ### autoreconf 24 | ]) 25 | 26 | AM_INIT_AUTOMAKE([foreign parallel-tests dist-xz color-tests]) 27 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 28 | 29 | ## the build chain 30 | AC_PROG_CC([icc cc gcc]) 31 | SXE_CHECK_CC([c11 c1x c99 gnu99]) 32 | SXE_CHECK_CFLAGS 33 | AC_CHECK_TOOLS([AR], [xiar ar], [false]) 34 | AC_C_BIGENDIAN 35 | 36 | ## check if yuck is globally available 37 | AX_CHECK_YUCK 38 | AX_YUCK_SCMVER([version.mk]) 39 | AC_CONFIG_LINKS([GNUmakefile:GNUmakefile]) 40 | 41 | ## out test suite 42 | AX_CHECK_CLITORIS 43 | 44 | ## output 45 | AC_CONFIG_FILES([Makefile]) 46 | AC_CONFIG_FILES([build-aux/Makefile]) 47 | AC_CONFIG_FILES([info/Makefile]) 48 | AC_CONFIG_FILES([src/Makefile]) 49 | AC_CONFIG_FILES([test/Makefile]) 50 | AC_OUTPUT 51 | 52 | echo 53 | echo 54 | echo "Build summary" 55 | echo "=============" 56 | echo 57 | echo "[[x]] sample" 58 | echo 59 | 60 | ## configure ends here 61 | dnl configure.ac ends here 62 | -------------------------------------------------------------------------------- /info/Makefile.am: -------------------------------------------------------------------------------- 1 | # Help the Developers and yourself. Just use the C locale and settings 2 | # for the compilation. They can still be overriden by make LANG= 3 | # but that is general a not very good idea 4 | LANG=C 5 | LC_ALL=C 6 | 7 | include $(top_builddir)/version.mk 8 | 9 | BUILT_SOURCES = 10 | EXTRA_DIST = $(BUILT_SOURCES) 11 | EXTRA_DIST += $(man1_MANS) 12 | SUFFIXES = 13 | 14 | man1_MANS = 15 | man1_MANS += sample.man 16 | 17 | EXTRA_DIST += author.h2m 18 | 19 | VPATH = $(srcdir):@top_srcdir@/src 20 | SUFFIXES += .yuck 21 | SUFFIXES += .man 22 | .yuck.man: 23 | $(AM_V_GEN) PATH="$(top_builddir)/build-aux:$${PATH}" \ 24 | yuck$(EXEEXT) genman \ 25 | --package "$(PACKAGE_NAME)" \ 26 | -i "$(srcdir)/author.h2m" \ 27 | -o $@ --version-file "$(top_builddir)/.version" $< 28 | 29 | ## Makefile.am ends here 30 | -------------------------------------------------------------------------------- /info/author.h2m: -------------------------------------------------------------------------------- 1 | [Author] 2 | Written by Sebastian Freundt 3 | 4 | [Reporting bugs] 5 | Report bugs to: https://github.com/hroptatyr/sample/issues 6 | 7 | -------------------------------------------------------------------------------- /m4/clitoris.m4: -------------------------------------------------------------------------------- 1 | dnl clitoris.m4 --- checks necessary for the clitoris test harness 2 | dnl 3 | dnl Copyright (C) 2013-2016 Sebastian Freundt 4 | dnl 5 | dnl Author: Sebastian Freundt 6 | dnl 7 | dnl Redistribution and use in source and binary forms, with or without 8 | dnl modification, are permitted provided that the following conditions 9 | dnl are met: 10 | dnl 11 | dnl 1. Redistributions of source code must retain the above copyright 12 | dnl notice, this list of conditions and the following disclaimer. 13 | dnl 14 | dnl 2. Redistributions in binary form must reproduce the above copyright 15 | dnl notice, this list of conditions and the following disclaimer in the 16 | dnl documentation and/or other materials provided with the distribution. 17 | dnl 18 | dnl 3. Neither the name of the author nor the names of any contributors 19 | dnl may be used to endorse or promote products derived from this 20 | dnl software without specific prior written permission. 21 | dnl 22 | dnl THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 23 | dnl IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | dnl WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | dnl DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 | dnl FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | dnl CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | dnl SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 29 | dnl BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 | dnl WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | dnl OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 32 | dnl IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | dnl 34 | dnl This file is part of clitoris. 35 | 36 | AC_DEFUN([AX_CHECK_CLITORIS], [dnl 37 | ## check for pseudo-tty support 38 | AC_CHECK_HEADERS([pty.h]) 39 | AM_CONDITIONAL([HAVE_PTY_H], [test "${ac_cv_header_pty_h}" = "yes"]) 40 | 41 | ## we might want to use splice(2) 42 | AC_CHECK_FUNCS([splice]) 43 | ])dnl AX_CHECK_CLITORIS 44 | 45 | dnl clitoris.m4 ends here 46 | -------------------------------------------------------------------------------- /m4/sxe-compiler.m4: -------------------------------------------------------------------------------- 1 | dnl compiler.m4 --- compiler magic 2 | dnl 3 | dnl Copyright (C) 2005-2015 Sebastian Freundt 4 | dnl Copyright (c) 2005 Steven G. Johnson 5 | dnl Copyright (c) 2005 Matteo Frigo 6 | dnl 7 | dnl Author: Sebastian Freundt 8 | dnl 9 | dnl Redistribution and use in source and binary forms, with or without 10 | dnl modification, are permitted provided that the following conditions 11 | dnl are met: 12 | dnl 13 | dnl 1. Redistributions of source code must retain the above copyright 14 | dnl notice, this list of conditions and the following disclaimer. 15 | dnl 16 | dnl 2. Redistributions in binary form must reproduce the above copyright 17 | dnl notice, this list of conditions and the following disclaimer in the 18 | dnl documentation and/or other materials provided with the distribution. 19 | dnl 20 | dnl 3. Neither the name of the author nor the names of any contributors 21 | dnl may be used to endorse or promote products derived from this 22 | dnl software without specific prior written permission. 23 | dnl 24 | dnl THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 25 | dnl IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | dnl WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | dnl DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | dnl FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | dnl CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | dnl SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 31 | dnl BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 32 | dnl WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | dnl OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 34 | dnl IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | dnl 36 | dnl This file is part of SXEmacs. 37 | 38 | ##### http://autoconf-archive.cryp.to/ax_check_compiler_flags.html 39 | ## renamed the prefix to SXE_ 40 | AC_DEFUN([SXE_CHECK_COMPILER_FLAG], [dnl 41 | dnl SXE_CHECK_COMPILER_FLAG([flag], [action-if-accepted], [action-if-not-accepted]) 42 | AC_MSG_CHECKING([whether _AC_LANG compiler accepts $1]) 43 | 44 | ## store werror status, then turn -Werror on 45 | save_ac_[]_AC_LANG_ABBREV[]_werror_flag="${ac_[]_AC_LANG_ABBREV[]_werror_flag}" 46 | AC_LANG_WERROR 47 | 48 | AC_CACHE_VAL(AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flag_$1), [dnl 49 | sxe_save_FLAGS="${[]_AC_LANG_PREFIX[]FLAGS}" 50 | _AC_LANG_PREFIX[]FLAGS="$1" 51 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM($4)], 52 | eval AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flag_$1)="yes", 53 | eval AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flag_$1)="no") 54 | _AC_LANG_PREFIX[]FLAGS="${sxe_save_FLAGS}" 55 | ]) 56 | eval sxe_check_flag=$AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flag_$1) 57 | ac_[]_AC_LANG_ABBREV[]_werror_flag="${save_ac_[]_AC_LANG_ABBREV[]_werror_flag}" 58 | 59 | AC_MSG_RESULT([${sxe_check_flag}]) 60 | if test "${sxe_check_flag}" = "yes"; then 61 | : 62 | $2 63 | else 64 | : 65 | $3 66 | fi 67 | ])dnl SXE_CHECK_COMPILER_FLAG 68 | 69 | AC_DEFUN([SXE_CHECK_PREPROC_FLAG], [dnl 70 | dnl SXE_CHECK_CPP_FLAG([flag], [action-if-found], [action-if-not-found]) 71 | AC_MSG_CHECKING([whether _AC_LANG preprocessor accepts $1]) 72 | 73 | ## store werror status, then turn -Werror on 74 | save_ac_[]_AC_LANG_ABBREV[]_werror_flag="${ac_[]_AC_LANG_ABBREV[]_werror_flag}" 75 | AC_LANG_WERROR 76 | 77 | AC_CACHE_VAL(AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]pp_flag_$1), [dnl 78 | sxe_save_FLAGS="${[]_AC_LANG_PREFIX[]PPFLAGS}" 79 | _AC_LANG_PREFIX[]PPFLAGS="$1" 80 | AC_PREPROC_IFELSE([AC_LANG_PROGRAM()], 81 | eval AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]pp_flag_$1)="yes", 82 | eval AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]pp_flag_$1)="no") 83 | _AC_LANG_PREFIX[]PPFLAGS=$sxe_save_FLAGS 84 | ]) 85 | eval sxe_check_flag=$AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]pp_flag_$1) 86 | ac_[]_AC_LANG_ABBREV[]_werror_flag="${save_ac_[]_AC_LANG_ABBREV[]_werror_flag}" 87 | 88 | AC_MSG_RESULT([${sxe_check_flag}]) 89 | if test "${sxe_check_flag}" = "yes"; then 90 | : 91 | $2 92 | else 93 | : 94 | $3 95 | fi 96 | ])dnl SXE_CHECK_PREPROC_FLAG 97 | 98 | AC_DEFUN([SXE_CHECK_CCLD_FLAG], [dnl 99 | dnl SXE_CHECK_CCLD_FLAG([flag], [action-if-accepted], [action-if-not-accepted]) 100 | AC_MSG_CHECKING([whether _AC_LANG linker accepts $1]) 101 | 102 | ## store werror status, then turn -Werror on 103 | save_ac_[]_AC_LANG_ABBREV[]_werror_flag="${ac_[]_AC_LANG_ABBREV[]_werror_flag}" 104 | AC_LANG_WERROR 105 | 106 | AC_CACHE_VAL(AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flag_$1), [dnl 107 | sxe_save_FLAGS="${[]_AC_LANG_PREFIX[]FLAGS}" 108 | _AC_LANG_PREFIX[]FLAGS="$1" 109 | AC_LINK_IFELSE([AC_LANG_PROGRAM()], 110 | eval AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flag_$1)="yes", 111 | eval AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flag_$1)="no") 112 | _AC_LANG_PREFIX[]FLAGS="${sxe_save_FLAGS}" 113 | ]) 114 | eval sxe_check_flag=$AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flag_$1) 115 | ac_[]_AC_LANG_ABBREV[]_werror_flag="${save_ac_[]_AC_LANG_ABBREV[]_werror_flag}" 116 | 117 | AC_MSG_RESULT([${sxe_check_flag}]) 118 | if test "${sxe_check_flag}" = "yes"; then 119 | : 120 | $2 121 | else 122 | : 123 | $3 124 | fi 125 | ])dnl SXE_CHECK_CCLD_FLAG 126 | 127 | 128 | AC_DEFUN([SXE_DEBUGFLAGS], [dnl 129 | ## distinguish between different compilers, no? 130 | SXE_CHECK_COMPILER_FLAG([-g]) 131 | SXE_CHECK_COMPILER_FLAG([-g3]) 132 | 133 | AC_PATH_PROG([DBX], [dbx]) 134 | if test -n "$ac_cv_path_DBX"; then 135 | SXE_CHECK_COMPILER_FLAG([-gstabs]) 136 | SXE_CHECK_COMPILER_FLAG([-gstabs3]) 137 | SXE_CHECK_COMPILER_FLAG([-gxcoff]) 138 | SXE_CHECK_COMPILER_FLAG([-gxcoff3]) 139 | fi 140 | 141 | AC_PATH_PROG([GDB], [gdb]) 142 | if test -n "$ac_cv_path_GDB"; then 143 | SXE_CHECK_COMPILER_FLAG([-ggdb]) 144 | SXE_CHECK_COMPILER_FLAG([-ggdb3]) 145 | fi 146 | 147 | AC_PATH_PROG([SDB], [sdb]) 148 | if test -n "$ac_cv_path_SDB"; then 149 | SXE_CHECK_COMPILER_FLAG([-gcoff]) 150 | SXE_CHECK_COMPILER_FLAG([-gcoff3]) 151 | fi 152 | 153 | ## final evaluation 154 | debugflags="" 155 | ## gdb 156 | if test "$sxe_cv_c_flag__ggdb3" = "yes"; then 157 | debugflags="$debugflags -ggdb3" 158 | elif test "$sxe_cv_c_flag__ggdb" = "yes"; then 159 | debugflags="$debugflags -ggdb" 160 | fi 161 | ## stabs 162 | if test "$sxe_cv_c_flag__gstabs3" = "yes"; then 163 | debugflags="$debugflags -gstabs3" 164 | elif test "$sxe_cv_c_flag__gstabs" = "yes"; then 165 | debugflags="$debugflags -gstabs" 166 | fi 167 | ## coff 168 | if test "$sxe_cv_c_flag__gcoff3" = "yes"; then 169 | debugflags="$debugflags -gcoff3" 170 | elif test "$sxe_cv_c_flag__gcoff" = "yes"; then 171 | debugflags="$debugflags -gcoff" 172 | fi 173 | ## xcoff 174 | if test "$sxe_cv_c_flag__gxcoff3" = "yes"; then 175 | debugflags="$debugflags -gxcoff3" 176 | elif test "$sxe_cv_c_flag__gxcoff" = "yes"; then 177 | debugflags="$debugflags -gxcoff" 178 | fi 179 | 180 | if test -z "debugflags" -a \ 181 | "$sxe_cv_c_flag__g" = "yes"; then 182 | debugflags="$debugflags -g" 183 | fi 184 | 185 | SXE_CHECK_COMPILER_FLAG([-ftime-report]) 186 | SXE_CHECK_COMPILER_FLAG([-fmem-report]) 187 | SXE_CHECK_COMPILER_FLAG([-fvar-tracking]) 188 | SXE_CHECK_COMPILER_FLAG([-save-temps]) 189 | 190 | #if test "$sxe_cv_c_flag__ggdb3" = "yes" -a \ 191 | # "$sxe_cv_c_flag__fvar_tracking" = "yes"; then 192 | # debugflags="$debugflags -fvar-tracking" 193 | #fi 194 | 195 | AC_MSG_CHECKING([for preferred debugging flags]) 196 | AC_MSG_RESULT([${debugflags}]) 197 | ])dnl SXE_DEBUGFLAGS 198 | 199 | AC_DEFUN([SXE_WARNFLAGS], [dnl 200 | ## Calculate warning flags. We separate the flags for warnings from 201 | ## the other flags because we want to force the warnings to be seen 202 | ## by everyone who doesn't specifically override them. 203 | 204 | ## by default we want the -Wall level 205 | SXE_CHECK_COMPILER_FLAG([-Wall], [warnflags="-Wall"]) 206 | 207 | SXE_CHECK_COMPILER_FLAG([-qinfo], [ 208 | warnflags="${warnflags} -qinfo"]) 209 | 210 | SXE_CHECK_COMPILER_FLAG([-Wextra], [ 211 | warnflags="${warnflags} -Wextra"]) 212 | 213 | ## Yuck, bad compares have been worth at 214 | ## least 3 crashes! 215 | ## Warnings about char subscripts are pretty 216 | ## pointless, though, 217 | ## and we use them in various places. 218 | SXE_CHECK_COMPILER_FLAG([-Wsign-compare], [ 219 | warnflags="$warnflags -Wsign-compare"]) 220 | SXE_CHECK_COMPILER_FLAG([-Wno-char-subscripts], [ 221 | warnflags="$warnflags -Wno-char-subscripts"]) 222 | SXE_CHECK_COMPILER_FLAG([-Wundef], [ 223 | warnflags="$warnflags -Wundef"]) 224 | 225 | ## too much at the moment, we rarely define protos 226 | #warnflags="$warnflags -Wmissing-prototypes -Wstrict-prototypes" 227 | 228 | ## somehow clang seems to think -Wpacked is to inform me 229 | ## about how unnecessary the packed attr is, so conditionalise ... 230 | SXE_CHECK_COMPILER_FLAG([-Wpacked], [ 231 | warnflags="$warnflags -Wpacked"], [:], [[ 232 | #if defined __clang__ 233 | # error 234 | #endif /* __clang__ */ 235 | ]]) 236 | 237 | ## glibc is intentionally not `-Wpointer-arith'-clean. 238 | ## Ulrich Drepper has rejected patches to fix 239 | ## the glibc header files. 240 | ## we dont care 241 | SXE_CHECK_COMPILER_FLAG([-Wpointer-arith], [ 242 | warnflags="$warnflags -Wpointer-arith"]) 243 | 244 | SXE_CHECK_COMPILER_FLAG([-Wshadow], [ 245 | warnflags="$warnflags -Wshadow"]) 246 | 247 | ## our code lacks declarations almost all the time 248 | SXE_CHECK_COMPILER_FLAG([-Wmissing-declarations], [ 249 | warnflags="$warnflags -Wmissing-declarations"]) 250 | SXE_CHECK_COMPILER_FLAG([-Wmissing-prototypes], [ 251 | warnflags="$warnflags -Wmissing-prototypes"]) 252 | 253 | ## gcc can't practically inline anything, so exclude this 254 | case "${CC}" in 255 | dnl ( 256 | *"gcc"*) 257 | ;; 258 | dnl ( 259 | *) 260 | SXE_CHECK_COMPILER_FLAG([-Winline], [ 261 | warnflags="$warnflags -Winline"]) 262 | ;; 263 | esac 264 | 265 | SXE_CHECK_COMPILER_FLAG([-Wbad-function-cast], [ 266 | warnflags="$warnflags -Wbad-function-cast"]) 267 | SXE_CHECK_COMPILER_FLAG([-Wcast-qual], [ 268 | warnflags="$warnflags -Wcast-qual"]) 269 | SXE_CHECK_COMPILER_FLAG([-Wcast-align], [ 270 | warnflags="$warnflags -Wcast-align"]) 271 | 272 | ## warn about incomplete switches 273 | ## for gcc, see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50422 274 | ## we used to have -Wswitch-default and -Wswitch-enum but that 275 | ## set gcc off quite badly in the nested switch case 276 | SXE_CHECK_COMPILER_FLAG([-Wno-switch], [ 277 | warnflags="$warnflags -Wno-switch"]) 278 | 279 | SXE_CHECK_COMPILER_FLAG([-Wunused-function], [ 280 | warnflags="$warnflags -Wunused-function"]) 281 | SXE_CHECK_COMPILER_FLAG([-Wunused-variable], [ 282 | warnflags="$warnflags -Wunused-variable"]) 283 | SXE_CHECK_COMPILER_FLAG([-Wunused-parameter], [ 284 | warnflags="$warnflags -Wunused-parameter"]) 285 | SXE_CHECK_COMPILER_FLAG([-Wunused-value], [ 286 | warnflags="$warnflags -Wunused-value"]) 287 | SXE_CHECK_COMPILER_FLAG([-Wunused], [ 288 | warnflags="$warnflags -Wunused"]) 289 | SXE_CHECK_COMPILER_FLAG([-Wmaybe-uninitialized], [ 290 | warnflags="${warnflags} -Wmaybe-uninitialized"]) 291 | 292 | SXE_CHECK_COMPILER_FLAG([-Wnopragma], [ 293 | warnflags="$warnflags -Wnopragma"]) 294 | 295 | SXE_CHECK_COMPILER_FLAG([-fdiagnostics-show-option], [ 296 | warnflags="${warnflags} -fdiagnostics-show-option"]) 297 | 298 | SXE_CHECK_COMPILER_FLAG([-Wunknown-pragmas], [ 299 | warnflags="$warnflags -Wunknown-pragmas"]) 300 | SXE_CHECK_COMPILER_FLAG([-Wuninitialized], [ 301 | warnflags="$warnflags -Wuninitialized"]) 302 | SXE_CHECK_COMPILER_FLAG([-Wreorder], [ 303 | warnflags="$warnflags -Wreorder"]) 304 | SXE_CHECK_COMPILER_FLAG([-Wdeprecated], [ 305 | warnflags="$warnflags -Wdeprecated"]) 306 | 307 | SXE_CHECK_COMPILER_FLAG([-Wno-parentheses], [ 308 | warnflags="${warnflags} -Wno-parentheses"]) 309 | 310 | ## icc specific 311 | SXE_CHECK_COMPILER_FLAG([-Wcheck], [ 312 | warnflags="$warnflags -Wcheck"]) 313 | 314 | dnl SXE_CHECK_COMPILER_FLAG([-Wp64], [ 315 | dnl warnflags="$warnflags -Wp64"]) 316 | 317 | SXE_CHECK_COMPILER_FLAG([-Wstrict-aliasing], [ 318 | warnflags="$warnflags -Wstrict-aliasing"]) 319 | 320 | SXE_CHECK_COMPILER_FLAG([-w3], [ 321 | warnflags="$warnflags -w3"]) 322 | 323 | SXE_CHECK_COMPILER_FLAG([-diag-disable 10237], [dnl 324 | warnflags="${warnflags} -diag-disable 10237"], [ 325 | SXE_CHECK_COMPILER_FLAG([-wd 10237], [dnl 326 | warnflags="${warnflags} -wd 10237"])]) 327 | 328 | SXE_CHECK_COMPILER_FLAG([-diag-disable 2102], [dnl 329 | warnflags="${warnflags} -diag-disable 2102"], [ 330 | SXE_CHECK_COMPILER_FLAG([-wd 2102], [dnl 331 | warnflags="${warnflags} -wd 2102"])]) 332 | 333 | SXE_CHECK_COMPILER_FLAG([-debug inline-debug-info], [ 334 | warnflags="${warnflags} -debug inline-debug-info"]) 335 | 336 | SXE_CHECK_COMPILER_FLAG([-diag-enable remark,vec,par], [ 337 | warnflags="${warnflags} -diag-enable remark,vec,par"]) 338 | 339 | ## for dfp754 340 | SXE_CHECK_COMPILER_FLAG([-Wunsuffixed-float-constants], [ 341 | warnflags="$warnflags -Wunsuffixed-float-constants"]) 342 | 343 | AC_MSG_CHECKING([for preferred warning flags]) 344 | AC_MSG_RESULT([${warnflags}]) 345 | ])dnl SXE_WARNFLAGS 346 | 347 | AC_DEFUN([SXE_OPTIFLAGS], [dnl 348 | AC_REQUIRE([SXE_USER_CFLAGS]) 349 | AC_REQUIRE([SXE_WARNFLAGS]) 350 | 351 | case " ${CFLAGS} ${EXTRA_CFLAGS} " in 352 | (*" -O"[[0-9]]" "*) 353 | ;; 354 | (*" -Os "*) 355 | ;; 356 | (*" -Og "*) 357 | ;; 358 | (*" -Ofast "*) 359 | ;; 360 | (*" -O "*) 361 | ;; 362 | (*) 363 | SXE_CHECK_COMPILER_FLAG([-O3], [ 364 | optiflags="${optiflags} -O3"]) 365 | ;; 366 | esac 367 | 368 | SXE_CHECK_COMPILER_FLAG([-ipo], [ 369 | optiflags="${optiflags} -ipo" 370 | 371 | AC_CHECK_TOOLS([AR], [xiar ar], [false]) 372 | AC_CHECK_TOOLS([LD], [xild ld], [false]) 373 | 374 | ## fiddle with xiar and xild params, kick ansi aliasing warnings 375 | if test "${ac_cv_prog_ac_ct_AR}" = "xiar"; then 376 | AR="${AR} -qdiag-disable=2102" 377 | fi 378 | if test "${ac_cv_prog_ac_ct_LD}" = "xild"; then 379 | LD="${LD} -qdiag-disable=2102" 380 | fi 381 | ]) 382 | 383 | SXE_CHECK_COMPILER_FLAG([-no-prec-div], [ 384 | optiflags="${optiflags} -no-prec-div"]) 385 | ])dnl SXE_OPTIFLAGS 386 | 387 | AC_DEFUN([SXE_CC_NATIVE], [dnl 388 | dnl Usage: SXE_CC_NATIVE([yes|no]) 389 | AC_ARG_ENABLE([native], [dnl 390 | AS_HELP_STRING(m4_case([$1], [yes], [--disable-native], [--enable-native]), [ 391 | Use code native to the build machine.])], 392 | [enable_native="${enableval}"], [enable_native="$1"]) 393 | 394 | ## -fast implies -static which is a dream but 395 | ## packager prefer dynamic binaries 396 | dnl SXE_CHECK_COMPILER_FLAG([-fast], [ 397 | dnl optiflags="${optiflags} -fast"]) 398 | 399 | ## auto-vectorisation 400 | dnl SXE_CHECK_COMPILER_FLAG([-axMIC-AVX512,CORE-AVX2,CORE-AVX-I,AVX,SSSE3], [ 401 | dnl optiflags="${optiflags} -axMIC-AVX512,CORE-AVX2,CORE-AVX-I,AVX,SSSE3"]) 402 | 403 | if test "${enable_native}" = "yes"; then 404 | case " ${CFLAGS} ${EXTRA_CFLAGS}" in 405 | (*" -mtune"*) 406 | ## don't tune 407 | ;; 408 | (*" -march"*) 409 | ## don't set march 410 | ;; 411 | (*" -m32 "*) 412 | ## don't bother 413 | ;; 414 | (*" -m64 "*) 415 | ## don't bother 416 | ;; 417 | (*) 418 | SXE_CHECK_COMPILER_FLAG([-xHost], [ 419 | optiflags="${optiflags} -xHost"], [ 420 | ## non-icc 421 | SXE_CHECK_COMPILER_FLAG([-mtune=native -march=native], [ 422 | optiflags="${optiflags} -mtune=native -march=native"]) 423 | ]) 424 | ;; 425 | esac 426 | fi 427 | ])dnl SXE_CC_NATIVE 428 | 429 | AC_DEFUN([SXE_FEATFLAGS], [dnl 430 | ## default flags for needed features 431 | AC_REQUIRE([SXE_CHECK_COMPILER_XFLAG]) 432 | XCCFLAG="${XFLAG}" 433 | 434 | ## recent gentoos went ballistic again, they compile PIE gcc's 435 | ## but there's no way to turn that misconduct off ... 436 | ## however I've got one report about a working PIE build 437 | ## we'll just check for -nopie here, if it works, we turn it on 438 | ## (and hence PIE off) and hope bug 16 remains fixed 439 | SXE_CHECK_COMPILER_FLAG([-nopie], 440 | [featflags="$featflags -nopie"]) 441 | 442 | ## icc and gcc related 443 | ## check if some stuff can be staticalised 444 | ## actually requires SXE_WARNFLAGS so warnings would be disabled 445 | ## that affect the outcome of the following tests 446 | SXE_CHECK_COMPILER_FLAG([-static-intel], [ 447 | featflags="${featflags} -static-intel" 448 | XCCLDFLAGS="${XCCLDFLAGS} \${XCCFLAG} -static-intel"], [:]) 449 | SXE_CHECK_COMPILER_FLAG([-static-libgcc], [ 450 | featflags="${featflags} -static-libgcc" 451 | XCCLDFLAGS="${XCCLDFLAGS} \${XCCFLAG} -static-libgcc"], [:]) 452 | 453 | SXE_CHECK_COMPILER_FLAG([-intel-extensions], [dnl 454 | featflags="${featflags} -intel-extensions"]) 455 | 456 | ## also pass on some diags to the linker 457 | if test "${sxe_cv_c_flag__diag_disable_10237}" = "yes"; then 458 | XCCLDFLAGS="${XCCLDFLAGS} \${XCCFLAG} -diag-disable=10237" 459 | fi 460 | if test "${sxe_cv_c_flag__diag_disable_2102}" = "yes"; then 461 | XCCLDFLAGS="${XCCLDFLAGS} \${XCCFLAG} -diag-disable=2102" 462 | fi 463 | 464 | AC_SUBST([XCCLDFLAGS]) 465 | AC_SUBST([XCCFLAG]) 466 | ])dnl SXE_FEATFLAGS 467 | 468 | AC_DEFUN([SXE_CHECK_COMPILER_XFLAG], [dnl 469 | if test "${XFLAG}" = ""; then 470 | SXE_CHECK_CCLD_FLAG([-XCClinker -foo], [XFLAG="-XCClinker"]) 471 | fi 472 | if test "${XFLAG}" = ""; then 473 | SXE_CHECK_CCLD_FLAG([-Xlinker -foo], [XFLAG="-Xlinker"]) 474 | fi 475 | 476 | AC_SUBST([XFLAG]) 477 | ])dnl SXE_CHECK_COMPILER_XFLAG 478 | 479 | AC_DEFUN([SXE_USER_CFLAGS], [dnl 480 | AC_MSG_CHECKING([for user provided CFLAGS/EXTRA_CFLAGS]) 481 | 482 | CFLAGS="${ac_cv_env_CFLAGS_value}" 483 | AC_MSG_RESULT([${CFLAGS} ${EXTRA_CFLAGS}]) 484 | ])dnl SXE_USER_CFLAGS 485 | 486 | 487 | AC_DEFUN([SXE_CHECK_CFLAGS], [dnl 488 | dnl Usage: SXE_CHECK_CFLAGS([option ...]) 489 | dnl valid options include: 490 | dnl + native[=yes|no] Emit the --enable-native flag 491 | 492 | ## those are passed on to our determined CFLAGS 493 | AC_ARG_VAR([EXTRA_CFLAGS], [C compiler flags to be APPENDED.]) 494 | 495 | ## check for user provided flags 496 | AC_REQUIRE([SXE_USER_CFLAGS]) 497 | ## Use either command line flag, environment var, or autodetection 498 | SXE_DEBUGFLAGS 499 | SXE_WARNFLAGS 500 | SXE_OPTIFLAGS 501 | m4_foreach_w([opt], [$1], [dnl 502 | m4_case(opt, 503 | [native], [SXE_CC_NATIVE], 504 | [native=yes], [SXE_CC_NATIVE([yes])], 505 | [native=no], [SXE_CC_NATIVE([no])]) 506 | ]) 507 | SXE_CFLAGS="${SXE_CFLAGS} ${debugflags} ${optiflags} ${warnflags}" 508 | 509 | SXE_FEATFLAGS 510 | SXE_CFLAGS="${SXE_CFLAGS} ${featflags}" 511 | 512 | save_ac_c_werror_flag="${ac_c_werror_flag}" 513 | 514 | CFLAGS="${CFLAGS} ${SXE_CFLAGS} ${EXTRA_CFLAGS}" 515 | AC_MSG_CHECKING([for preferred CFLAGS]) 516 | AC_MSG_RESULT([${CFLAGS}]) 517 | 518 | AC_MSG_NOTICE([ 519 | If you wish to APPEND your own flags you want to stop here and rerun the 520 | configure script like so: 521 | configure EXTRA_CFLAGS= 522 | 523 | If you wish to OVERRIDE these flags you want to stop here too and rerun 524 | the configure script like this: 525 | configure CFLAGS= 526 | 527 | You can always override the determined CFLAGS, partially or totally, 528 | using 529 | make -C CFLAGS= [target] 530 | or 531 | make CFLAGS= [target] 532 | respectively 533 | ]) 534 | 535 | ac_c_werror_flag="${save_ac_c_werror_flag}" 536 | ])dnl SXE_CHECK_CFLAGS 537 | 538 | AC_DEFUN([SXE_CHECK_CC], [dnl 539 | dnl SXE_CHECK_CC([STANDARDS]) 540 | dnl standards are flavours supported by the compiler chosen with AC_PROG_CC 541 | pushdef([stds], m4_default([$1], [gnu11 c11 gnu99 c99])) 542 | 543 | AC_REQUIRE([AC_CANONICAL_HOST]) 544 | AC_REQUIRE([AC_CANONICAL_BUILD]) 545 | AC_REQUIRE([AC_PROG_CPP]) 546 | AC_REQUIRE([AC_PROG_CC]) 547 | 548 | AC_HEADER_STDC 549 | 550 | case "${CC}" in dnl ( 551 | *"-std="*) 552 | ## user specified a std value already 553 | ;; 554 | dnl ( 555 | *) 556 | for i in []stds[]; do 557 | SXE_CHECK_COMPILER_FLAG([-std="${i}"], [ 558 | std="-std=${i}" 559 | save_CC="${CC}" 560 | CC="${CC} ${std}" 561 | SXE_CHECK_ANON_STRUCTS_DECL 562 | CC="${save_CC}" 563 | if test "${sxe_cv_have_anon_structs_decl}" \ 564 | = "yes"; then 565 | break 566 | fi 567 | ]) 568 | done 569 | 570 | AC_MSG_CHECKING([for preferred CC std]) 571 | AC_MSG_RESULT([${std}]) 572 | CC="${CC} ${std}" 573 | 574 | ## while we're at it, check for anon initialising too 575 | SXE_CHECK_ANON_STRUCTS_INIT 576 | ## oh and sloppy sloppy init 577 | SXE_CHECK_SLOPPY_STRUCTS_INIT 578 | ;; 579 | esac 580 | 581 | popdef([stds]) 582 | ])dnl SXE_CHECK_CC 583 | 584 | AC_DEFUN([SXE_CHECK_ANON_STRUCTS_INIT], [ 585 | AC_MSG_CHECKING([dnl 586 | whether C compiler can initialise anonymous structs and unions]) 587 | AC_LANG_PUSH([C]) 588 | 589 | ## backup our CFLAGS and unset it 590 | save_CFLAGS="${CFLAGS}" 591 | CFLAGS="" 592 | 593 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ 594 | union __test_u { 595 | int i; 596 | struct { 597 | char c; 598 | char padc; 599 | short int pads; 600 | }; 601 | }; 602 | ]], [[ 603 | union __test_u tmp = {.c = '4'}; 604 | ]])], [ 605 | sxe_cv_have_anon_structs_init="yes" 606 | ], [ 607 | sxe_cv_have_anon_structs_init="no" 608 | ]) 609 | AC_MSG_RESULT([${sxe_cv_have_anon_structs_init}]) 610 | 611 | ## restore CFLAGS 612 | CFLAGS="${save_CFLAGS}" 613 | 614 | if test "${sxe_cv_have_anon_structs_init}" = "yes"; then 615 | AC_DEFINE([HAVE_ANON_STRUCTS_INIT], [1], [dnl 616 | Whether c11 anon struct initialising works]) 617 | $1 618 | : 619 | else 620 | $2 621 | : 622 | fi 623 | AC_LANG_POP() 624 | ])dnl SXE_CHECK_ANON_STRUCTS_INIT 625 | 626 | AC_DEFUN([SXE_CHECK_ANON_STRUCTS_DECL], [ 627 | AC_MSG_CHECKING([dnl 628 | whether C compiler can understand anonymous structs and unions]) 629 | AC_LANG_PUSH([C]) 630 | 631 | ## backup our CFLAGS and unset it 632 | save_CFLAGS="${CFLAGS}" 633 | CFLAGS="" 634 | 635 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ 636 | union __test_u { 637 | int i; 638 | struct { 639 | char c; 640 | char padc; 641 | short int pads; 642 | }; 643 | }; 644 | ]], [[ 645 | /* nothing to do really*/ 646 | union __test_u foo; 647 | foo.c = 0; 648 | ]])], [ 649 | sxe_cv_have_anon_structs_decl="yes" 650 | ], [ 651 | sxe_cv_have_anon_structs_decl="no" 652 | ]) 653 | AC_MSG_RESULT([${sxe_cv_have_anon_structs_decl}]) 654 | 655 | ## restore CFLAGS 656 | CFLAGS="${save_CFLAGS}" 657 | 658 | if test "${sxe_cv_have_anon_structs_decl}" = "yes"; then 659 | AC_DEFINE([HAVE_ANON_STRUCTS_DECL], [1], [dnl 660 | Whether c11 anon structs declaring works]) 661 | $1 662 | : 663 | else 664 | $2 665 | : 666 | fi 667 | AC_LANG_POP() 668 | ])dnl SXE_CHECK_ANON_STRUCTS_DECL 669 | 670 | AC_DEFUN([SXE_CHECK_SLOPPY_STRUCTS_INIT], [ 671 | AC_LANG_PUSH([C]) 672 | 673 | ## backup our CFLAGS and unset it 674 | save_CFLAGS="${CFLAGS}" 675 | CFLAGS="-Werror" 676 | 677 | SXE_CHECK_COMPILER_FLAG([-Wmissing-field-initializers], [ 678 | CFLAGS="${CFLAGS} -Wmissing-field-initializers"]) 679 | 680 | AC_MSG_CHECKING([dnl 681 | whether C compiler can initialise structs and unions in a sloppy way]) 682 | 683 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ 684 | struct __test_s { 685 | int i; 686 | int j; 687 | }; 688 | ]], [[ 689 | struct __test_s tmp = {}; 690 | ]])], [ 691 | sxe_cv_have_sloppy_structs_init="yes" 692 | ], [ 693 | sxe_cv_have_sloppy_structs_init="no" 694 | ]) 695 | AC_MSG_RESULT([${sxe_cv_have_sloppy_structs_init}]) 696 | 697 | ## restore CFLAGS 698 | CFLAGS="${save_CFLAGS}" 699 | 700 | if test "${sxe_cv_have_sloppy_structs_init}" = "yes"; then 701 | AC_DEFINE([HAVE_SLOPPY_STRUCTS_INIT], [1], [dnl 702 | Whether sloppy struct initialising works]) 703 | $1 704 | : 705 | else 706 | $2 707 | : 708 | fi 709 | AC_LANG_POP() 710 | ])dnl SXE_CHECK_SLOPPY_STRUCTS_INIT 711 | 712 | AC_DEFUN([SXE_CHECK_INTRINS], [dnl 713 | AC_CHECK_HEADERS([immintrin.h]) 714 | AC_CHECK_HEADERS([x86intrin.h]) 715 | AC_CHECK_HEADERS([ia32intrin.h]) 716 | AC_CHECK_HEADERS([popcntintrin.h]) 717 | AC_CHECK_TYPES([__m128i], [], [], [[ 718 | #if defined HAVE_X86INTRIN_H 719 | # include 720 | #elif defined HAVE_IMMINTRIN_H 721 | # include 722 | #endif 723 | ]]) 724 | AC_CHECK_TYPES([__m256i], [], [], [[ 725 | #if defined HAVE_X86INTRIN_H 726 | # include 727 | #elif defined HAVE_IMMINTRIN_H 728 | # include 729 | #endif 730 | ]]) 731 | AC_CHECK_TYPES([__m512i], [], [], [[ 732 | #if defined HAVE_X86INTRIN_H 733 | # include 734 | #elif defined HAVE_IMMINTRIN_H 735 | # include 736 | #endif 737 | ]]) 738 | AC_CHECK_TYPES([__mmask64], [], [], [[ 739 | #if defined HAVE_X86INTRIN_H 740 | # include 741 | #elif defined HAVE_IMMINTRIN_H 742 | # include 743 | #endif 744 | ]]) 745 | ])dnl SXE_CHECK_INTRINS 746 | 747 | AC_DEFUN([SXE_CHECK_SIMD], [dnl 748 | dnl Usage: SXE_CHECK_SIMD([INTRIN], [[SNIPPET], [IF-FOUND], [IF-NOT-FOUND]]) 749 | AC_REQUIRE([SXE_CHECK_INTRINS]) 750 | 751 | AC_MSG_CHECKING([for SIMD routine $1]) 752 | AC_LINK_IFELSE([AC_LANG_PROGRAM([[ 753 | #if defined HAVE_IA32INTRIN_H 754 | # include 755 | #endif 756 | #if defined HAVE_X86INTRIN_H 757 | # include 758 | #endif 759 | #if defined HAVE_IMMINTRIN_H 760 | # include 761 | #endif 762 | #if defined HAVE_POPCNTINTRIN_H 763 | # include 764 | #endif 765 | ]], [ifelse([$2],[],[$1(0U)],[$2]);])], [ 766 | eval AS_TR_SH(ac_cv_func_$1)="yes" 767 | AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1], [dnl 768 | Define to 1 if you have the `$1' simd routine]) 769 | $3 770 | ], [ 771 | eval AS_TR_SH(ac_cv_func_$1)="no" 772 | $4 773 | ]) 774 | AC_MSG_RESULT([${ac_cv_func_$1}]) 775 | ])dnl SXE_CHECK_SIMD 776 | 777 | AC_DEFUN([SXE_CHECK_CILK], [dnl 778 | dnl Usage: SXE_CHECK_CILK([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) 779 | dnl defines sxe_cv_feat_cilk to "yes" if applicable, "no" otherwise 780 | dnl also AC_DEFINEs HAVE_CILK 781 | AC_CHECK_HEADERS([cilk/cilk.h]) 782 | 783 | save_CFLAGS="${CFLAGS}" 784 | SXE_CHECK_COMPILER_FLAG([-fcilkplus], [CFLAGS="${CFLAGS} -fcilkplus"]) 785 | 786 | AC_MSG_CHECKING([whether Cilk+ keywords work]) 787 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ 788 | #include 789 | #if defined HAVE_CILK_CILK_H 790 | # include 791 | #else /* !HAVE_CILK_CILK_H */ 792 | # define cilk_spawn _Cilk_spawn 793 | # define cilk_sync _Cilk_sync 794 | # define cilk_for _Cilk_for 795 | #endif /* HAVE_CILK_CILK_H */ 796 | 797 | static char *trick; 798 | 799 | static int pcmp(const void *x, const void *y) 800 | { 801 | return (const char*)x - (const char*)y; 802 | } 803 | ]], [ 804 | int x = 0; 805 | int j; 806 | 807 | cilk_spawn qsort(trick, 1, 2, pcmp); 808 | qsort(trick + 4, 1, 2, pcmp); 809 | cilk_sync; 810 | 811 | cilk_for(j = 0; j < 8; j++) { 812 | x++; 813 | } 814 | ])], [ 815 | AC_DEFINE([HAVE_CILK], [1], [define when compiler supports Cilk+ keywords]) 816 | sxe_cv_feat_cilk="yes" 817 | $1 818 | ], [ 819 | CFLAGS="${save_CFLAGS}" 820 | sxe_cv_feat_cilk="no" 821 | $2 822 | ]) 823 | AC_MSG_RESULT([${sxe_cv_feat_cilk}]) 824 | ])dnl SXE_CHECK_CILK 825 | 826 | dnl sxe-compiler.m4 ends here 827 | -------------------------------------------------------------------------------- /m4/sxe-linker.m4: -------------------------------------------------------------------------------- 1 | dnl linker.m4 --- linker magic 2 | dnl 3 | dnl Copyright (C) 2005 - 2012 Sebastian Freundt 4 | dnl Copyright (c) 2005 Steven G. Johnson 5 | dnl Copyright (c) 2005 Matteo Frigo 6 | dnl 7 | dnl Author: Sebastian Freundt 8 | dnl 9 | dnl Redistribution and use in source and binary forms, with or without 10 | dnl modification, are permitted provided that the following conditions 11 | dnl are met: 12 | dnl 13 | dnl 1. Redistributions of source code must retain the above copyright 14 | dnl notice, this list of conditions and the following disclaimer. 15 | dnl 16 | dnl 2. Redistributions in binary form must reproduce the above copyright 17 | dnl notice, this list of conditions and the following disclaimer in the 18 | dnl documentation and/or other materials provided with the distribution. 19 | dnl 20 | dnl 3. Neither the name of the author nor the names of any contributors 21 | dnl may be used to endorse or promote products derived from this 22 | dnl software without specific prior written permission. 23 | dnl 24 | dnl THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 25 | dnl IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | dnl WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | dnl DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | dnl FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | dnl CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | dnl SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 31 | dnl BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 32 | dnl WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | dnl OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 34 | dnl IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | dnl 36 | dnl This file is part of SXEmacs. 37 | 38 | AC_DEFUN([SXE_CHECK_LINKER_FLAGS], [dnl 39 | dnl just like SXE_CHECK_COMPILER_FLAGS but calls the linker 40 | dnl SXE_CHECK_LINKER_FLAGS(, , , 41 | dnl ) 42 | AC_REQUIRE([SXE_CHECK_COMPILER_XFLAG]) 43 | 44 | AC_MSG_CHECKING([whether linker accepts $1]) 45 | 46 | dnl Some hackery here since AC_CACHE_VAL can't handle a non-literal varname: 47 | AC_LANG_WERROR([on]) 48 | AS_LITERAL_IF([$1], [ 49 | AC_CACHE_VAL(AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flags_$1), [ 50 | sxe_save_FLAGS=$[]_AC_LANG_PREFIX[]FLAGS 51 | _AC_LANG_PREFIX[]FLAGS="$4 ${XFLAG} $1" 52 | AC_LINK_IFELSE([AC_LANG_PROGRAM()], 53 | AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flags_$1)="yes", 54 | AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flags_$1)="no") 55 | _AC_LANG_PREFIX[]FLAGS=$sxe_save_FLAGS])], [ 56 | sxe_save_FLAGS=$[]_AC_LANG_PREFIX[]FLAGS 57 | _AC_LANG_PREFIX[]FLAGS="$4 ${XFLAG} $1" 58 | AC_LINK_IFELSE([AC_LANG_PROGRAM()], 59 | eval AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flags_$1)="yes", 60 | eval AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flags_$1)="no") 61 | _AC_LANG_PREFIX[]FLAGS=$sxe_save_FLAGS]) 62 | eval sxe_check_linker_flags=$AS_TR_SH(sxe_cv_[]_AC_LANG_ABBREV[]_flags_$1) 63 | AC_LANG_WERROR([off]) 64 | 65 | AC_MSG_RESULT([$sxe_check_linker_flags]) 66 | if test "$sxe_check_linker_flags" = "yes"; then 67 | m4_default([$2], :) 68 | else 69 | m4_default([$3], :) 70 | fi 71 | ])dnl SXE_CHECK_LINKER_FLAGS 72 | 73 | 74 | AC_DEFUN([SXE_CHECK_LD_ZFLAG], [dnl 75 | pushdef([LD_ZFLAG], [$1]) 76 | pushdef([cv_zflag], [sxe_cv_ld__z_]translit(LD_ZFLAG,[-.=],[___])) 77 | 78 | SXE_CHECK_LINKER_FLAGS([-z ]LD_ZFLAG[]) 79 | 80 | dnl Use the check that actually calls the compiler/linker to examine the flags 81 | dnl AC_CACHE_CHECK([whether linker supports -z ]LD_ZFLAG[], 82 | dnl []cv_zflag[], [_SXE_CHECK_LD_ZFLAG(LD_ZFLAG)]) 83 | 84 | popdef([cv_zflag]) 85 | popdef([LD_ZFLAG]) 86 | ])dnl SXE_CHECK_LD_ZFLAG 87 | 88 | AC_DEFUN([_SXE_CHECK_LD_ZFLAG], [dnl 89 | ## arg1 is the flag to check for 90 | pushdef([LD_ZFLAG], [$1]) 91 | pushdef([cv_zflag], [sxe_cv_ld__z_]translit(LD_ZFLAG,[-.=],[___])) 92 | 93 | if test "$GCC" = "yes"; then 94 | if test "($CC -Xlinker --help 2>&1 | \ 95 | grep \"-z []LD_ZFLAG[]\" > /dev/null 2>&1 ) "; then 96 | cv_zflag="yes" 97 | else 98 | cv_zflag="no" 99 | fi 100 | elif test -n "$LD"; then 101 | if test "($LD --help 2>&1 | \ 102 | grep \"-z []LD_ZFLAG[]\" > /dev/null 2>&1 )"; then 103 | cv_zflag="yes" 104 | else 105 | cv_zflag="no" 106 | fi 107 | else 108 | cv_zflag="no" 109 | fi 110 | 111 | popdef([cv_zflag]) 112 | popdef([LD_ZFLAG]) 113 | ])dnl _SXE_CHECK_LD_ZFLAG 114 | 115 | AC_DEFUN([SXE_LD_EXPORT_DYNAMIC], [dnl 116 | SXE_CHECK_LINKER_FLAGS([-export-dynamic], [ 117 | LD_EXPORT_DYNAMIC="-export-dynamic" 118 | ]) 119 | 120 | AC_SUBST([LD_EXPORT_DYNAMIC]) 121 | ])dnl SXE_LD_EXPORT_DYNAMIC 122 | 123 | AC_DEFUN([SXE_CHECK_LDFLAGS], [dnl 124 | AC_REQUIRE([SXE_CHECK_COMPILER_XFLAG]) 125 | 126 | ## relocation 127 | SXE_CHECK_LD_ZFLAG([combreloc]) 128 | SXE_CHECK_LD_ZFLAG([nocombreloc]) 129 | ## symbols 130 | SXE_CHECK_LD_ZFLAG([defs]) 131 | SXE_CHECK_LD_ZFLAG([muldefs]) 132 | ## search paths 133 | SXE_CHECK_LD_ZFLAG([nodefaultlib]) 134 | ## binding policy 135 | SXE_CHECK_LD_ZFLAG([lazy]) 136 | SXE_CHECK_LD_ZFLAG([now]) 137 | 138 | SXE_LD_EXPORT_DYNAMIC 139 | 140 | LDFLAGS="${ldflags} ${ac_cv_env_LDFLAGS_value}" 141 | AC_MSG_CHECKING([for preferred LDFLAGS]) 142 | AC_MSG_RESULT([${LDFLAGS}]) 143 | 144 | AC_MSG_NOTICE([ 145 | If you wish to ADD your own flags you want to stop here and rerun the 146 | configure script like so: 147 | configure LDFLAGS= 148 | 149 | You can always override the determined LDFLAGS, partially or totally, 150 | using 151 | make -C LDFLAGS= [target] 152 | or 153 | make LDFLAGS= [target] 154 | respectively 155 | 156 | NOTE: -C option is not available on all systems 157 | ]) 158 | ])dnl SXE_CHECK_LDFLAGS 159 | 160 | AC_DEFUN([SXE_PREPEND_LINKER_FLAG], [dnl 161 | ## a convenience function to add such linker flags to variables 162 | ## arg1 is the flag to add (must include -z if applicable) 163 | ## arg2 is the variable whither to prepend 164 | pushdef([FLAG], [$1]) 165 | pushdef([__FLAG], [-Wl,]patsubst([$1], [ ], [[,]])) 166 | pushdef([VAR], [$2]) 167 | 168 | []VAR[]="[]__FLAG[] $[]VAR[]" 169 | if test "$extra_verbose" = "yes"; then 170 | echo " Prepending linker flag \"[]__FLAG[]\" to \$[]VAR[]" 171 | fi 172 | 173 | popdef([VAR]) 174 | popdef([__FLAG]) 175 | popdef([FLAG]) 176 | ])dnl SXE_PREPEND_LINKER_FLAG 177 | 178 | dnl sxe-linker.m4 ends here 179 | -------------------------------------------------------------------------------- /m4/yuck.m4: -------------------------------------------------------------------------------- 1 | dnl yuck.m4 --- yuck goodies 2 | dnl 3 | dnl Copyright (C) 2013-2016 Sebastian Freundt 4 | dnl 5 | dnl Author: Sebastian Freundt 6 | dnl 7 | dnl Redistribution and use in source and binary forms, with or without 8 | dnl modification, are permitted provided that the following conditions 9 | dnl are met: 10 | dnl 11 | dnl 1. Redistributions of source code must retain the above copyright 12 | dnl notice, this list of conditions and the following disclaimer. 13 | dnl 14 | dnl 2. Redistributions in binary form must reproduce the above copyright 15 | dnl notice, this list of conditions and the following disclaimer in the 16 | dnl documentation and/or other materials provided with the distribution. 17 | dnl 18 | dnl 3. Neither the name of the author nor the names of any contributors 19 | dnl may be used to endorse or promote products derived from this 20 | dnl software without specific prior written permission. 21 | dnl 22 | dnl THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 23 | dnl IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | dnl WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | dnl DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 | dnl FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | dnl CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | dnl SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 29 | dnl BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 | dnl WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | dnl OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 32 | dnl IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | dnl 34 | dnl This file is part of yuck. 35 | 36 | AC_DEFUN([AX_CHECK_M4_BUFFERS], [dnl 37 | AC_MSG_CHECKING([for m4 with sufficient capabilities]) 38 | 39 | AC_ARG_VAR([M4], [full path to the m4 tool]) 40 | probe_M4="${M4-m4}" 41 | if ${probe_M4} >/dev/null 2>&1 \ 42 | -Dx='y y y y y y y y y y y y y y y y' \ 43 | -Dy='z z z z z z z z z z z z z z z z' \ 44 | -Dz='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0' <<'EOF' 45 | [define(`foo', x)] 46 | EOF 47 | then 48 | ## ah well done 49 | AC_MSG_RESULT([${probe_M4}]) 50 | M4="${probe_M4}" 51 | else 52 | ## check if a little buffer massage solves the problem 53 | probe_M4="${M4-m4} -B16384" 54 | if ${probe_M4} >/dev/null 2>&1 \ 55 | -Dx='y y y y y y y y y y y y y y y y' \ 56 | -Dy='z z z z z z z z z z z z z z z z' \ 57 | -Dz='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0' <<'EOF' 58 | [define(`foo', x)] 59 | EOF 60 | then 61 | ## very well then, let's use -B 62 | AC_MSG_RESULT([${probe_M4}]) 63 | M4="${probe_M4}" 64 | else 65 | AC_MSG_WARN([m4 on this machine might suffer from big buffers.]) 66 | M4="${M4-m4}" 67 | fi 68 | fi 69 | 70 | AC_DEFINE_UNQUOTED([YUCK_M4], ["${M4}"], [m4 value used for yuck build]) 71 | ])dnl AX_CHECK_M4_BUFFERS 72 | 73 | AC_DEFUN([AX_CHECK_YUCK], [dnl 74 | AC_ARG_WITH([included-yuck], [dnl 75 | AS_HELP_STRING([--with-included-yuck], [ 76 | Use included copy of the yuck command line parser generator 77 | instead of the system-wide one.])], [with_included_yuck="${withval}"], [$1]) 78 | 79 | AC_REQUIRE([AX_CHECK_M4_BUFFERS]) 80 | if test "${with_included_yuck}" != "yes"; then 81 | AC_PATH_PROG([YUCK], [yuck]) 82 | AC_ARG_VAR([YUCK], [full path to the yuck tool]) 83 | 84 | if test -n "${YUCK}"; then 85 | ## see what m4 they used back then 86 | YUCK_M4=`${YUCK} config --m4 2>/dev/null` 87 | M4="${YUCK_M4-$M4}" 88 | fi 89 | fi 90 | AM_CONDITIONAL([HAVE_YUCK], [dnl 91 | test "${with_included_yuck}" != "yes" -a -n "${YUCK}"]) 92 | 93 | ## further requirement is either getline() or fgetln() 94 | AC_CHECK_FUNCS([getline]) 95 | AC_CHECK_FUNCS([fgetln]) 96 | ])dnl AX_CHECK_YUCK 97 | 98 | AC_DEFUN([AX_YUCK_SCMVER], [dnl 99 | ## initially generate version.mk and yuck.version here 100 | ## because only GNU make can do this at make time 101 | pushdef([vfile], [$1]) 102 | 103 | AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) 104 | AC_LANG_PUSH([C]) 105 | AC_PROG_CC_C99 106 | ## use our yuck-scmver tool 107 | AC_MSG_CHECKING([for stipulated version files]) 108 | save_CPPFLAGS="${CPPFLAGS}" 109 | CPPFLAGS="-I${srcdir}/src -I${ac_aux_dir} ${CPPFLAGS}" 110 | AC_RUN_IFELSE([AC_LANG_SOURCE([[ 111 | #define CONFIGURE 112 | #define _XOPEN_SOURCE 600 113 | #define VERSION_FILE "${srcdir}/.version" 114 | #include "yuck-scmver.c" 115 | ]])], [STIP_VERSION=`./conftest$EXEEXT`], [AC_MSG_RESULT([none])], [dnl 116 | AC_MSG_RESULT([impossible, cross-compiling]) 117 | if test -f "[]vfile[]" -o \ 118 | -f "${srcdir}/[]vfile[]" -o \ 119 | -f "${srcdir}/.version"; then 120 | AC_MSG_NOTICE([ 121 | Files that (possibly) mandate versions have been detected. 122 | These are `]vfile[' or `${srcdir}/]vfile[' or `${srcdir}/.version'. 123 | However, their contents cannot be automatically checked for integrity 124 | due to building for a platform other than the current one 125 | (cross-compiling). 126 | 127 | I will proceed with the most conservative guess for the stipulated 128 | version, which is `${VERSION}'. 129 | 130 | If that appears to be wrong, or needs overriding, please edit the 131 | aforementioned files manually. 132 | 133 | Also note, even though this project comes with all the tools to 134 | perform a successful bootstrap for any of the files above, should 135 | they go out of date or be deleted, they don't support cross-builds. 136 | ]) 137 | fi 138 | ]) 139 | CPPFLAGS="${save_CPPFLAGS}" 140 | AC_LANG_POP([C]) 141 | 142 | if test -n "${STIP_VERSION}"; then 143 | VERSION="${STIP_VERSION}" 144 | fi 145 | ## also massage version.mk file 146 | if test -f "[]vfile[]" -a ! -w "[]vfile[]"; then 147 | : 148 | elif test -f "${srcdir}/[]vfile[]"; then 149 | ## make sure it's in the builddir as well 150 | cp -p "${srcdir}/[]vfile[]" "[]vfile[]" 2>/dev/null 151 | elif test -f "${srcdir}/[]vfile[].in"; then 152 | ${M4-m4} -DYUCK_SCMVER_VERSION="${VERSION}" \ 153 | "${srcdir}/[]vfile[].in" > "[]vfile[]" 154 | else 155 | echo "VERSION = ${VERSION}" > "[]vfile[]" 156 | fi 157 | ## make sure .version is generated (for version.mk target in GNUmakefile) 158 | if test -f "${srcdir}/.version"; then 159 | cp -p "${srcdir}/.version" ".version" 2>/dev/null 160 | else 161 | echo "v${VERSION}" > ".version" 2>/dev/null 162 | fi 163 | 164 | popdef([vfile]) 165 | ])dnl AX_YUCK_SCMVER 166 | 167 | dnl yuck.m4 ends here 168 | -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: sample 2 | version: '0.1.0-GIT' 3 | summary: Produce a sample of lines from files. 4 | description: | 5 | Command-line tool to produce a sample of lines from files. 6 | The sample size is either fixed or proportional to the size 7 | of the file. Additionally, the header and footer can be 8 | included in the sample. 9 | grade: stable 10 | confinement: strict 11 | 12 | apps: 13 | sample: 14 | command: bin/sample 15 | 16 | parts: 17 | sample: 18 | plugin: autotools 19 | source: https://github.com/hroptatyr/sample.git 20 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | ### Makefile.am 2 | include $(top_builddir)/version.mk 3 | 4 | AM_CFLAGS = $(EXTRA_CFLAGS) 5 | AM_CPPFLAGS = -D_POSIX_C_SOURCE=201001L -D_XOPEN_SOURCE=700 -D_BSD_SOURCE 6 | AM_CPPFLAGS += -D_HAVE_VERSION_H 7 | AM_LDFLAGS = $(XCCLDFLAGS) 8 | 9 | bin_PROGRAMS = 10 | noinst_PROGRAMS = 11 | lib_LTLIBRARIES = 12 | noinst_LTLIBRARIES = 13 | pkglib_LTLIBRARIES = 14 | noinst_HEADERS = 15 | BUILT_SOURCES = 16 | EXTRA_DIST = $(BUILT_SOURCES) 17 | CLEANFILES = 18 | SUFFIXES = 19 | 20 | CLEANFILES += version.c 21 | EXTRA_DIST += version.c.in 22 | EXTRA_DIST += nifty.h 23 | 24 | 25 | bin_PROGRAMS += sample 26 | sample_SOURCES = sample.c 27 | sample_SOURCES += version.c version.h 28 | sample_LDADD = -lm 29 | BUILT_SOURCES += sample.yucc 30 | 31 | 32 | ## version rules 33 | version.c: $(srcdir)/version.c.in $(top_builddir)/.version 34 | $(AM_V_GEN) PATH="$(top_builddir)/build-aux:$${PATH}" \ 35 | yuck$(EXEEXT) scmver --ignore-noscm --force -o $@ \ 36 | --use-reference --reference $(top_builddir)/.version \ 37 | $(srcdir)/version.c.in 38 | 39 | ## yuck rule 40 | SUFFIXES += .yuck 41 | SUFFIXES += .yucc 42 | .yuck.yucc: 43 | $(AM_V_GEN) PATH=$(top_builddir)/build-aux:"$${PATH}" \ 44 | yuck$(EXEEXT) gen -o $@ $< 45 | 46 | ## Create preprocessor output (debugging purposes only) 47 | .c.i: 48 | $(COMPILE) -E -o $@ $< 49 | 50 | ## Create assembler output (debugging purposes only) 51 | .c.s: 52 | $(COMPILE) -S -c $(AM_CFLAGS) $< 53 | -------------------------------------------------------------------------------- /src/nifty.h: -------------------------------------------------------------------------------- 1 | /*** nifty.h -- generally handy macroes 2 | * 3 | * Copyright (C) 2009-2022 Sebastian Freundt 4 | * 5 | * Author: Sebastian Freundt 6 | * 7 | * This file is part of sample. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the author nor the names of any contributors 21 | * may be used to endorse or promote products derived from this 22 | * software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 25 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 31 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 32 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 34 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | ***/ 37 | #if !defined INCLUDED_nifty_h_ 38 | #define INCLUDED_nifty_h_ 39 | #include 40 | #include 41 | 42 | #if !defined LIKELY 43 | # define LIKELY(_x) __builtin_expect((_x), 1) 44 | #endif /* !LIKELY */ 45 | #if !defined UNLIKELY 46 | # define UNLIKELY(_x) __builtin_expect((_x), 0) 47 | #endif /* UNLIKELY */ 48 | 49 | #if !defined UNUSED 50 | # define UNUSED(_x) _x __attribute__((unused)) 51 | #endif /* !UNUSED */ 52 | 53 | #if !defined ALGN 54 | # define ALGN(_x, to) _x __attribute__((aligned(to))) 55 | #endif /* !ALGN */ 56 | 57 | #if !defined countof 58 | # define countof(x) (sizeof(x) / sizeof(*x)) 59 | #endif /* !countof */ 60 | 61 | #if !defined strlenof 62 | # define strlenof(x) (sizeof(x) - 1U) 63 | #endif /* !strlenof */ 64 | 65 | #define _paste(x, y) x ## y 66 | #define paste(x, y) _paste(x, y) 67 | 68 | #if !defined with 69 | # define with(args...) \ 70 | for (args, *paste(__ep, __LINE__) = (void*)1; \ 71 | paste(__ep, __LINE__); paste(__ep, __LINE__)= 0) 72 | #endif /* !with */ 73 | 74 | #if !defined if_with 75 | # define if_with(init, args...) \ 76 | for (init, *paste(__ep, __LINE__) = (void*)1; \ 77 | paste(__ep, __LINE__) && (args); paste(__ep, __LINE__)= 0) 78 | #endif /* !if_with */ 79 | 80 | #define once \ 81 | static int paste(__, __LINE__); \ 82 | if (!paste(__, __LINE__)++) 83 | #define but_first \ 84 | static int paste(__, __LINE__); \ 85 | if (paste(__, __LINE__)++) 86 | 87 | static inline void* 88 | deconst(const void *cp) 89 | { 90 | union { 91 | const void *c; 92 | void *p; 93 | } tmp = {cp}; 94 | return tmp.p; 95 | } 96 | 97 | static inline size_t 98 | memncpy(void *restrict buf, const void *src, size_t zrc) 99 | { 100 | memcpy(buf, src, zrc); 101 | return zrc; 102 | } 103 | 104 | static inline size_t 105 | memnmove(void *tgt, const void *src, size_t zrc) 106 | { 107 | memmove(tgt, src, zrc); 108 | return zrc; 109 | } 110 | 111 | #endif /* INCLUDED_nifty_h_ */ 112 | -------------------------------------------------------------------------------- /src/sample.c: -------------------------------------------------------------------------------- 1 | /*** sample.c -- output a sample of a file 2 | * 3 | * Copyright (C) 2016-2022 Sebastian Freundt 4 | * 5 | * Author: Sebastian Freundt 6 | * 7 | * This file is part of sample. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the author nor the names of any contributors 21 | * may be used to endorse or promote products derived from this 22 | * software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 25 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 31 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 32 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 34 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | ***/ 37 | #if defined HAVE_CONFIG_H 38 | # include "config.h" 39 | #endif /* HAVE_CONFIG_H */ 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include "nifty.h" 56 | 57 | #if defined BUFSIZ 58 | # undef BUFSIZ 59 | #endif /* BUFSIZ */ 60 | #define BUFSIZ (65536U) 61 | 62 | static size_t nheader = 5U; 63 | static size_t nfooter = 5U; 64 | static long long unsigned int rate = UINT32_MAX / 10U; 65 | static size_t nfixed; 66 | /* limit for VLAs */ 67 | static size_t stklmt; 68 | static unsigned int quietp; 69 | 70 | 71 | static void 72 | __attribute__((format(printf, 1, 2))) 73 | error(const char *fmt, ...) 74 | { 75 | va_list vap; 76 | va_start(vap, fmt); 77 | vfprintf(stderr, fmt, vap); 78 | va_end(vap); 79 | if (errno) { 80 | fputc(':', stderr); 81 | fputc(' ', stderr); 82 | fputs(strerror(errno), stderr); 83 | } 84 | fputc('\n', stderr); 85 | return; 86 | } 87 | 88 | static inline __attribute__((pure, const)) size_t 89 | min_z(size_t z1, size_t z2) 90 | { 91 | return z1 <= z2 ? z1 : z2; 92 | } 93 | 94 | 95 | static uint64_t g32; 96 | 97 | static inline __attribute__((pure, const)) uint32_t 98 | ror32(uint32_t v, unsigned int r) 99 | { 100 | return (v >> r) | (v << ((-r) & 0x1fU)); 101 | } 102 | 103 | static inline uint32_t pcg32_xsh_rr(uint64_t *s) 104 | { 105 | static const uint64_t m = 0x5851f42d4c957f2dULL; 106 | static const uint64_t i = 0x1ULL; 107 | uint64_t old = *s; 108 | uint32_t xor = ((old >> 18U) ^ old) >> 27U; 109 | uint32_t rot = old >> 59U; 110 | *s = old * m + i; 111 | return ror32(xor, rot); 112 | } 113 | 114 | static void 115 | pcg32_srandom(uint64_t seed) 116 | { 117 | pcg32_xsh_rr(&g32); 118 | g32 += seed; 119 | pcg32_xsh_rr(&g32); 120 | return; 121 | } 122 | 123 | static inline unsigned int 124 | runifu32(void) 125 | { 126 | return pcg32_xsh_rr(&g32); 127 | } 128 | 129 | static uint32_t 130 | runifu32b(uint32_t bound) 131 | { 132 | uint32_t threshold = -bound % bound; 133 | for (;;) { 134 | uint32_t r = pcg32_xsh_rr(&g32); 135 | if (r >= threshold) { 136 | return r % bound; 137 | } 138 | } 139 | } 140 | 141 | static unsigned int 142 | rexp32(unsigned int n, unsigned int d) 143 | { 144 | double u = (double)runifu32() / 0x1.p32; 145 | double lambda = log((double)n / (double)d); 146 | return (unsigned int)(log1p(-u) / lambda); 147 | } 148 | 149 | 150 | /* buffer */ 151 | static char *buf; 152 | static size_t zbuf; 153 | /* reservoir */ 154 | static char *rsv; 155 | static size_t zrsv; 156 | /* compactify */ 157 | static uint8_t *comp; 158 | static size_t *idir; 159 | 160 | static void 161 | compactify(size_t *restrict off, const size_t m, const size_t n) 162 | { 163 | /* helper for reservoir sampling 164 | * compact M lines into N whose offsets are in OFF */ 165 | size_t o = 0U; 166 | 167 | if (UNLIKELY(comp == NULL)) { 168 | comp = malloc(m * sizeof(*comp)); 169 | if (UNLIKELY(comp == NULL)) { 170 | return; 171 | } 172 | idir = malloc(n * sizeof(*idir)); 173 | if (UNLIKELY(idir == NULL)) { 174 | free(comp); 175 | comp = NULL; 176 | return; 177 | } 178 | } 179 | 180 | /* prep compactifier, this is a radix sort */ 181 | memset(comp, 0, m * sizeof(*comp)); 182 | /* prep IDIR buffer */ 183 | for (size_t i = 0U; i < n; i++) { 184 | idir[i] = i; 185 | } 186 | for (size_t i = n; i < m; i++) { 187 | idir[runifu32b(n)] = i; 188 | } 189 | /* now sort him */ 190 | for (size_t i = 0U; i < n; i++) { 191 | comp[idir[i]] = 1U; 192 | } 193 | 194 | /* ... now move them lines 195 | * we calculate streaks of lines and move them in bulk */ 196 | for (size_t i = 0U, beg = 0U, end; beg < m; beg = end + 1U) { 197 | size_t len; 198 | 199 | /* find first line to move, COMP[] is guaranteed to have 200 | * N matches */ 201 | for (; !comp[beg]; beg++); 202 | for (end = beg + 1U; end < m && comp[end]; end++); 203 | 204 | memmove(rsv + o, rsv + off[beg], len = off[end] - off[beg]); 205 | 206 | /* resolve into lines again */ 207 | for (size_t j = beg, bof = off[beg]; j < end; j++) { 208 | off[i++] = o + off[j] - bof; 209 | } 210 | o += len; 211 | } 212 | off[n] = o; 213 | return; 214 | } 215 | 216 | static int 217 | sample_0(int fd) 218 | { 219 | (void)fd; 220 | return 0; 221 | } 222 | 223 | static int 224 | sample_gen(int fd) 225 | { 226 | /* generic sampler */ 227 | /* number of lines read so far */ 228 | size_t nfln = 0U; 229 | /* number of output lines so far */ 230 | size_t noln = 0U; 231 | /* fill of BUF, also used as offset of end-of-header in HDR mode */ 232 | size_t nbuf = 0U; 233 | /* index into BUF to the beginning of the last unprocessed line */ 234 | size_t ibuf = 0U; 235 | /* number of octets read per read() */ 236 | ssize_t nrd; 237 | /* offsets to footer */ 238 | size_t _last[stklmt]; 239 | size_t *last = _last; 240 | #define LAST(x) last[(x) % (nfooter + 1U)] 241 | #define FIRST(x) ((x) > nfooter ? LAST(x) : 0U) 242 | /* 3 major states, HEAD BEEF/CAKE and TAIL */ 243 | enum { 244 | EVAL, 245 | HEAD, 246 | BEEF, 247 | TAIL, 248 | CAKE, 249 | } state = EVAL; 250 | 251 | with (char *tmp = realloc(buf, BUFSIZ)) { 252 | if (UNLIKELY(tmp == NULL)) { 253 | /* just bugger off */ 254 | return -1; 255 | } 256 | /* otherwise swap ptrs */ 257 | buf = tmp; 258 | zbuf = BUFSIZ; 259 | } 260 | 261 | if (nfooter >= countof(_last)) { 262 | /* better do it with heap space */ 263 | last = malloc((nfooter + 1U) * sizeof(*last)); 264 | } 265 | 266 | /* deal with header */ 267 | while ((nrd = read(fd, buf + nbuf, zbuf - nbuf)) > 0) { 268 | /* calc next round's NBUF already */ 269 | nbuf += nrd; 270 | 271 | switch (state) { 272 | case EVAL: 273 | if (rate > UINT32_MAX) { 274 | /* oh they want everything printed */ 275 | fwrite(buf, sizeof(*buf), nrd, stdout); 276 | nbuf = 0U; 277 | break; 278 | } else if (!nfooter && !nheader) { 279 | goto cake; 280 | } else if (!nheader) { 281 | goto tail; 282 | } 283 | /* otherwise let HEAD state decide */ 284 | state = HEAD; 285 | case HEAD: 286 | for (const char *x; 287 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf));) { 288 | const size_t o = ibuf; 289 | 290 | ibuf = ++x - buf; 291 | fwrite(buf + o, sizeof(*buf), ibuf - o, stdout); 292 | noln++; 293 | 294 | if (++nfln >= nheader) { 295 | if (UNLIKELY(!nfooter && !rate)) { 296 | /* that's it */ 297 | return 0; 298 | } else if (!nfooter) { 299 | goto cake; 300 | } 301 | /* otherwise the most generic mode */ 302 | goto tail; 303 | } 304 | } 305 | goto wrap; 306 | 307 | cake: 308 | if (!quietp) { 309 | fwrite("...\n", 1, 4U, stdout); 310 | } 311 | state = CAKE; 312 | case CAKE: 313 | /* CAKE is the mode where we don't track tail lines */ 314 | for (const char *x; 315 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf));) { 316 | const size_t o = ibuf; 317 | 318 | ibuf = ++x - buf; 319 | nfln++; 320 | 321 | /* sample */ 322 | if (runifu32() < rate) { 323 | fwrite(buf + o, sizeof(*buf), 324 | ibuf - o, stdout); 325 | noln++; 326 | } 327 | } 328 | goto wrap; 329 | 330 | wrap: 331 | if (LIKELY(nbuf < zbuf / 2U)) { 332 | /* we've got enough buffer, use, him */ 333 | break; 334 | } else if (UNLIKELY(!ibuf)) { 335 | /* great, try a resize */ 336 | const size_t nuz = zbuf * 2U; 337 | char *tmp = realloc(buf, nuz); 338 | 339 | if (UNLIKELY(tmp == NULL)) { 340 | return -1; 341 | } 342 | /* otherwise assign and retry */ 343 | buf = tmp; 344 | zbuf = nuz; 345 | } else if (LIKELY(ibuf < nbuf)) { 346 | memmove(buf, buf + ibuf, nbuf - ibuf); 347 | } 348 | nbuf -= ibuf; 349 | ibuf = 0U; 350 | break; 351 | 352 | tail: 353 | state = TAIL; 354 | case TAIL: 355 | for (const char *x; 356 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf));) { 357 | /* keep track of footers */ 358 | LAST(nfln - nheader) = ibuf; 359 | ibuf = ++x - buf; 360 | 361 | if (++nfln > nheader + nfooter && rate) { 362 | goto beef; 363 | } 364 | } 365 | goto over; 366 | 367 | beef: 368 | if (!quietp) { 369 | fwrite("...\n", 1, 4U, stdout); 370 | } 371 | state = BEEF; 372 | /* we need one more sample step because the 373 | * condition above that got us here goes one 374 | * step further than it should, lest we print 375 | * the ellipsis when there's exactly 376 | * nheader + nfoooter lines in the buffer */ 377 | goto sample; 378 | 379 | case BEEF: 380 | for (const char *x; 381 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf));) { 382 | /* keep track of footers */ 383 | LAST(nfln - nheader) = ibuf; 384 | ibuf = ++x - buf; 385 | 386 | nfln++; 387 | 388 | sample: 389 | /* sample */ 390 | if (runifu32() < rate) { 391 | const size_t this = 392 | LAST(nfln - nheader + 0U); 393 | const size_t next = 394 | LAST(nfln - nheader + 1U); 395 | 396 | fwrite(buf + this, sizeof(*buf), 397 | next - this, stdout); 398 | noln++; 399 | } 400 | } 401 | goto over; 402 | 403 | over: 404 | /* beef buffer overrun handling */ 405 | with (const size_t frst = FIRST(nfln - nheader)) { 406 | if (LIKELY(nbuf < zbuf / 2U)) { 407 | /* just read more stuff */ 408 | break; 409 | } else if (UNLIKELY(!frst || frst == ibuf)) { 410 | /* resize and retry */ 411 | const size_t nuz = zbuf * 2U; 412 | char *tmp = realloc(buf, nuz); 413 | 414 | if (UNLIKELY(tmp == NULL)) { 415 | return -1; 416 | } 417 | /* otherwise assign and retry */ 418 | buf = tmp; 419 | zbuf = nuz; 420 | break; 421 | } 422 | memmove(buf, buf + frst, nbuf - frst); 423 | for (size_t i = 0U, 424 | n = min_z(nfooter + 1U, 425 | nfln - nheader); 426 | i < n; i++) { 427 | last[i] -= frst; 428 | } 429 | nbuf -= frst; 430 | ibuf -= frst; 431 | } 432 | /* keep track of last footer */ 433 | LAST(nfln - nheader) = ibuf; 434 | break; 435 | } 436 | } 437 | if (noln > nheader || 438 | !rate && nfln > nheader + nfooter) { 439 | if (!quietp) { 440 | fwrite("...\n", 1, 4U, stdout); 441 | } 442 | } 443 | /* fast forward footer if there wasn't enough lines */ 444 | if (nfln > nheader + nfooter) { 445 | const size_t beg = LAST(nfln - nheader - nfooter - 0U); 446 | const size_t end = LAST(nfln - nheader - nfooter - 1U); 447 | fwrite(buf + beg, sizeof(*buf), end - beg, stdout); 448 | } else if (nfln > nheader) { 449 | const size_t beg = last[0U]; 450 | const size_t end = last[nfln - nheader]; 451 | fwrite(buf + beg, sizeof(*buf), end - beg, stdout); 452 | } 453 | if (last != _last) { 454 | free(last); 455 | } 456 | return 0; 457 | } 458 | 459 | static int 460 | sample_rsv(int fd) 461 | { 462 | /* generic sampler */ 463 | /* number of lines read so far */ 464 | size_t nfln = 0U; 465 | /* fill of BUF, also used as offset of end-of-header in HDR mode */ 466 | size_t nbuf = 0U; 467 | /* index into BUF to the beginning of the last unprocessed line */ 468 | size_t ibuf = 0U; 469 | /* skip up to this line */ 470 | size_t gap = 0U; 471 | /* nfixed buffer oversampling */ 472 | size_t nfxd = 0U; 473 | /* number of octets read per read() */ 474 | ssize_t nrd; 475 | /* offsets to footer */ 476 | size_t _last[stklmt / 3U]; 477 | size_t *last = _last; 478 | /* reservoir lines 479 | * we store offsets into RSV buffer in LRSV, which is 3 times the 480 | * size of NFIXED to concentrate the memmove()ing 481 | * the kill count for each cell is stored in LGAP */ 482 | size_t _lrsv[stklmt / 3U]; 483 | size_t *lrsv = _lrsv; 484 | const size_t mult = 4U; 485 | /* 3 major states, HEAD BEEF/CAKE and TAIL */ 486 | enum { 487 | EVAL, 488 | HEAD, 489 | BEEF, 490 | FILL, 491 | BEXP, 492 | } state = EVAL; 493 | 494 | #define MEMZCPY(tgt, off, tsz, src, len) \ 495 | do { \ 496 | const size_t nul = (off) + (len); \ 497 | if (nul > (tsz)) { \ 498 | size_t nuz = (tsz); \ 499 | char *tmp; \ 500 | while ((nuz *= 2U) < nul); \ 501 | tmp = realloc((tgt), nuz); \ 502 | if (UNLIKELY(tmp == NULL)) { \ 503 | return -1; \ 504 | } \ 505 | /* otherwise assign */ \ 506 | (tgt) = tmp; \ 507 | (tsz) = nuz; \ 508 | } \ 509 | memcpy((tgt) + (off), (src), (len)); \ 510 | } while (0) 511 | 512 | with (char *tmp = realloc(buf, BUFSIZ)) { 513 | if (UNLIKELY(tmp == NULL)) { 514 | /* just bugger off */ 515 | return -1; 516 | } 517 | /* otherwise swap ptrs */ 518 | buf = tmp; 519 | zbuf = BUFSIZ; 520 | } 521 | with (char *tmp = realloc(rsv, BUFSIZ)) { 522 | if (UNLIKELY(tmp == NULL)) { 523 | /* just bugger off */ 524 | return -1; 525 | } 526 | /* otherwise swap ptrs */ 527 | rsv = tmp; 528 | zrsv = BUFSIZ; 529 | } 530 | if (nfooter >= countof(_last)) { 531 | /* do him with heap space */ 532 | last = malloc((nfooter + 1U) * sizeof(*last)); 533 | } 534 | if (mult * nfixed >= countof(_lrsv)) { 535 | /* do her with heap space */ 536 | lrsv = malloc((mult * nfixed + 1U) * sizeof(*lrsv)); 537 | } 538 | 539 | /* deal with header */ 540 | while ((nrd = read(fd, buf + nbuf, zbuf - nbuf)) > 0) { 541 | /* calc next round's NBUF already */ 542 | nbuf += nrd; 543 | 544 | switch (state) { 545 | case EVAL: 546 | if (!nheader) { 547 | goto fill; 548 | } 549 | /* otherwise let HEAD state decide */ 550 | state = HEAD; 551 | case HEAD: 552 | for (const char *x; 553 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf));) { 554 | const size_t o = ibuf; 555 | 556 | ibuf = ++x - buf; 557 | fwrite(buf + o, sizeof(*buf), ibuf - o, stdout); 558 | 559 | if (++nfln >= nheader) { 560 | if (UNLIKELY(!nfixed)) { 561 | /* that's it */ 562 | return 0; 563 | } 564 | /* otherwise the most generic mode */ 565 | goto fill; 566 | } 567 | } 568 | goto wrap; 569 | 570 | wrap: 571 | if (LIKELY(nbuf < zbuf / 2U)) { 572 | /* no need for buffer juggling */ 573 | break; 574 | } else if (UNLIKELY(!ibuf)) { 575 | /* great, try a resize */ 576 | const size_t nuz = zbuf * 2U; 577 | char *tmp = realloc(buf, nuz); 578 | 579 | if (UNLIKELY(tmp == NULL)) { 580 | return -1; 581 | } 582 | /* otherwise assign and retry */ 583 | buf = tmp; 584 | zbuf = nuz; 585 | } else if (LIKELY(ibuf < nbuf)) { 586 | memmove(buf, buf + ibuf, nbuf - ibuf); 587 | nbuf -= ibuf; 588 | ibuf = 0U; 589 | } 590 | break; 591 | 592 | fill: 593 | state = FILL; 594 | case FILL: 595 | for (const char *x; 596 | nfln - nheader < nfixed && 597 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 598 | nfln++) { 599 | /* keep track of footers */ 600 | lrsv[nfln - nheader] = ibuf; 601 | LAST(nfln - nheader) = ibuf; 602 | ibuf = ++x - buf; 603 | } 604 | for (const char *x; 605 | nfln - nheader >= nfixed && 606 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 607 | ) { 608 | LAST(nfln - nheader) = ibuf; 609 | ibuf = ++x - buf; 610 | 611 | if (++nfln - nheader >= nfixed + nfooter) { 612 | goto beef; 613 | } 614 | } 615 | goto over; 616 | 617 | beef: 618 | state = BEEF; 619 | /* take on the reservoir */ 620 | MEMZCPY(rsv, 0U, zrsv, buf + lrsv[0U], 621 | LAST(nfln - nheader - nfooter) - lrsv[0U]); 622 | lrsv[nfixed] = LAST(nfln - nheader - nfooter); 623 | for (size_t i = nfixed + 1U; i > 0; i--) { 624 | lrsv[i - 1U] -= lrsv[0U]; 625 | } 626 | 627 | nfxd = nfixed; 628 | 629 | /* we need one more sample step because the 630 | * condition above that got us here goes one 631 | * step further than it should, lest we print 632 | * the ellipsis when there's exactly 633 | * nheader + nfooter lines in the buffer */ 634 | case BEEF: 635 | for (const char *x; 636 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 637 | ibuf = x - buf + 1U, nfln++) { 638 | /* current line length */ 639 | const size_t y = 640 | LAST(nfln - nheader - nfooter + 1U) - 641 | LAST(nfln - nheader - nfooter + 0U); 642 | 643 | /* keep track of footers */ 644 | LAST(nfln - nheader) = ibuf; 645 | 646 | if (nfln - nheader >= 4U * nfixed) { 647 | /* switch to gap sampling */ 648 | goto bexp; 649 | } 650 | 651 | /* keep with probability nfixed / nfln */ 652 | if (runifu32b(nfln - nheader) >= nfixed) { 653 | continue; 654 | } 655 | 656 | if (UNLIKELY(nfxd >= mult * nfixed)) { 657 | /* condense lrsv */ 658 | compactify(lrsv, nfxd, nfixed); 659 | nfxd = nfixed; 660 | } 661 | 662 | /* bang this line */ 663 | MEMZCPY(rsv, lrsv[nfxd], zrsv, 664 | buf + LAST(nfln - nheader - nfooter), y); 665 | /* and memorise him */ 666 | lrsv[nfxd + 1U] = lrsv[nfxd] + y; 667 | nfxd++; 668 | } 669 | goto over; 670 | 671 | bexp: 672 | gap = nfln - nheader + 673 | rexp32(nfln - nheader - nfixed, nfln); 674 | state = BEXP; 675 | case BEXP: 676 | for (const char *x; 677 | nfln - nheader < gap && 678 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 679 | ibuf = x - buf + 1U, nfln++) { 680 | /* every line could be our last, so keep 681 | * track of them */ 682 | LAST(nfln - nheader) = ibuf; 683 | } 684 | for (const char *x; 685 | nfln - nheader >= gap && 686 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 687 | ) { 688 | /* current line length */ 689 | const size_t y = 690 | LAST(nfln - nheader - nfooter + 1U) - 691 | LAST(nfln - nheader - nfooter + 0U); 692 | 693 | /* keep track of footers */ 694 | LAST(nfln - nheader) = ibuf; 695 | 696 | if (UNLIKELY(nfxd >= mult * nfixed)) { 697 | /* condense lrsv */ 698 | compactify(lrsv, nfxd, nfixed); 699 | nfxd = nfixed; 700 | } 701 | 702 | /* bang this line */ 703 | MEMZCPY(rsv, lrsv[nfxd], zrsv, 704 | buf + LAST(nfln - nheader - nfooter), y); 705 | /* and memorise him */ 706 | lrsv[nfxd + 1U] = lrsv[nfxd] + y; 707 | nfxd++; 708 | 709 | ibuf = x - buf + 1U; 710 | nfln++; 711 | goto bexp; 712 | } 713 | goto over; 714 | 715 | over: 716 | /* beef buffer overrun */ 717 | with (const size_t frst = FIRST(nfln - nheader)) { 718 | if (LIKELY(nbuf < zbuf / 2U)) { 719 | /* just read more stuff */ 720 | break; 721 | } else if (UNLIKELY(!frst || frst == ibuf || 722 | nfln - nheader <= 723 | nfixed + nfooter)) { 724 | /* resize and retry */ 725 | const size_t nuz = zbuf * 2U; 726 | char *tmp = realloc(buf, nuz); 727 | 728 | if (UNLIKELY(tmp == NULL)) { 729 | return -1; 730 | } 731 | /* otherwise assign and retry */ 732 | buf = tmp; 733 | zbuf = nuz; 734 | break; 735 | } 736 | memmove(buf, buf + frst, nbuf - frst); 737 | for (size_t i = 0U, 738 | n = min_z(nfooter + 1U, 739 | nfln - nheader); 740 | i < n; i++) { 741 | last[i] -= frst; 742 | } 743 | nbuf -= frst; 744 | ibuf -= frst; 745 | } 746 | /* keep track of last footer */ 747 | LAST(nfln - nheader) = ibuf; 748 | break; 749 | } 750 | } 751 | if (nfln >= nheader + nfixed + nfooter) { 752 | /* compactify to obtain the final result */ 753 | compactify(lrsv, nfxd, nfixed); 754 | 755 | const size_t z = lrsv[nfixed]; 756 | const size_t beg = LAST(nfln - nheader - nfooter - 0U); 757 | const size_t end = LAST(nfln - nheader - nfooter - 1U); 758 | 759 | if (nfln > nheader + nfixed + nfooter) { 760 | if (!quietp) { 761 | fwrite("...\n", 1, 4U, stdout); 762 | } 763 | } 764 | fwrite(rsv + lrsv[0U], sizeof(*rsv), z - lrsv[0U], stdout); 765 | if (nfln > nheader + nfixed + nfooter) { 766 | if (!quietp) { 767 | fwrite("...\n", 1, 4U, stdout); 768 | } 769 | } 770 | fwrite(buf + beg, sizeof(*buf), end - beg, stdout); 771 | } else if (nfln > nheader + nfooter) { 772 | const size_t beg = lrsv[0U]; 773 | const size_t end = LAST(nfln - nheader - nfooter - 1U); 774 | fwrite(buf + beg, sizeof(*buf), end - beg, stdout); 775 | } else if (nfln > nheader) { 776 | const size_t beg = last[0U]; 777 | const size_t end = last[nfln - nheader]; 778 | fwrite(buf + beg, sizeof(*buf), end - beg, stdout); 779 | } 780 | if (last != _last) { 781 | free(last); 782 | } 783 | if (lrsv != _lrsv) { 784 | free(lrsv); 785 | } 786 | return 0; 787 | } 788 | 789 | static int 790 | sample_rsv_1f(int fd) 791 | { 792 | /* generic sampler */ 793 | /* number of lines read so far */ 794 | size_t nfln = 0U; 795 | /* fill of BUF, also used as offset of end-of-header in HDR mode */ 796 | size_t nbuf = 0U; 797 | /* index into BUF to the beginning of the last unprocessed line */ 798 | size_t ibuf = 0U; 799 | /* skip up to this line */ 800 | size_t gap = 0U; 801 | /* nfixed buffer oversampling */ 802 | size_t nfxd = 0U; 803 | /* number of octets read per read() */ 804 | ssize_t nrd; 805 | /* offsets to footer */ 806 | size_t last; 807 | /* reservoir lines 808 | * we store offsets into RSV buffer in LRSV, which is 3 times the 809 | * size of NFIXED to concentrate the memmove()ing 810 | * the kill count for each cell is stored in LGAP */ 811 | size_t _lrsv[stklmt / 3U]; 812 | size_t *lrsv = _lrsv; 813 | const size_t mult = 4U; 814 | /* 3 major states, HEAD BEEF/CAKE and TAIL */ 815 | enum { 816 | EVAL, 817 | HEAD, 818 | BEEF, 819 | FILL, 820 | BEXP, 821 | } state = EVAL; 822 | 823 | with (char *tmp = realloc(buf, BUFSIZ)) { 824 | if (UNLIKELY(tmp == NULL)) { 825 | /* just bugger off */ 826 | return -1; 827 | } 828 | /* otherwise swap ptrs */ 829 | buf = tmp; 830 | zbuf = BUFSIZ; 831 | } 832 | with (char *tmp = realloc(rsv, BUFSIZ)) { 833 | if (UNLIKELY(tmp == NULL)) { 834 | /* just bugger off */ 835 | return -1; 836 | } 837 | /* otherwise swap ptrs */ 838 | rsv = tmp; 839 | zrsv = BUFSIZ; 840 | } 841 | if (mult * nfixed >= countof(_lrsv)) { 842 | /* do her with heap space */ 843 | lrsv = malloc((mult * nfixed + 1U) * sizeof(*lrsv)); 844 | } 845 | 846 | /* deal with header */ 847 | while ((nrd = read(fd, buf + nbuf, zbuf - nbuf)) > 0) { 848 | /* calc next round's NBUF already */ 849 | nbuf += nrd; 850 | 851 | switch (state) { 852 | case EVAL: 853 | if (!nheader) { 854 | goto fill; 855 | } 856 | /* otherwise let HEAD state decide */ 857 | state = HEAD; 858 | case HEAD: 859 | for (const char *x; 860 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf));) { 861 | const size_t o = ibuf; 862 | 863 | ibuf = ++x - buf; 864 | fwrite(buf + o, sizeof(*buf), ibuf - o, stdout); 865 | 866 | if (++nfln >= nheader) { 867 | if (UNLIKELY(!nfixed)) { 868 | /* that's it */ 869 | return 0; 870 | } 871 | /* otherwise the most generic mode */ 872 | goto fill; 873 | } 874 | } 875 | goto wrap; 876 | 877 | wrap: 878 | if (LIKELY(nbuf < zbuf / 2U)) { 879 | /* no need for buffer juggling */ 880 | break; 881 | } else if (UNLIKELY(!ibuf)) { 882 | /* great, try a resize */ 883 | const size_t nuz = zbuf * 2U; 884 | char *tmp = realloc(buf, nuz); 885 | 886 | if (UNLIKELY(tmp == NULL)) { 887 | return -1; 888 | } 889 | /* otherwise assign and retry */ 890 | buf = tmp; 891 | zbuf = nuz; 892 | } else if (LIKELY(ibuf < nbuf)) { 893 | memmove(buf, buf + ibuf, nbuf - ibuf); 894 | nbuf -= ibuf; 895 | ibuf = 0U; 896 | } 897 | break; 898 | 899 | fill: 900 | state = FILL; 901 | case FILL: 902 | for (const char *x; 903 | nfln - nheader < nfixed && 904 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 905 | nfln++) { 906 | /* keep track of footers */ 907 | lrsv[nfln - nheader] = ibuf; 908 | last = ibuf; 909 | ibuf = ++x - buf; 910 | } 911 | for (const char *x; 912 | nfln - nheader >= nfixed && 913 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 914 | ) { 915 | last = ibuf; 916 | ibuf = ++x - buf; 917 | 918 | if (++nfln - nheader >= nfixed + 1U) { 919 | goto beef; 920 | } 921 | } 922 | goto over; 923 | 924 | beef: 925 | state = BEEF; 926 | /* take on the reservoir */ 927 | MEMZCPY(rsv, 0U, zrsv, buf + lrsv[0U], last - lrsv[0U]); 928 | lrsv[nfixed] = last; 929 | for (size_t i = nfixed + 1U; i > 0; i--) { 930 | lrsv[i - 1U] -= lrsv[0U]; 931 | } 932 | 933 | nfxd = nfixed; 934 | 935 | /* we need one more sample step because the 936 | * condition above that got us here goes one 937 | * step further than it should, lest we print 938 | * the ellipsis when there's exactly 939 | * nheader + nfooter lines in the buffer */ 940 | case BEEF: 941 | for (const char *x; 942 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 943 | ibuf = x - buf + 1U, nfln++) { 944 | /* current line length */ 945 | const size_t beg = last; 946 | const size_t end = ibuf; 947 | 948 | /* keep track of footers */ 949 | last = ibuf; 950 | 951 | if (nfln - nheader >= 4U * nfixed) { 952 | /* switch to gap sampling */ 953 | goto bexp; 954 | } 955 | 956 | /* keep with probability nfixed / nfln */ 957 | if (runifu32b(nfln - nheader) >= nfixed) { 958 | continue; 959 | } 960 | 961 | if (UNLIKELY(nfxd >= mult * nfixed)) { 962 | /* condense lrsv */ 963 | compactify(lrsv, nfxd, nfixed); 964 | nfxd = nfixed; 965 | } 966 | 967 | /* bang this line */ 968 | MEMZCPY(rsv, lrsv[nfxd], zrsv, buf + beg, end - beg); 969 | /* and memorise him */ 970 | lrsv[nfxd + 1U] = lrsv[nfxd] + end - beg; 971 | nfxd++; 972 | } 973 | goto over; 974 | 975 | bexp: 976 | gap = nfln - nheader + 977 | rexp32(nfln - nheader - nfixed, nfln); 978 | state = BEXP; 979 | case BEXP: 980 | for (const char *x; 981 | nfln - nheader < gap && 982 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 983 | ibuf = x - buf + 1U, nfln++) { 984 | /* every line could be our last, so keep 985 | * track of them */ 986 | last = ibuf; 987 | } 988 | for (const char *x; 989 | nfln - nheader >= gap && 990 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 991 | ) { 992 | /* current line length */ 993 | const size_t beg = last; 994 | const size_t end = ibuf; 995 | 996 | /* keep track of footers */ 997 | last = ibuf; 998 | 999 | if (UNLIKELY(nfxd >= mult * nfixed)) { 1000 | /* condense lrsv */ 1001 | compactify(lrsv, nfxd, nfixed); 1002 | nfxd = nfixed; 1003 | } 1004 | 1005 | /* bang this line */ 1006 | MEMZCPY(rsv, lrsv[nfxd], zrsv, buf + beg, end - beg); 1007 | /* and memorise him */ 1008 | lrsv[nfxd + 1U] = lrsv[nfxd] + end - beg; 1009 | nfxd++; 1010 | 1011 | ibuf = x - buf + 1U; 1012 | nfln++; 1013 | goto bexp; 1014 | } 1015 | goto over; 1016 | 1017 | over: 1018 | /* beef buffer overrun */ 1019 | with (const size_t frst = last) { 1020 | if (LIKELY(nbuf < zbuf / 2U)) { 1021 | /* just read more stuff */ 1022 | break; 1023 | } else if (UNLIKELY(!frst || frst == ibuf || 1024 | nfln - nheader <= nfixed + 1U)) { 1025 | /* resize and retry */ 1026 | const size_t nuz = zbuf * 2U; 1027 | char *tmp = realloc(buf, nuz); 1028 | 1029 | if (UNLIKELY(tmp == NULL)) { 1030 | return -1; 1031 | } 1032 | /* otherwise assign and retry */ 1033 | buf = tmp; 1034 | zbuf = nuz; 1035 | break; 1036 | } 1037 | memmove(buf, buf + frst, nbuf - frst); 1038 | nbuf -= frst; 1039 | ibuf -= frst; 1040 | last = 0U; 1041 | } 1042 | break; 1043 | } 1044 | } 1045 | if (nfln >= nheader + nfixed + 1U) { 1046 | /* compactify to obtain the final result */ 1047 | compactify(lrsv, nfxd, nfixed); 1048 | 1049 | const size_t z = lrsv[nfixed]; 1050 | const size_t beg = last; 1051 | const size_t end = nbuf; 1052 | 1053 | if (nfln > nheader + nfixed + 1U) { 1054 | if (!quietp) { 1055 | fwrite("...\n", 1, 4U, stdout); 1056 | } 1057 | } 1058 | fwrite(rsv + lrsv[0U], sizeof(*rsv), z - lrsv[0U], stdout); 1059 | if (nfln > nheader + nfixed + 1U) { 1060 | if (!quietp) { 1061 | fwrite("...\n", 1, 4U, stdout); 1062 | } 1063 | } 1064 | fwrite(buf + beg, sizeof(*buf), end - beg, stdout); 1065 | } else if (nfln > nheader + 1U) { 1066 | const size_t beg = lrsv[0U]; 1067 | const size_t end = nbuf; 1068 | fwrite(buf + beg, sizeof(*buf), end - beg, stdout); 1069 | } else if (nfln > nheader) { 1070 | const size_t beg = last; 1071 | const size_t end = nbuf; 1072 | fwrite(buf + beg, sizeof(*buf), end - beg, stdout); 1073 | } 1074 | if (lrsv != _lrsv) { 1075 | free(lrsv); 1076 | } 1077 | return 0; 1078 | } 1079 | 1080 | static int 1081 | sample_rsv_0f(int fd) 1082 | { 1083 | /* generic sampler */ 1084 | /* number of lines read so far */ 1085 | size_t nfln = 0U; 1086 | /* fill of BUF, also used as offset of end-of-header in HDR mode */ 1087 | size_t nbuf = 0U; 1088 | /* index into BUF to the beginning of the last unprocessed line */ 1089 | size_t ibuf = 0U; 1090 | /* skip up to this line */ 1091 | size_t gap = 0U; 1092 | /* nfixed buffer oversampling */ 1093 | size_t nfxd = 0U; 1094 | /* number of octets read per read() */ 1095 | ssize_t nrd; 1096 | /* reservoir lines 1097 | * we store offsets into RSV buffer in LRSV, which is 3 times the 1098 | * size of NFIXED to concentrate the memmove()ing */ 1099 | size_t _lrsv[stklmt / 2U]; 1100 | size_t *lrsv = _lrsv; 1101 | const size_t mult = 4U; 1102 | /* major states */ 1103 | enum { 1104 | EVAL, 1105 | HEAD, 1106 | BEEF, 1107 | FILL, 1108 | BEXP, 1109 | } state = EVAL; 1110 | 1111 | with (char *tmp = realloc(buf, BUFSIZ)) { 1112 | if (UNLIKELY(tmp == NULL)) { 1113 | /* just bugger off */ 1114 | return -1; 1115 | } 1116 | /* otherwise swap ptrs */ 1117 | buf = tmp; 1118 | zbuf = BUFSIZ; 1119 | } 1120 | with (char *tmp = realloc(rsv, BUFSIZ)) { 1121 | if (UNLIKELY(tmp == NULL)) { 1122 | /* just bugger off */ 1123 | return -1; 1124 | } 1125 | /* otherwise swap ptrs */ 1126 | rsv = tmp; 1127 | zrsv = BUFSIZ; 1128 | } 1129 | if (mult * nfixed >= countof(_lrsv)) { 1130 | /* do her with heap space */ 1131 | lrsv = malloc((mult * nfixed + 1U) * sizeof(*lrsv)); 1132 | } 1133 | 1134 | /* deal with header */ 1135 | while ((nrd = read(fd, buf + nbuf, zbuf - nbuf)) > 0) { 1136 | /* calc next round's NBUF already */ 1137 | nbuf += nrd; 1138 | 1139 | switch (state) { 1140 | case EVAL: 1141 | if (!nheader) { 1142 | goto fill; 1143 | } 1144 | /* otherwise let HEAD state decide */ 1145 | state = HEAD; 1146 | case HEAD: 1147 | for (const char *x; 1148 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf));) { 1149 | const size_t o = ibuf; 1150 | 1151 | ibuf = ++x - buf; 1152 | fwrite(buf + o, sizeof(*buf), ibuf - o, stdout); 1153 | 1154 | if (++nfln >= nheader) { 1155 | if (UNLIKELY(!nfixed)) { 1156 | /* that's it */ 1157 | return 0; 1158 | } 1159 | /* otherwise the most generic mode */ 1160 | goto fill; 1161 | } 1162 | } 1163 | goto wrap; 1164 | 1165 | wrap: 1166 | if (LIKELY(nbuf < zbuf / 2U)) { 1167 | /* just read some more */ 1168 | break; 1169 | } else if (UNLIKELY(!ibuf)) { 1170 | /* great, try a resize */ 1171 | const size_t nuz = zbuf * 2U; 1172 | char *tmp = realloc(buf, nuz); 1173 | 1174 | if (UNLIKELY(tmp == NULL)) { 1175 | return -1; 1176 | } 1177 | /* otherwise assign and retry */ 1178 | buf = tmp; 1179 | zbuf = nuz; 1180 | } else if (LIKELY(ibuf < nbuf)) { 1181 | memmove(buf, buf + ibuf, nbuf - ibuf); 1182 | nbuf -= ibuf; 1183 | ibuf = 0U; 1184 | } 1185 | break; 1186 | 1187 | fill: 1188 | state = FILL; 1189 | case FILL: 1190 | for (const char *x; 1191 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf));) { 1192 | /* keep track of lines */ 1193 | lrsv[nfln - nheader] = ibuf; 1194 | nfln++; 1195 | ibuf = ++x - buf; 1196 | 1197 | if (nfln - nheader >= nfixed) { 1198 | goto beef; 1199 | } 1200 | } 1201 | goto over; 1202 | 1203 | beef: 1204 | state = BEEF; 1205 | /* take on the reservoir */ 1206 | MEMZCPY(rsv, 0U, zrsv, buf + lrsv[0U], ibuf - lrsv[0U]); 1207 | lrsv[nfixed] = ibuf; 1208 | for (size_t i = nfixed + 1U; i > 0; i--) { 1209 | lrsv[i - 1U] -= lrsv[0U]; 1210 | } 1211 | 1212 | nfxd = nfixed; 1213 | 1214 | /* we need one more sample step because the 1215 | * condition above that got us here goes one 1216 | * step further than it should, lest we print 1217 | * the ellipsis when there's exactly 1218 | * nheader + nfoooter lines in the buffer */ 1219 | case BEEF: 1220 | for (const char *x; 1221 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 1222 | ibuf = x - buf + 1U, nfln++) { 1223 | /* current line length */ 1224 | const size_t y = x - buf + 1U - ibuf; 1225 | 1226 | if (nfln - nheader >= 4U * nfixed) { 1227 | /* switch to gap sampling */ 1228 | goto bexp; 1229 | } 1230 | 1231 | /* keep with probability nfixed / nfln */ 1232 | if (runifu32b(nfln - nheader) >= nfixed) { 1233 | continue; 1234 | } 1235 | 1236 | if (UNLIKELY(nfxd >= mult * nfixed)) { 1237 | /* condense lrsv */ 1238 | compactify(lrsv, nfxd, nfixed); 1239 | nfxd = nfixed; 1240 | } 1241 | 1242 | /* bang this line */ 1243 | MEMZCPY(rsv, lrsv[nfxd], zrsv, buf + ibuf, y); 1244 | /* and memorise him */ 1245 | lrsv[nfxd + 1U] = lrsv[nfxd] + y; 1246 | nfxd++; 1247 | } 1248 | goto over; 1249 | 1250 | bexp: 1251 | gap = nfln - nheader + 1252 | rexp32(nfln - nheader - nfixed, nfln); 1253 | state = BEXP; 1254 | case BEXP: 1255 | for (const char *x; 1256 | nfln - nheader < gap && 1257 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 1258 | ibuf = x - buf + 1U, nfln++); 1259 | for (const char *x; 1260 | nfln - nheader >= gap && 1261 | (x = memchr(buf + ibuf, '\n', nbuf - ibuf)); 1262 | ) { 1263 | /* current line length */ 1264 | const size_t y = x - buf + 1U - ibuf; 1265 | 1266 | if (UNLIKELY(nfxd >= mult * nfixed)) { 1267 | /* condense lrsv */ 1268 | compactify(lrsv, nfxd, nfixed); 1269 | nfxd = nfixed; 1270 | } 1271 | 1272 | /* bang this line */ 1273 | MEMZCPY(rsv, lrsv[nfxd], zrsv, buf + ibuf, y); 1274 | /* and memorise him */ 1275 | lrsv[nfxd + 1U] = lrsv[nfxd] + y; 1276 | nfxd++; 1277 | 1278 | ibuf = x - buf + 1U; 1279 | nfln++; 1280 | goto bexp; 1281 | } 1282 | goto over; 1283 | 1284 | over: 1285 | /* beef buffer overrun */ 1286 | if (LIKELY(nbuf < zbuf / 2U)) { 1287 | /* we'll risk reading some more */ 1288 | break; 1289 | } else if (UNLIKELY(!ibuf || nfln - nheader <= nfixed)) { 1290 | /* resize and retry */ 1291 | const size_t nuz = zbuf * 2U; 1292 | char *tmp = realloc(buf, nuz); 1293 | 1294 | if (UNLIKELY(tmp == NULL)) { 1295 | return -1; 1296 | } 1297 | /* otherwise assign and retry */ 1298 | buf = tmp; 1299 | zbuf = nuz; 1300 | break; 1301 | } 1302 | memmove(buf, buf + ibuf, nbuf - ibuf); 1303 | nbuf -= ibuf; 1304 | ibuf -= ibuf; 1305 | break; 1306 | } 1307 | } 1308 | if (nfln > nheader + nfixed) { 1309 | /* compactify to obtain the final result */ 1310 | compactify(lrsv, nfxd, nfixed); 1311 | 1312 | if (!quietp) { 1313 | fwrite("...\n", 1, 4U, stdout); 1314 | } 1315 | fwrite(rsv + lrsv[0U], sizeof(*rsv), 1316 | lrsv[nfixed] - lrsv[0U], stdout); 1317 | if (!quietp) { 1318 | fwrite("...\n", 1, 4U, stdout); 1319 | } 1320 | } else if (nfln == nheader + nfixed) { 1321 | /* we ran 0 steps through beef */ 1322 | const size_t z = lrsv[nfixed]; 1323 | 1324 | fwrite(rsv + lrsv[0U], sizeof(*rsv), z - lrsv[0U], stdout); 1325 | } else if (ibuf > lrsv[0U]) { 1326 | fwrite(buf + lrsv[0U], sizeof(*buf), ibuf - lrsv[0U], stdout); 1327 | } 1328 | if (lrsv != _lrsv) { 1329 | free(lrsv); 1330 | } 1331 | return 0; 1332 | } 1333 | 1334 | static int 1335 | sample(const char *fn) 1336 | { 1337 | int(*sample)(int) = sample_gen; 1338 | struct stat st; 1339 | int rc = 0; 1340 | int fd; 1341 | 1342 | if (nfixed) { 1343 | switch (nfooter) { 1344 | case 0U: 1345 | sample = sample_rsv_0f; 1346 | break; 1347 | case 1U: 1348 | sample = sample_rsv_1f; 1349 | break; 1350 | default: 1351 | sample = sample_rsv; 1352 | break; 1353 | } 1354 | } else if (!rate && !nfooter && !nheader) { 1355 | sample = sample_0; 1356 | } 1357 | 1358 | if (fn == NULL || fn[0U] == '-' && fn[1U] == '\0') { 1359 | /* stdin ... *sigh* */ 1360 | fd = STDIN_FILENO; 1361 | return sample(fd); 1362 | } else if (UNLIKELY((fd = open(fn, O_RDONLY)) < 0)) { 1363 | error("\ 1364 | Error: cannot open file `%s'", fn); 1365 | return -1; 1366 | } else if (UNLIKELY(fstat(fd, &st) < 0)) { 1367 | error("\ 1368 | Error: cannot stat file `%s'", fn); 1369 | rc = -1; 1370 | } else if (!S_ISREG(st.st_mode)) { 1371 | /* fgetln/getline */ 1372 | rc = sample(fd); 1373 | } else { 1374 | rc = sample(fd); 1375 | } 1376 | 1377 | close(fd); 1378 | return rc; 1379 | } 1380 | 1381 | 1382 | #include "sample.yucc" 1383 | 1384 | int 1385 | main(int argc, char *argv[]) 1386 | { 1387 | yuck_t argi[1U]; 1388 | int rc = 0; 1389 | 1390 | if (yuck_parse(argi, argc, argv)) { 1391 | rc = 1; 1392 | goto out; 1393 | } 1394 | 1395 | if (argi->girdle_arg) { 1396 | nheader = nfooter = strtoul(argi->girdle_arg, NULL, 0); 1397 | } 1398 | if (argi->header_arg) { 1399 | nheader = strtoul(argi->header_arg, NULL, 0); 1400 | } 1401 | if (argi->footer_arg) { 1402 | nfooter = strtoul(argi->footer_arg, NULL, 0); 1403 | } 1404 | /* capture -q|--quiet */ 1405 | quietp = argi->quiet_flag; 1406 | 1407 | /* treat ttys specially */ 1408 | if (isatty(STDOUT_FILENO) && !argi->rate_arg) { 1409 | #if defined TIOCGWINSZ 1410 | with (struct winsize ws) { 1411 | if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) { 1412 | /* proceed as usual */ 1413 | break; 1414 | } else if (nheader + nfooter + 5 >= ws.ws_row) { 1415 | /* not enough room */ 1416 | break; 1417 | } 1418 | /* otherwise switch to -N mode, 1419 | * leave some room for ellipsis, prompt and stuff */ 1420 | nfixed = ws.ws_row - (nheader + nfooter + 5); 1421 | } 1422 | #endif /* TIOCGWINSZ */ 1423 | } 1424 | 1425 | if (argi->rate_arg) { 1426 | char *on; 1427 | double x = strtod(argi->rate_arg, &on); 1428 | 1429 | if (x < 0.) { 1430 | errno = 0, error("\ 1431 | Error: sample rate must be non-negative"); 1432 | rc = 1; 1433 | goto out; 1434 | } else if (*on == '%' && x > 100.) { 1435 | errno = 0, error("\ 1436 | Error: sample rate in percent must be <=100"); 1437 | rc = 1; 1438 | goto out; 1439 | } else if (*on == '%') { 1440 | x /= 100.; 1441 | } 1442 | 1443 | if (x > 1.) { 1444 | x = 1. / x; 1445 | } 1446 | 1447 | rate = (long long unsigned int)(0x1.p32 * x); 1448 | } 1449 | if (argi->fixed_arg) { 1450 | char *on; 1451 | nfixed = strtoul(argi->fixed_arg, &on, 0); 1452 | if (!nfixed && !*on) { 1453 | /* they want no randomness, just use zero rate */ 1454 | rate = 0U; 1455 | } else if (!nfixed || *on) { 1456 | errno = 0, error("\ 1457 | Error: parameter to --fixed must be positive"); 1458 | rc = 1; 1459 | goto out; 1460 | } 1461 | } 1462 | 1463 | with (uint64_t seed = 0U) { 1464 | if (argi->seed_arg) { 1465 | char *on; 1466 | seed = strtoull(argi->seed_arg, &on, 0); 1467 | if (!seed || *on) { 1468 | errno = 0, error("\ 1469 | Error: seeds must be positive integers"); 1470 | rc = 1; 1471 | goto out; 1472 | } 1473 | } else { 1474 | seed = time(NULL); 1475 | seed <<= 20U; 1476 | seed ^= getpid(); 1477 | } 1478 | /* initialise randomness */ 1479 | pcg32_srandom(seed); 1480 | 1481 | if (argi->dashs_flag) { 1482 | fprintf(stderr, "0x%016llx\n", seed); 1483 | } 1484 | } 1485 | 1486 | /* obtain stack limits */ 1487 | with (struct rlimit lmt) { 1488 | if (getrlimit(RLIMIT_STACK, &lmt) < 0) { 1489 | /* yeah right */ 1490 | break; 1491 | } else if (lmt.rlim_cur == RLIM_INFINITY) { 1492 | /* brilliant, is this make 3.81? */ 1493 | break; 1494 | } 1495 | stklmt = lmt.rlim_cur / sizeof(stklmt) / 2U; 1496 | } 1497 | 1498 | for (size_t i = 0U; i < argi->nargs + !argi->nargs; i++) { 1499 | rc |= sample(argi->args[i]) < 0; 1500 | } 1501 | 1502 | if (buf != NULL) { 1503 | free(buf); 1504 | } 1505 | if (rsv != NULL) { 1506 | free(rsv); 1507 | } 1508 | if (comp != NULL) { 1509 | free(comp); 1510 | } 1511 | if (idir != NULL) { 1512 | free(idir); 1513 | } 1514 | 1515 | out: 1516 | yuck_free(argi); 1517 | return rc; 1518 | } 1519 | 1520 | /* sample.c ends here */ 1521 | -------------------------------------------------------------------------------- /src/sample.yuck: -------------------------------------------------------------------------------- 1 | Usage: sample [FILE] 2 | 3 | Output a sample of FILE, or stdin if omitted. 4 | 5 | -F, --footer=NUM Print NUM lines of the footer, default: 5. 6 | -G, --girdle=NUM Print NUM lines of header and footer. 7 | -H, --header=NUM Print NUM lines of the header, default: 5. 8 | -n, --fixed=NUM Produce a sample of exactly NUM lines. 9 | The sampling rate will be implicit and the 10 | footer option will be ignored. 11 | -r, --rate=X Sample at rate X. Values between 0 and 1, 12 | or if suffixed with %, are taken literally, 13 | values >1 are interpreted as 1 in X, 14 | default: 10%. 15 | -S, --seed=X Seed sample with X, default: random seed. 16 | -s Print the seed used to stderr. 17 | -q, --quiet Do not emit ellipses. 18 | -------------------------------------------------------------------------------- /src/version.c.in: -------------------------------------------------------------------------------- 1 | /* -*- c -*- */ 2 | #include "version.h" 3 | 4 | const char _version_string[] = "YUCK_SCMVER_VERSION"; 5 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDED_version_h_ 2 | #define INCLUDED_version_h_ 3 | 4 | extern const char _version_string[]; 5 | #define package_version _version_string 6 | 7 | #endif /* INCLUDED_version_h_ */ 8 | -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | # Help the Developers and yourself. Just use the C locale and settings 2 | # for the compilation. They can still be overriden by make LANG= 3 | # but that is general a not very good idea 4 | include $(top_builddir)/version.mk 5 | 6 | LANG = C 7 | LC_ALL = C 8 | 9 | AM_CFLAGS = $(EXTRA_CFLAGS) 10 | AM_CPPFLAGS = -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600 -D_BSD_SOURCE 11 | AM_CPPFLAGS += -DTEST 12 | 13 | EXTRA_DIST = $(BUILT_SOURCES) $(TESTS) 14 | TESTS = 15 | TEST_EXTENSIONS = 16 | BUILT_SOURCES = 17 | dt_tests = 18 | bin_tests = 19 | batch_tests = 20 | check_PROGRAMS = 21 | CLEANFILES = $(check_PROGRAMS) 22 | 23 | ## summon our helper 24 | include clitoris.am 25 | AM_CLIT_LOG_FLAGS = -v --builddir $(top_builddir)/src 26 | TESTS_ENVIRONMENT = root=$(top_srcdir) AEIOU_TRDIR=$(top_builddir)/src 27 | 28 | 29 | TESTS += sample_01.clit 30 | TESTS += sample_02.clit 31 | TESTS += sample_03.clit 32 | TESTS += sample_04.clit 33 | TESTS += sample_05.clit 34 | TESTS += sample_06.clit 35 | TESTS += sample_07.clit 36 | TESTS += sample_08.clit 37 | TESTS += sample_09.clit 38 | TESTS += sample_10.clit 39 | TESTS += sample_11.clit 40 | TESTS += sample_12.clit 41 | TESTS += sample_13.clit 42 | TESTS += sample_14.clit 43 | TESTS += sample_15.clit 44 | TESTS += sample_16.clit 45 | TESTS += sample_17.clit 46 | TESTS += sample_18.clit 47 | TESTS += sample_19.clit 48 | TESTS += sample_20.clit 49 | TESTS += sample_21.clit 50 | TESTS += sample_22.clit 51 | TESTS += sample_23.clit 52 | TESTS += sample_24.clit 53 | TESTS += sample_25.clit 54 | TESTS += sample_26.clit 55 | TESTS += sample_27.clit 56 | TESTS += sample_28.clit 57 | 58 | ## Makefile.am ends here 59 | -------------------------------------------------------------------------------- /test/clitoris.am: -------------------------------------------------------------------------------- 1 | ## assumes TEST_EXTENSIONS, BUILT_SOURCES, check_PROGRAMS have been set 2 | TEST_EXTENSIONS += .clit 3 | CLIT_LOG_COMPILER = $(builddir)/clitoris 4 | 5 | ## our friendly helper 6 | check_PROGRAMS += clitoris 7 | clitoris_SOURCES = clitoris.c clitoris.yuck 8 | clitoris_CPPFLAGS = -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600 -D_BSD_SOURCE 9 | clitoris_CPPFLAGS += -D_ALL_SOURCE -D_NETBSD_SOURCE 10 | clitoris_CPPFLAGS += -UHAVE_VERSION_H 11 | clitoris_LDADD = 12 | if HAVE_PTY_H 13 | clitoris_LDADD += -lutil 14 | endif HAVE_PTY_H 15 | BUILT_SOURCES += clitoris.yucc 16 | 17 | ## yuck rule 18 | clitoris.yucc: clitoris.yuck 19 | $(AM_V_GEN) PATH=$(top_builddir)/build-aux:"$${PATH}" \ 20 | yuck$(EXEEXT) gen -o $@ $< 21 | -------------------------------------------------------------------------------- /test/clitoris.yuck: -------------------------------------------------------------------------------- 1 | Usage: clitoris [OPTION]... TEST_FILE 2 | 3 | Run .clit regression test files. 4 | 5 | --builddir=DIR Specify where the compiled binaries can be found. 6 | --srcdir=DIR Specify where the source tree resides. 7 | --shell=CMD Use CMD as shell command, default: /bin/sh 8 | 9 | -v, --verbose Repeat execution steps on stderr. 10 | -t, --pseudo-tty Allocate a pseudo-tty for the tests to run in. 11 | 12 | --timeout=SECS Time out after SECS seconds. 13 | 14 | -k, --keep-going Don't break on the first failure or differing output 15 | 16 | --diff=CMD Use specified diff command instead of the one in PATH. 17 | -x, --exit-code[=N] Map any non-0 exit code from the commands to N, 18 | (default: 1), or, if N is omitted, pass the exit 19 | code through as is. 20 | -------------------------------------------------------------------------------- /test/sample_01.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 3 | sample 4 | 1 5 | 2 6 | 3 7 | $ 8 | -------------------------------------------------------------------------------- /test/sample_02.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 5 | sample 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | $ 10 | -------------------------------------------------------------------------------- /test/sample_03.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 10 | sample 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | $ 15 | -------------------------------------------------------------------------------- /test/sample_04.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 11 | sample -S 0x11223344 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | ... 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | $ 16 | -------------------------------------------------------------------------------- /test/sample_05.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 3 | sample -r 0 4 | 1 5 | 2 6 | 3 7 | $ 8 | -------------------------------------------------------------------------------- /test/sample_06.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 5 | sample -r 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | $ 10 | -------------------------------------------------------------------------------- /test/sample_07.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 10 | sample -r 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | $ 15 | -------------------------------------------------------------------------------- /test/sample_08.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 11 | sample -r 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | ... 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | $ 16 | -------------------------------------------------------------------------------- /test/sample_09.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 50 | sample -S 0x11223344 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | ... 10 | 30 11 | 39 12 | 40 13 | 45 14 | ... 15 | 46 16 | 47 17 | 48 18 | 49 19 | 50 20 | $ 21 | -------------------------------------------------------------------------------- /test/sample_10.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 50 | sample -r 0.1 -S 0x11223344 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | ... 10 | 30 11 | 39 12 | 40 13 | 45 14 | ... 15 | 46 16 | 47 17 | 48 18 | 49 19 | 50 20 | $ 21 | -------------------------------------------------------------------------------- /test/sample_11.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 20 | sample -r 1 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 13 17 | 14 18 | 15 19 | 16 20 | 17 21 | 18 22 | 19 23 | 20 24 | $ 25 | -------------------------------------------------------------------------------- /test/sample_12.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 20 | sample -r 1 -G 10 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 13 17 | 14 18 | 15 19 | 16 20 | 17 21 | 18 22 | 19 23 | 20 24 | $ 25 | -------------------------------------------------------------------------------- /test/sample_13.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 20 | sample -r 1 -G 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 13 17 | 14 18 | 15 19 | 16 20 | 17 21 | 18 22 | 19 23 | 20 24 | $ 25 | -------------------------------------------------------------------------------- /test/sample_14.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 20 | sample -r 0 -H 5 -F 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | $ 10 | -------------------------------------------------------------------------------- /test/sample_15.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 20 | sample -r 50% -S 0x11223344 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | ... 10 | 6 11 | 11 12 | 13 13 | 15 14 | ... 15 | 16 16 | 17 17 | 18 18 | 19 19 | 20 20 | $ 21 | -------------------------------------------------------------------------------- /test/sample_16.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 20 | sample -r 0.5 -G 0 -S 0x11223344 4 | ... 5 | 1 6 | 6 7 | 8 8 | 10 9 | 13 10 | 14 11 | 15 12 | ... 13 | $ 14 | -------------------------------------------------------------------------------- /test/sample_17.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 40 | sample -F 0 -n 10 -S 0x11223344 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | ... 10 | 7 11 | 11 12 | 16 13 | 18 14 | 30 15 | 31 16 | 32 17 | 33 18 | 36 19 | 38 20 | ... 21 | $ 22 | -------------------------------------------------------------------------------- /test/sample_18.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | ## should trigger the gap algo 4 | $ seq 1 100 | sample -G 0 -n 4 -S 0x1 5 | ... 6 | 26 7 | 42 8 | 52 9 | 70 10 | ... 11 | $ 12 | -------------------------------------------------------------------------------- /test/sample_19.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 40 | sample -F 5 -n 10 -S 0x11223344 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | ... 10 | 7 11 | 16 12 | 17 13 | 19 14 | 21 15 | 23 16 | 24 17 | 25 18 | 33 19 | 35 20 | ... 21 | 36 22 | 37 23 | 38 24 | 39 25 | 40 26 | $ 27 | -------------------------------------------------------------------------------- /test/sample_20.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | ## should trigger the gap algo 4 | $ seq 1 100 | sample -H 0 -n 4 -S 0x3 5 | ... 6 | 9 7 | 39 8 | 47 9 | 76 10 | ... 11 | 96 12 | 97 13 | 98 14 | 99 15 | 100 16 | $ 17 | -------------------------------------------------------------------------------- /test/sample_21.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 9 | sample -n 10 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | $ 14 | -------------------------------------------------------------------------------- /test/sample_22.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 10 | sample -n 10 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | $ 15 | -------------------------------------------------------------------------------- /test/sample_23.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 19 | sample -n 10 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 13 17 | 14 18 | 15 19 | 16 20 | 17 21 | 18 22 | 19 23 | $ 24 | -------------------------------------------------------------------------------- /test/sample_24.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 20 | sample -n 10 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 13 17 | 14 18 | 15 19 | 16 20 | 17 21 | 18 22 | 19 23 | 20 24 | $ 25 | -------------------------------------------------------------------------------- /test/sample_25.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 9 | sample -G 0 -n 10 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | $ 14 | -------------------------------------------------------------------------------- /test/sample_26.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 10 | sample -G 0 -n 10 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | $ 15 | -------------------------------------------------------------------------------- /test/sample_27.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 19 | sample -H 10 -F 0 -n 10 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 13 17 | 14 18 | 15 19 | 16 20 | 17 21 | 18 22 | 19 23 | $ 24 | -------------------------------------------------------------------------------- /test/sample_28.clit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/clitoris 2 | 3 | $ seq 1 20 | sample -H 10 -F 0 -n 10 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 13 17 | 14 18 | 15 19 | 16 20 | 17 21 | 18 22 | 19 23 | 20 24 | $ 25 | -------------------------------------------------------------------------------- /version.mk.in: -------------------------------------------------------------------------------- 1 | VERSION = YUCK_SCMVER_VERSION 2 | PACKAGE_VERSION = $(VERSION) 3 | PACKAGE_STRING = $(PACKAGE) $(VERSION) 4 | --------------------------------------------------------------------------------