├── .github └── workflows │ └── github-actions-demo.yml ├── Makefile ├── Makefile.inc ├── README ├── a_getopt ├── Makefile ├── alt_getopt ├── alt_getopt.pod └── alt_getopt.sh ├── doc ├── INSTALL ├── LICENSE ├── Makefile ├── NEWS └── TODO ├── examples ├── Makefile ├── demo_alt_getopt ├── demo_alt_getopt.sh ├── demo_alt_getopt2.sh ├── demo_alt_join ├── demo_backslash_in ├── demo_backslash_in.in ├── demo_basename ├── demo_dirname ├── demo_embed_str ├── demo_fieldwidths ├── demo_fieldwidths.in ├── demo_ftrans ├── demo_glob ├── demo_glob2ere ├── demo_has_prefix ├── demo_has_prefix.in ├── demo_has_suffix ├── demo_has_suffix.in ├── demo_heapsort ├── demo_heapsort.in ├── demo_heapsort2 ├── demo_heapsort3 ├── demo_heapsort3.in ├── demo_heapsort4 ├── demo_heapsort4.in ├── demo_ini ├── demo_ini.in ├── demo_io ├── demo_minmax ├── demo_modinfo ├── demo_multisub ├── demo_power_getopt ├── demo_power_getopt2 ├── demo_quicksort ├── demo_quicksort2 ├── demo_quicksort3 ├── demo_readfile ├── demo_readfile.in ├── demo_runcmd ├── demo_shquote ├── demo_shquote.in ├── demo_splitre ├── demo_str2regexp ├── demo_tmpfile ├── demo_tokenre ├── demo_tokenre.in ├── demo_tokenre2 ├── demo_tokenre2.in ├── demo_tokenre3 ├── demo_tokenre3.in ├── demo_tokenre4 ├── demo_trim ├── demo_trim.in └── demo_trim_in ├── help.mk ├── modules ├── CR_in.awk ├── Makefile ├── abort.awk ├── abs.awk ├── alt_assert.awk ├── alt_getopt.awk ├── alt_join.awk ├── backslash_in.awk ├── basename.awk ├── braceexpand.awk ├── dirname.awk ├── embed_str.awk ├── exitnow.awk ├── fieldwidths.awk ├── ftrans_in.awk ├── gawk │ └── ord.awk ├── glob.awk ├── has_prefix.awk ├── has_suffix.awk ├── heapsort.awk ├── ini.awk ├── init_getopt.awk ├── io.awk ├── isnum.awk ├── match_br.awk ├── max.awk ├── min.awk ├── modinfo.awk ├── multisub.awk ├── pod_footer.txt ├── pod_header.txt ├── pow.awk ├── power_getopt.awk ├── quicksort.awk ├── readfile.awk ├── runcmd.awk ├── shquote.awk ├── sort.awk ├── str2regexp.awk ├── tmpfile.awk ├── tokenre.awk ├── trim.awk ├── trim_in.awk ├── xclose.awk ├── xgetline.awk └── xsystem.awk ├── runawk ├── Makefile ├── common.h ├── dynarray.c ├── dynarray.h ├── file_hier.c ├── file_hier.h ├── runawk.c └── runawk.pod └── test ├── Makefile ├── mods1 ├── module1.1.awk ├── module1.2.awk ├── module1.3.awk └── test_modinfo ├── mods2 ├── module2.1.awk ├── module2.2.awk └── module2.3.awk ├── mods3 ├── failed1.awk ├── failed2.awk ├── failed3.awk ├── failed4.awk └── test5.awk ├── test.out └── test.sh /.github/workflows/github-actions-demo.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions Demo 2 | run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 3 | on: [push] 4 | jobs: 5 | Explore-GitHub-Actions: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." 9 | - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" 10 | - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." 11 | - name: Check out repository code 12 | uses: actions/checkout@v3 13 | - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." 14 | - run: echo "🖥️ The workflow is now ready to test your code on the runner." 15 | - name: List files in the repository 16 | run: | 17 | ls ${{ github.workspace }} 18 | - run: echo "🍏 This job's status is ${{ job.status }}." 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ################################################## 2 | 3 | SUBPRJ = runawk:test modules examples a_getopt doc 4 | SUBPRJ_DFLT ?= runawk modules 5 | 6 | WITH_ALT_GETOPT ?= yes 7 | 8 | .if ${WITH_ALT_GETOPT:tl} == "yes" 9 | SUBPRJ_DFLT += a_getopt 10 | .endif 11 | 12 | MKC_REQD = 0.26.0 13 | 14 | ################################################## 15 | 16 | # "cleandir" also removes temporary files of regression tests 17 | cleandir: cleandir-test 18 | 19 | # Avoid running test-runawk, test-modules etc. 20 | test: all-test 21 | @: 22 | 23 | # Also implement test_all 24 | test_all: test_all-test 25 | 26 | # We use "target "manpages" for making a distribution tarball 27 | TARGETS += _manpages # _manpages is a new recursive target 28 | DIST_TARGETS = manpages 29 | manpages: _manpages 30 | rm ${MKC_CACHEDIR}/_mkc* 31 | 32 | .include "help.mk" 33 | .include "Makefile.inc" 34 | .include 35 | -------------------------------------------------------------------------------- /Makefile.inc: -------------------------------------------------------------------------------- 1 | VERSION = 1.6.2 2 | 3 | BIRTHDATE = 2007-09-24 4 | 5 | EGDIR ?= ${DATADIR}/doc/runawk/examples 6 | MODULESDIR ?= ${DATADIR}/runawk 7 | DOCDIR ?= ${DATADIR}/doc/runawk 8 | 9 | TARGETS += test_all 10 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ====================================================================== 2 | 3 | runawk: small wrapper for AWK interpreter that impements modules 4 | system and helps one to write the standalone AWK programs. 5 | 6 | author: Aleksey Cheusov 7 | 8 | project's home page: http://sourceforge.net/projects/runawk 9 | 10 | licence: MIT license 11 | 12 | ====================================================================== 13 | 14 | 15 | 16 | MOTIVATION 17 | 18 | See section MOTIVATION of runawk.1 or runawk.pod. 19 | 20 | ====================================================================== 21 | 22 | INSTALLATION 23 | 24 | See INSTALL file. 25 | 26 | ====================================================================== 27 | 28 | MODULES 29 | 30 | RunAWK provides lots of AWK modules ready for use. All of them are 31 | under modules/ subdirectory. They are installed by default. 32 | 33 | abort.awk, alt_assert.awk 34 | - power abort() and assert() 35 | alt_join.awk - join :-) 36 | braceexpand.awk - awk equivalent to shell's braceexpand functionality 37 | basename.awk, dirname.awk 38 | - equivalents to basename(1) and dirname(1) 39 | embed_str.awk - reads strings from AWK code 40 | exitnow.awk - 41 | has_prefix.awk, has_suffix.awk 42 | - trivial string functions 43 | match_br.awk - find pair closed brackets in a string 44 | max.awk, min.awk, isnum.awk, abs.awk 45 | - trivial math functions 46 | modinfo.awk - routines to get an information about modules that 47 | are in use 48 | multisub.awk - another replacement functions 49 | pow.awk - synonym for ^ 50 | init_getopt.awk, alt_getopt.awk, power_getopt.awk 51 | - POSIX/SUS compatible routines 52 | for handling options EASILY ;-) 53 | readfile.awk - read an entire file into variable 54 | runcmd.awk - safe wrapper over system() 55 | shquote.awk - escapes shell's special characters 56 | heapsort.awk, quicksort.awk, sort.awk 57 | - sorting functions 58 | str2regexp.awk - string to regexp routines 59 | tokenre.awk - alternative mechanism for separating input into tokens 60 | xclose.awk, xgetline.awk, xsystem.awk 61 | - functions to write robust programs 62 | tmpfile.awk - generates names of automatically deleted tempfiles 63 | fieldwidths.awk - portable equivalent of GNU awk's FIELDWIDTHS ;-) 64 | CR_in.awk - removes CR symbols from input lines. 65 | trim.awk - functions for trimming spaces from the string. 66 | trim_in.awk - trims spaces from input lines. 67 | backslash_in.awk - treats backslash symbol at the end of line as 68 | "this line will continue on the next one". 69 | ini.awk - functions for reading Win .ini files. 70 | ftrans_in.awk - handle data file transitions 71 | ord.awk - character to id and id to character convertors 72 | 73 | ====================================================================== 74 | 75 | SCRIPTS 76 | 77 | RunAWK also provides a few scripts: 78 | 79 | alt_getopt - program arguments parser aware of long options, 80 | alternative to POSIX getopts and getopt(1). 81 | In addition to alt_getopt(1) utility alt_getopt.sh 82 | shell library is also provided that provides 83 | an alternative way for handling options. 84 | alt_getopt.sh - shell library functionally equivalent 85 | to alt_getopt(1) 86 | 87 | ====================================================================== 88 | 89 | EXAMPLES 90 | 91 | Lots of examples are available in examples/ subdirectory. 92 | -------------------------------------------------------------------------------- /a_getopt/Makefile: -------------------------------------------------------------------------------- 1 | SCRIPTS += alt_getopt 2 | 3 | MAN += alt_getopt.1 4 | 5 | FILES += alt_getopt.sh 6 | 7 | CLEANFILES = alt_getopt.1 8 | 9 | .PHONY: _manpages 10 | _manpages: ${MAN} 11 | 12 | .include 13 | -------------------------------------------------------------------------------- /a_getopt/alt_getopt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | # Copyright (c) 2010 Aleksey Cheusov 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining 6 | # a copy of this software and associated documentation files (the 7 | # "Software"), to deal in the Software without restriction, including 8 | # without limitation the rights to use, copy, modify, merge, publish, 9 | # distribute, sublicense, and/or sell copies of the Software, and to 10 | # permit persons to whom the Software is furnished to do so, subject to 11 | # the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be 14 | # included in all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | #use "alt_assert.awk" 25 | #use "alt_getopt.awk" 26 | #use "shquote.awk" 27 | #use "embed_str.awk" 28 | 29 | #env "LC_ALL=C" 30 | 31 | #.begin-str help 32 | # alt_getopt is a command options parser, it gets options 33 | # specification and application's options as arguments and outputs 34 | # shell commands to be executed. 35 | # Usage: 36 | # alt_getopt -h 37 | # alt_getopt [OPTIONS] opt1 sh_cmd1 [opt2 sh_cmd2...] -- [args] 38 | # OPTIONS: 39 | # -h|--help display this screen 40 | # -c do not output 'shift' command, variable 41 | # __shifts_cnt is assigned instead 42 | #.end-str 43 | 44 | function __takes_arg (opt){ 45 | if (!opt in __getopt_opts) 46 | return 0 47 | if (__getopt_opts [opt] == takes_arg) 48 | return 1 49 | if (__getopt_opts [opt] == "") 50 | return 0 51 | return __getopt_opts [__getopt_opts [opt]] == takes_arg 52 | } 53 | 54 | BEGIN { 55 | start = 1 56 | if (ARGV [1] == "-h" || ARGV [1] == "--help"){ 57 | print EMBED_STR ["help"] > "/dev/stderr" 58 | exitnow(0) 59 | } 60 | if (ARGV [1] == "-c"){ 61 | delete ARGV [1] 62 | ++start 63 | output_shift_cnt = 1 64 | } 65 | 66 | for (i=start; i < ARGC; i += 2){ 67 | if (ARGV [i] == "--"){ 68 | delete ARGV [i] 69 | ++i 70 | break 71 | } 72 | 73 | if (ARGV [i] == "" || ARGV [i+1] == ""){ 74 | print "bad arguments, see alt_getopt -h for more information" \ 75 | > "/dev/stderr" 76 | } 77 | 78 | if (ARGV [i] ~ /^[A-Za-z0-9!@%^=+\/,.]$/){ 79 | shopts = shopts ARGV [i] 80 | action [ARGV [i]] = ARGV [i+1] 81 | }else if (ARGV [i] ~ /^=[A-Za-z0-9!@%^=+\/,.]$/){ 82 | opt = substr(ARGV [i], 2) 83 | shopts = shopts opt ":" 84 | action [opt] = ARGV [i+1] 85 | }else if (ARGV [i] ~ /^[A-Za-z0-9!@%^=+\/,.][ |][^ |]+$/){ 86 | split(ARGV [i], arr, /[ |]/) 87 | shopts = shopts arr [1] 88 | long_opts [arr [2]] = arr [1] 89 | action [arr [1]] = ARGV [i+1] 90 | }else if (ARGV [i] ~ /^=[A-Za-z0-9!@%^=+\/,.][ |][^ |]+$/){ 91 | split(substr(ARGV [i], 2), arr, /[ |]/) 92 | shopts = shopts arr [1] ":" 93 | long_opts [arr [2]] = arr [1] 94 | action [arr [1]] = ARGV [i+1] 95 | }else if (ARGV [i] ~ /^[^=][^ |]*$/){ 96 | long_opts [ARGV [i]] = "" 97 | action [ARGV [i]] = ARGV [i+1] 98 | }else if (ARGV [i] ~ /^=[^ |]+$/){ 99 | opt = substr(ARGV [i], 2) 100 | long_opts [opt] = takes_arg 101 | action [opt] = ARGV [i+1] 102 | }else{ 103 | abort("unknown format of'" ARGV [i] "'") 104 | } 105 | 106 | delete ARGV [i] 107 | delete ARGV [i+1] 108 | } 109 | 110 | first = i 111 | 112 | while (getopt(shopts, 1)){ 113 | if (__takes_arg(optopt)) 114 | print action [optopt] shquote(optarg) 115 | else 116 | print action [optopt] 117 | } 118 | 119 | shifts_cnt = 0 120 | for (i=first; i < ARGC; ++i){ 121 | if (ARGV [i] == "") 122 | ++shifts_cnt 123 | } 124 | if (output_shift_cnt) 125 | print "shifts=" shifts_cnt 126 | else 127 | print "shift", shifts_cnt 128 | 129 | exitnow(0) 130 | } 131 | -------------------------------------------------------------------------------- /a_getopt/alt_getopt.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | alt_getopt - parse command options 4 | 5 | =head1 SYNOPSIS 6 | 7 | cmds=`alt_getopt opt1 action1 [opt2 action2...] -- "$@"` 8 | eval "$cmds" 9 | 10 | =head1 DESCRIPTION 11 | 12 | B is a command options parser. It conforms to POSIX 13 | and supports long options, that is works the same way as 14 | getopt_long(3) found in *BSD and GNU libc. 15 | 16 | =head1 EXAMPLES 17 | 18 | The following shell script accepts the options: -v (or 19 | --verbose), -h (or --help), long option --fake without short synonym, 20 | --len accepting argument, -o (or --output) accepting argument, 21 | short options -f and -F without long synonyms etc. If option is 22 | applied, appropriate shell command is executed. 23 | 24 | #!/bin/sh 25 | help () { echo 'help here'; } 26 | 27 | process_args (){ 28 | alt_getopt \ 29 | 'v|verbose' 'verbose=1' \ 30 | 'h help' help \ 31 | 'fake' fake_flag=1 \ 32 | '=len' len= \ 33 | '=o output' output= \ 34 | '=m msg' "msg=" \ 35 | 'V version' "echo 'alt_getopt-0-1-0'" \ 36 | f 'flag=1' \ 37 | F 'flag=' \ 38 | =n number= \ 39 | -- "$@" 40 | } 41 | 42 | cmds=`process_args "$@"` 43 | eval "$cmds" 44 | 45 | echo "flag=$flag" 46 | echo "msg=$msg" 47 | ... 48 | 49 | Another way to handle options using alt_getopt.sh 50 | 51 | #!/bin/sh 52 | . alt_getopt.sh 53 | 54 | help () { 55 | cat 1>&2 < 78 | 79 | =head1 SEE ALSO 80 | L 81 | L 82 | -------------------------------------------------------------------------------- /a_getopt/alt_getopt.sh: -------------------------------------------------------------------------------- 1 | # -*- mode: sh; -*- 2 | 3 | __nl=' 4 | ' 5 | 6 | __shq (){ 7 | __cmd=`printf '%s\n' "$1" | sed "s|'|'\\\\\''|g"` 8 | printf "%s\n" "'$__cmd'" 9 | } 10 | 11 | add_arg (){ 12 | if test $# -ne 2 -a $# -ne 3; then 13 | echo 'add_arg function requires either 2 or three arguments' 1>&2 14 | exit 10 15 | fi 16 | if test $# -eq 3; then 17 | help_msg="${help_msg}$3$__nl" 18 | fi 19 | __i=`__shq "$1"` 20 | __args="$__args $__i" 21 | __i=`__shq "$2"` 22 | __args="$__args $__i" 23 | } 24 | 25 | __run_alt_getopt (){ 26 | eval "alt_getopt $__args" "$@" 27 | } 28 | 29 | process_args (){ 30 | __args="$__args --" 31 | for i in "$@"; do 32 | __i=`__shq "$1"` 33 | __args="$__args $__i" 34 | shift 35 | done 36 | __cmds=`eval alt_getopt -c $__args` 37 | eval "$__cmds" 38 | } 39 | -------------------------------------------------------------------------------- /doc/INSTALL: -------------------------------------------------------------------------------- 1 | ====================================================================== 2 | 3 | INSTALLATION 4 | 5 | 0) 6 | Maybe runawk is already packaged for your platform. See PACKAGES 7 | section below and your system's packages management tools. 8 | 9 | 1) 10 | We need bmake (portable version of NetBSD make) for building runawk. 11 | I'd recommend to use latest stable version of bmake. 12 | 13 | http://crufty.net/help/sjg/bmake.html 14 | http://freshmeat.net/projects/bmake/ 15 | 16 | <<<***!!! GNU make IS NOT GOOD !!!***>>> 17 | 18 | 2) 19 | We also need mk-configure >= 0.21.0. 20 | 21 | http://sourceforge.net/projects/mk-configure/ 22 | 23 | 3) Uncompress tarball you've downloaded like this 24 | gzip -dc runawk-X-Y-Z.tar.gz | tar -xf- 25 | 26 | 2) cd runawk-X.Y.Z 27 | 28 | 3) mkcmake 29 | 30 | 4) mkcmake test 31 | If this step fails on your system, PLEASE LET ME KNOW. 32 | 33 | 5) mkcmake install 34 | 35 | There is a lot of Makefile variables that can be changed during 36 | build and installation. 37 | 38 | MODULESDIR - directory where modules are installed to [${PREFIX}/share/runawk] 39 | AWK_PROG - path to the default AWK interpreter 40 | STDIN_FILENAME - path to stdin device file [-] 41 | 42 | PREFIX - where runawk(1) is installed to 43 | MANDIR - root directory for manual pages 44 | DESTDIR - fake root for installation 45 | CPPFLAGS 46 | CFLAGS 47 | LDFLAGS 48 | LDADD 49 | ... 50 | 51 | See mk-configure(7) for details. 52 | 53 | 6) RunAWK distribution consists of the following subprojects: 54 | - runawk -- runawk executable 55 | - modules -- modules written in .awk 56 | - doc -- README, COPYRIGHT, NEW, TODO files 57 | - examples -- samples of use for runawk and its modules 58 | - a_getopt -- alt_getopt(1) and alt_getopt.sh 59 | 60 | By default only "runawk", "modules" and "a_getopt" are built and installed. 61 | However you can easily build and install any subproject like the following 62 | 63 | mkcmake all-examples install-examples 64 | mkcmake all-doc install-doc 65 | etc. 66 | 67 | See mk-configure(7) and ./Makefile for details. 68 | 69 | You can also set subprojects you need in SUBPRJ_DFLT variable. 70 | 71 | export SUBPRJ_DFLT='runawk modules examples a_getopt doc' 72 | mkcmake all install 73 | 74 | 7) Non-standard variable you may want to change. 75 | EGDIR - directiory where examples/ will be installed to 76 | ($PREFIX/share/doc/runawk/examples) 77 | MODULESDIR - directory where modules/ will be installed to 78 | ($PREFIX/share/runawk) 79 | DOCDIR - directory where doc/ will be installed to 80 | ($PREFIX/share/doc/runawk) 81 | AWK_PROG - path to the default AWK interpreter 82 | (/usr/bin/awk) 83 | STDIN_FILENAME - path to stdin device file 84 | (-) 85 | 86 | ------------------------------ 87 | Examples of building and installing: 88 | 89 | 0) 90 | mkcmake all install 91 | 92 | 1) 93 | export PREFIX=/usr SYSCONFDIR=/etc; \ 94 | mkcmake all install DESTDIR=/tmp/fakeroot 95 | 96 | 2) 97 | export PREFIX=/usr SYSCONFDIR=/etc 98 | export SUBPRJ_DFLT='runawk modules a_getopt examples doc' 99 | export AWK_PROG=/bin/awk 100 | mkcmake all install DESTDIR=/tmp/fakeroot 101 | 102 | 3) 103 | env CC='icc -no-gcc' \ 104 | PREFIX=/home/cheusov/local \ 105 | CPPFLAGS=-I/usr/pkg/include \ 106 | LDFLAGS='-L/usr/pkg/lib -Wl,-rpath -Wl,/usr/pkg/lib' \ 107 | LDADD=-lextralib \ 108 | CFLAGS='-O2 -fomit-frame-pointers' \ 109 | mkcmake -s all install 110 | -------------------------------------------------------------------------------- /doc/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2014 Aleksey Cheusov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | FILES = LICENSE NEWS ../README TODO 2 | FILESDIR = ${DOCDIR} 3 | 4 | .include 5 | -------------------------------------------------------------------------------- /doc/TODO: -------------------------------------------------------------------------------- 1 | plan -1: 2 | - gener{it,ate}/chpp/m1/m5 -- general purpose text preprocessor 3 | 4 | plan 0: 5 | - floor, ceil functions 6 | - glob() - disable memoization 7 | - support X11-like long options, e.g. -display, -geometry etc. 8 | - grep(1) in runawk? (POSIX grep -E with some of GNU grep extensions) 9 | - gawk/hash.awk (using gawk extensions): 10 | remove_by_key(@cond_func) and remove_by_value(@cond_func) 11 | - qstack.awk: 12 | stack_push, stack_pop, stack_top, stack_isempty, stack_clean 13 | queue_push, queue_pop, queue_back, queue_front, queue_isempty, 14 | queue_clean, queue_push_back, queue_pop_front 15 | - inspect.awk: inspect_str, inspect_hash 16 | for displaying invisible characters 17 | 18 | plan 1: 19 | - strtonum from gawk? 20 | - random numbers generator from /dev/[u]random 21 | - tokenre_in.awk: synonym for tokenre.awk 22 | - reference to EGDIR from mk-configure.7 23 | - {string,hash,math}.awk -- accumulative modules 24 | - msub() - cleverer replacement for multisub 25 | - bre2ere.awk Basic RE to Extended RE convertor 26 | - libconfig/libconfuse analog written in/for AWK 27 | - csv.awk -- module for "comma separated values" format described in RFC4180 28 | - hash.awk - functions for working with hashes. 29 | items_count() ? 30 | - support for Windows 31 | - remove fflush() ? 32 | - quicksort has quadratic complexity when sorting equal values 33 | - tr.awk ? 34 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | EXAMPLES != cd ${.CURDIR}; echo demo_* 2 | 3 | FILES = ${EXAMPLES:N*~:M*.in} 4 | SCRIPTS = ${EXAMPLES:N*~:N*.in} 5 | 6 | FILESDIR = ${EGDIR} 7 | SCRIPTSDIR = ${EGDIR} 8 | 9 | .include 10 | -------------------------------------------------------------------------------- /examples/demo_alt_getopt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "alt_getopt.awk" 4 | 5 | BEGIN { 6 | long_opts ["verbose"] = "v" 7 | long_opts ["help"] = "h" 8 | long_opts ["fake"] = "" 9 | long_opts ["len"] = takes_arg 10 | long_opts ["output"] = "o" 11 | 12 | while (getopt("hVvo:n:")){ 13 | if (optopt == "h"){ 14 | print "option `h'" 15 | }else if (optopt == "V"){ 16 | print "option `V'" 17 | }else if (optopt == "v"){ 18 | print "option `v'" 19 | }else if (optopt == "o"){ 20 | print "option `o':", optarg 21 | }else if (optopt == "n"){ 22 | print "option `n':", optarg 23 | }else if (optopt == "fake"){ 24 | print "option `fake'" 25 | }else if (optopt == "len"){ 26 | print "option `len':", optarg 27 | }else{ 28 | abort() 29 | } 30 | fflush() 31 | } 32 | 33 | for (i=1; i < ARGC; ++i){ 34 | if (ARGV [i] != "") 35 | printf "ARGV [%s] = %s\n", i, ARGV [i] 36 | } 37 | 38 | exit 0 39 | } 40 | -------------------------------------------------------------------------------- /examples/demo_alt_getopt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # this is a demo for alt_getopt(1) program, not for alt_getopt.awk module! 4 | 5 | help () { 6 | cat 1>&2 <'" \ 22 | f 'flag=1' \ 23 | F 'flag=' \ 24 | q 'quiet="1"; verbose=0' \ 25 | =n number= \ 26 | -- "$@" # You MUST specify -- here 27 | } 28 | 29 | a=`process_args "$@"` 30 | eval "$a" 31 | 32 | echo "verbose=$verbose" 33 | echo "fake_flag=$fake_flag" 34 | echo "len=$len" 35 | echo "output=$output" 36 | echo "msg=$msg" 37 | echo "n=$number" 38 | echo "flag=$flag" 39 | echo "quiet=$quiet" 40 | 41 | i=1 42 | while test $# -ne 0; do 43 | printf "arg [%d]=%s\n" "$i" "$1" 44 | i=`expr $i + 1` 45 | shift 46 | done 47 | -------------------------------------------------------------------------------- /examples/demo_alt_getopt2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # this is a demo for alt_getopt(1) program and alt_getopt.sh shell module 4 | 5 | . alt_getopt.sh 6 | 7 | help () { 8 | cat 1>&2 < flag with value 14 | # =-LONG-FLAG long flag with value 15 | # =S short flag with value 16 | # -X short flag 17 | # =Y short flag with value 18 | # -P print ARGC and ARGV 19 | #.end-str 20 | 21 | # Very powerful easy for use module for handling options 22 | 23 | BEGIN { 24 | if (getarg("P")){ 25 | print "ARGC=" ARGC 26 | for (i=1; i < ARGC; ++i){ 27 | printf "ARGV [%d]=%s\n", i, ARGV [i] 28 | } 29 | exit (0) 30 | } 31 | 32 | print "The following option were applied" 33 | for (i in options){ 34 | print "options [" i "]=" options [i] 35 | } 36 | print "f --- " getarg("f") 37 | print "flag --- " getarg("flag") 38 | print "long-flag --- " getarg("long-flag") 39 | print "s --- " getarg("s") 40 | print "F --- " getarg("F", "default1") 41 | print "FLAG --- " getarg("FLAG", "default2") 42 | print "LONG-FLAG --- " getarg("LONG-FLAG", "default3") 43 | print "S --- " getarg("S", "default4") 44 | print "X --- " getarg("X") 45 | print "Y --- " getarg("Y", "default5") 46 | 47 | 48 | exit 0 49 | } 50 | -------------------------------------------------------------------------------- /examples/demo_power_getopt2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "power_getopt.awk" 4 | 5 | #.begin-str help 6 | # power_getopt - program demonstrating a power of power_getopt.awk module 7 | # usage: power_getopt [OPTIONS] 8 | # OPTIONS: 9 | # -?|--help display this screen 10 | #.end-str 11 | 12 | BEGIN { 13 | # the only implemented functionality is -?|--help options 14 | exit 0 15 | } 16 | -------------------------------------------------------------------------------- /examples/demo_quicksort: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "quicksort.awk" 4 | 5 | # This demo sorts the input lines as strings and outputs them to stdout 6 | 7 | # Input files for this demo: examples/demo_quicksort.in 8 | 9 | { 10 | array [++count] = $0 11 | } 12 | 13 | END { 14 | quicksort(array, remap, 1, count) 15 | 16 | for (i=1; i <= count; ++i){ 17 | print array [remap [i]] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/demo_quicksort2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "quicksort.awk" 4 | #use "alt_assert.awk" 5 | 6 | # This is a regression test for quicksort.awk module 7 | 8 | BEGIN { 9 | srand() 10 | 11 | cnt = 400 12 | 13 | start = 20 14 | end = start + cnt 15 | 16 | sum = 0 17 | for (i=start; i <= end; ++i){ 18 | arr [i] = int (rand() * 50) 19 | sum += arr [i] 20 | } 21 | 22 | quicksort(arr, remap, start, end) 23 | 24 | end_sum = 0 25 | for (i=start; i <= end; ++i){ 26 | # printf "arr [%s]=%s\n", i, arr [remap [i]] 27 | assert(i == start || arr [remap [i-1]] <= arr [remap [i]], "failed!") 28 | end_sum += arr [remap [i]] 29 | } 30 | assert(sum == end_sum, "sum != end_sum") 31 | 32 | exit 0 33 | } 34 | -------------------------------------------------------------------------------- /examples/demo_quicksort3: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "quicksort.awk" 4 | 5 | # This demo sorts the input lines as (key, value) pair 6 | # and outputs sorted pairs. 7 | 8 | # Input file for this demo: examples/demo_heapsort3.in 9 | 10 | { 11 | val = $2 12 | val += NR * 0.00001 # for regression tests 13 | hash [$1] = val 14 | } 15 | 16 | END { 17 | count = quicksort_values(hash, remap) 18 | 19 | for (i=1; i <= count; ++i){ 20 | print remap [i], hash [remap [i]] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/demo_readfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "readfile.awk" 4 | 5 | # This demo reads an entire file, stores its content in a variable and 6 | # then print it. 7 | 8 | # Input files for this demo: examples/demo_readfile.in* 9 | 10 | BEGIN { 11 | if (ARGC != 2){ 12 | print "usage: demo_readfile " 13 | exit 1 14 | } 15 | 16 | content = readfile(ARGV [1]) 17 | print content 18 | } 19 | -------------------------------------------------------------------------------- /examples/demo_readfile.in: -------------------------------------------------------------------------------- 1 | line1 2 | line2 3 | line3 4 | -------------------------------------------------------------------------------- /examples/demo_runcmd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "runcmd.awk" 4 | 5 | # Demo foir runcmd.awk module 6 | 7 | BEGIN { 8 | fn1 = "file name.txt" 9 | fn2 = "~file&name'foobar'.txt" 10 | 11 | xruncmd1("touch", "", fn1) 12 | xruncmd1("echo", "foobar >", fn2) 13 | 14 | xruncmd1("ls", "-1", fn1) 15 | xruncmd1("ls", "", fn2) 16 | 17 | ret = runcmd1("grep", "baz", fn2) 18 | assert(1 == ret, "runcmd failed, exit status: " ret) 19 | 20 | xruncmd1("rm", "-f", fn1) 21 | xruncmd1("rm", "", fn2) 22 | 23 | print "succeeded" 24 | 25 | exit 0 26 | } 27 | -------------------------------------------------------------------------------- /examples/demo_shquote: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "shquote.awk" 4 | #use "xsystem.awk" 5 | 6 | # This demo runs shquote ($0) for each input line and outputs the result. 7 | 8 | # Input files for this demo: examples/demo_shquote.in 9 | 10 | { 11 | print shquote($0) 12 | xsystem("printf 'printf: %s\n' " shquote($0)) 13 | } 14 | -------------------------------------------------------------------------------- /examples/demo_shquote.in: -------------------------------------------------------------------------------- 1 | ' 2 | '' 3 | ''' 4 | \ 5 | \\ 6 | \\\ 7 | \\' 8 | '\' 9 | 10 | Apple's 11 | apple 12 | file name.txt 13 | *&;<>#~^$`!][(){}? 14 | -------------------------------------------------------------------------------- /examples/demo_splitre: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "tokenre.awk" 4 | 5 | # This demo splits input line into tokens according to regexp that 6 | # defines lexem of hypotetic programming language. 7 | 8 | # Input files for this demo: examples/demo_tokenre2.in* 9 | 10 | BEGIN { 11 | re = "if|then|else|while|do|end" 12 | re = re "|" "[0-9]+([.][0-9]+)?" 13 | re = re "|" ":=|=|<|>|!=|[+]|-|[*]|/|[.][.]" 14 | re = re "|" "[()]" 15 | re = re "|" "[[:alpha:]_][[:alnum:]_]*" 16 | re = re "|" "'[^']*'" 17 | } 18 | 19 | { 20 | cnt = splitre0(arr, re) 21 | for (i=1; i <= cnt; ++i){ 22 | print arr [i] 23 | } 24 | print "" 25 | } 26 | -------------------------------------------------------------------------------- /examples/demo_str2regexp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "str2regexp.awk" 4 | 5 | # This demo converts string ($0) to regexp that matches this string 6 | 7 | # Input files for this demo: examples/demo_str2regexp.in* 8 | 9 | { 10 | print $0 " --> " str2regexp($0) 11 | } 12 | -------------------------------------------------------------------------------- /examples/demo_tmpfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "tmpfile.awk" 4 | #use "exitnow.awk" 5 | 6 | # This demo generates temporary file names and outputs them to stdout 7 | 8 | BEGIN { 9 | tmpfile1 = tmpfile() 10 | tmpfile2 = tmpfile() 11 | tmpsubdir = tmpfile() 12 | 13 | print "file 1" > tmpfile1 14 | 15 | print "file 2" > tmpfile2 16 | 17 | system("mkdir " tmpsubdir) 18 | 19 | tmpfile3 = tmpsubdir "/file3" 20 | print "file 3" > tmpfile3 21 | 22 | close(tmpfile1) 23 | close(tmpfile2) 24 | close(tmpfile3) 25 | 26 | system("cat " tmpfile1 " " tmpfile2 " " tmpfile3) 27 | 28 | exitnow(0) 29 | } 30 | -------------------------------------------------------------------------------- /examples/demo_tokenre: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "tokenre.awk" 4 | 5 | # This demo splits input line into tokens according to regexp that 6 | # defines a token, not between tokens. 7 | # 8 | # Suported tokens: 9 | # - sequence of non-space and not " characters 10 | # - "la-la-la even spaces here " 11 | 12 | # Input files for this demo: examples/demo_tokenre.in* 13 | 14 | BEGIN { 15 | TRE="\"[^\"]*\"|[^\"[:space:]]+" 16 | } 17 | 18 | { 19 | print "NF=" NF 20 | for (i=1; i <= NF; ++i){ 21 | printf "$%d=%s\n", i, $i 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/demo_tokenre.in: -------------------------------------------------------------------------------- 1 | wn: UNIX "UNIX guru" "UNIX operating system" "UNIX system" 2 | jargon: Unix "Unix brain damage" "Unix conspiracy" 3 | "Unix weenie" 4 | foldoc: "berkeley unix" "bsd unix" "call unix" 5 | "united kingdom unix users group" unix "unix box" 6 | "unix brain damage" "unix conspiracy" "unix international" 7 | "unix man page" "unix manual page" "unix system v" 8 | "unix to unix copy" "unix weenie" "unix wizard" "usg unix" 9 | -------------------------------------------------------------------------------- /examples/demo_tokenre2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "tokenre.awk" 4 | 5 | # This demo splits input line into tokens according to regexp that 6 | # defines lexem of hypotetic programming language. 7 | 8 | # Input files for this demo: examples/demo_tokenre2.in* 9 | 10 | BEGIN { 11 | TRE = "if|then|else|while|do|end" 12 | TRE = TRE "|" "[0-9]+([.][0-9]+)?" 13 | TRE = TRE "|" ":=|=|<|>|!=|[+]|-|[*]|/|[.][.]" 14 | TRE = TRE "|" "[()]" 15 | TRE = TRE "|" "[[:alpha:]_][[:alnum:]_]*" 16 | TRE = TRE "|" "'[^']*'" 17 | } 18 | 19 | { 20 | for (i=1; i <= NF; ++i){ 21 | print $i 22 | } 23 | print "" 24 | } 25 | -------------------------------------------------------------------------------- /examples/demo_tokenre2.in: -------------------------------------------------------------------------------- 1 | a := 1 2 | b := 2.3 3 | 4 | while a+b < 10 do 5 | a := a+1 6 | end 7 | 8 | print 'Hello world!' 9 | print 'a = ' .. a 10 | print 'b = ' .. b 11 | -------------------------------------------------------------------------------- /examples/demo_tokenre3: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "tokenre.awk" 4 | 5 | # This demo extracts e-mails and URLs from input text and outputs them 6 | # Using tokenre.awk for searching was inspired by talks with Vlad Shakhov 7 | 8 | # Input files for this demo: examples/demo_tokenre3.in* 9 | 10 | BEGIN { 11 | # regexp for emails and URLs are just examples and therefore don't 12 | # conform to appropriate RFCs. 13 | domains = "(com|org|net|ru|by|ua)" 14 | TRE = "[^ [:punct:]]+@([^ [:punct:]]+[.])+" domains 15 | TRE = TRE "|" "(https?|ftp|dict)://([^ [:punct:]]+[.])+" domains "(/[^ [:punct:]]*)?" 16 | } 17 | 18 | { 19 | for (i=1; i <= NF; ++i){ 20 | print $i 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/demo_tokenre3.in: -------------------------------------------------------------------------------- 1 | RunAWK is hosted here: http://sourceforge.net/projects/runawk/ and 2 | here http://freshmeat.net/projects/runawk/ 3 | 4 | dictd.xdsl.by serves dictionary protocol (RFC2229) server dict://dictd.xdsl.by/ 5 | 6 | runawk's author and dictd's current maintainer is me , 7 | dictd's original author is Rick Faith 8 | 9 | Two and emails. 10 | -------------------------------------------------------------------------------- /examples/demo_tokenre4: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "tokenre.awk" 4 | 5 | # This demo extracts e-mails and URLs from input text and outputs them 6 | # Using tokenre.awk for searching was inspired by talks with Vlad Shakhov 7 | 8 | # Input files for this demo: examples/demo_tokenre3.in* 9 | 10 | BEGIN { 11 | # regexp for emails and URLs are just examples and therefore don't 12 | # conform to appropriate RFCs. 13 | domains = "(com|org|net|ru|by|ua)" 14 | re = "[^ [:punct:]]+@([^ [:punct:]]+[.])+" domains 15 | re = re "|" "(https?|ftp|dict)://([^ [:punct:]]+[.])+" domains "(/[^ [:punct:]]*)?" 16 | } 17 | 18 | { 19 | cnt = splitre($0, arr, re) 20 | for (i=1; i <= cnt; ++i){ 21 | if (i > 1) 22 | printf " " 23 | printf "%s", arr [i] 24 | } 25 | if (cnt) 26 | printf "\n" 27 | } 28 | -------------------------------------------------------------------------------- /examples/demo_trim: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "trim.awk" 4 | 5 | # This is a demo for trim.awk module 6 | 7 | # Input files for this demo: examples/demo_trim.in 8 | 9 | { 10 | printf "trim_l(\"%s\")=\"%s\"\n", $0, trim_l($0) 11 | printf "trim_r(\"%s\")=\"%s\"\n", $0, trim_r($0) 12 | printf "trim_c(\"%s\")=\"%s\"\n", $0, trim_c($0) 13 | printf "trim_lr(\"%s\")=\"%s\"\n", $0, trim_lr($0) 14 | printf "trim_lrc(\"%s\")=\"%s\"\n", $0, trim_lrc($0) 15 | } 16 | -------------------------------------------------------------------------------- /examples/demo_trim.in: -------------------------------------------------------------------------------- 1 | Van Der Graaf Generator 2 | Peter Hammill 3 | King Crimson 4 | Robert Fripp 5 | Jethro Tull 6 | Jan Anderson 7 | -------------------------------------------------------------------------------- /examples/demo_trim_in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "trim_in.awk" 4 | 5 | # This is a demo for trim_in.awk module 6 | # Input files for this demo: examples/demo_trim_in.in 7 | 8 | { 9 | print $0 10 | } 11 | -------------------------------------------------------------------------------- /help.mk: -------------------------------------------------------------------------------- 1 | HELP_MSG.runawk = "runawk executable" 2 | HELP_MSG.modules = "modules written in AWK" 3 | HELP_MSG.a_getopt = "shell function for handling command line options" 4 | HELP_MSG.doc = "LICENSE, README, TODO and NEWS" 5 | HELP_MSG.examples = "Samples of use of runawk and modules" 6 | HELP_MSG.test = "Tests for runawk and modules" 7 | -------------------------------------------------------------------------------- /modules/CR_in.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 CR_in.awk 9 | # 10 | # As the name of this module says (_in suffix) this module reads and 11 | # optionally changes input lines. 12 | # 13 | # Carriage-Return symbol at the end of input lines is removed. 14 | # This symbol usually appears in Windows text files. 15 | # If you want to adapt your script to accept windows files on input, 16 | # just put 17 | # 18 | # #use "CR_in.awk" 19 | # 20 | # in the very beginning of your script. 21 | # 22 | 23 | { 24 | gsub(/\r+$/, "") 25 | } 26 | -------------------------------------------------------------------------------- /modules/Makefile: -------------------------------------------------------------------------------- 1 | MODULES != cd ${.CURDIR}; LC_ALL=C ls *.awk gawk/*.awk 2 | 3 | FILES = ${MODULES} 4 | FILESDIR = ${MODULESDIR} 5 | FILESNAME_gawk/ord.awk = gawk/ord.awk 6 | 7 | MAN = runawk_modules.3 8 | 9 | .PHONY: _manpages 10 | _manpages: ${MAN} 11 | 12 | .PATH: ${.OBJDIR} 13 | 14 | CLEANFILES += runawk_modules.3 runawk_modules.pod 15 | 16 | runawk_modules.pod: ${MODULES} pod_header.txt pod_footer.txt 17 | { set -e; cd ${.CURDIR}; \ 18 | cat pod_header.txt; \ 19 | awk '/^# =head/, NF==0 {if (/^#/) print substr($$0, 3)}' \ 20 | ${MODULES}; \ 21 | cat pod_footer.txt; } > ${.TARGET} 22 | 23 | .include 24 | -------------------------------------------------------------------------------- /modules/abort.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | 7 | #use "exitnow.awk" 8 | 9 | # =head2 abort.awk 10 | # 11 | # =over 2 12 | # 13 | # =item I 14 | # 15 | # print MSG to stderr and exits program with 16 | # EXIT_STATUS. EXIT_STATUS defaults to 1. 17 | # 18 | # =back 19 | # 20 | 21 | function abort (msg, status){ 22 | printf "error: %s\n", msg > "/dev/stderr" 23 | printf " ARGV[0]=%s\n", ARGV[0] > "/dev/stderr" 24 | printf " $0=`%s`\n", $0 > "/dev/stderr" 25 | printf " NF=%d\n", NF > "/dev/stderr" 26 | printf " FNR=%d\n", FNR > "/dev/stderr" 27 | printf " FILENAME=%s\n", FILENAME > "/dev/stderr" 28 | 29 | if (!status){ 30 | status = 1 31 | } 32 | 33 | exitnow(status) 34 | } 35 | -------------------------------------------------------------------------------- /modules/abs.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 abs.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # return absolute value of V. 15 | # 16 | # =back 17 | # 18 | 19 | function abs (v){ 20 | if (v < 0) 21 | return -v 22 | else 23 | return v 24 | } 25 | -------------------------------------------------------------------------------- /modules/alt_assert.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | #use "abort.awk" 9 | 10 | # =head2 alt_assert.awk 11 | # 12 | # =over 2 13 | # 14 | # =item I 15 | # 16 | # print an 17 | # error message MSG to standard error and terminates 18 | # the program with STATUS exit code if CONDITION is false. 19 | # 20 | # =back 21 | # 22 | 23 | function assert (cond, msg, status){ 24 | if (!cond){ 25 | abort("assertion failed: " msg, status) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/alt_getopt.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 alt_getopt.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # This function processes ARGV array and 15 | # returns TRUE if option is received, 16 | # received option is saved in 'optopt' variable, option argument (if any) 17 | # is saved in 'optarg' variable. Long options (like --help or 18 | # --long-option) present in GNU libc and BSD systems are also supported. 19 | # 20 | # NOTE: alt_getopt.awk module follows rules 21 | # from SUS/POSIX "Utility Syntax Guidelines" 22 | # 23 | # =back 24 | # 25 | 26 | ############################################################### 27 | # # EXAMPLE 28 | 29 | # #!/usr/bin/env runawk 30 | # 31 | # #use "alt_getopt.awk" 32 | # 33 | # BEGIN { 34 | # long_opts ["verbose"] = "v" 35 | # long_opts ["help"] = "h" 36 | # long_opts ["fake"] = "" 37 | # long_opts ["len"] = takes_arg 38 | # long_opts ["output"] = "o" 39 | # 40 | # while (getopt("hVvo:n:")){ 41 | # if (optopt == "h"){ 42 | # print "option `h'" 43 | # }else if (optopt == "V"){ 44 | # print "option `V'" 45 | # }else if (optopt == "v"){ 46 | # print "option `v'" 47 | # }else if (optopt == "o"){ 48 | # print "option `o':", optarg 49 | # }else if (optopt == "n"){ 50 | # print "option `n':", optarg 51 | # }else if (optopt == "fake"){ 52 | # print "option `fake'" 53 | # }else if (optopt == "len"){ 54 | # print "option `len':", optarg 55 | # }else{ 56 | # abort() 57 | # } 58 | # } 59 | # 60 | # for (i=1; i < ARGC; ++i){ 61 | # if (ARGV [i] != "") 62 | # printf "ARGV [%s] = %s\n", i, ARGV [i] 63 | # } 64 | # 65 | # exit 0 66 | # } 67 | ############################################################### 68 | 69 | #use "alt_assert.awk" 70 | 71 | function __getopt_errexit (msg, status) 72 | { 73 | print msg > "/dev/stderr" 74 | exitnow(status) 75 | } 76 | 77 | function __getopt_addstdin ( i) 78 | { 79 | for (i=1; i < ARGC; ++i){ 80 | if (ARGV [i] != __getopt_fill){ 81 | return 82 | } 83 | } 84 | 85 | ARGV [1] = "-" 86 | } 87 | 88 | BEGIN { 89 | opterr = "" 90 | 91 | takes_arg = -1 92 | } 93 | 94 | function __getopt_prepare (\ 95 | short_opts, 96 | i, len, nc) # local vars 97 | { 98 | if ("-" in __getopt_opts){ 99 | # already initialized 100 | return 101 | } 102 | 103 | len = length(short_opts) 104 | for (i=1; i <= len; ++i){ 105 | if (i < len && substr(short_opts, i+1, 1) == ":"){ 106 | __getopt_opts [substr(short_opts, i, 1)] = takes_arg 107 | ++i 108 | }else{ 109 | __getopt_opts [substr(short_opts, i, 1)] = "" 110 | } 111 | } 112 | 113 | for (i in long_opts){ 114 | __getopt_opts [i] = long_opts [i] 115 | } 116 | } 117 | 118 | function __getopt_process_short_opts (\ 119 | i, 120 | opts) # local vars 121 | { 122 | opts = substr(ARGV [i], 2) 123 | optopt = substr(opts, 1, 1) 124 | 125 | if (! (optopt in __getopt_opts)){ 126 | __getopt_errexit("Unknown option `-" optopt "'", 2) 127 | } 128 | 129 | if (__getopt_opts [optopt] == takes_arg){ 130 | if (length(opts) == 1){ 131 | optarg = ARGV [i+1] 132 | ARGV [i] = ARGV [i+1] = __getopt_fill 133 | }else{ 134 | optarg = substr(opts, 2) 135 | ARGV [i] = __getopt_fill 136 | } 137 | return 1 138 | } 139 | 140 | if (length(opts) > 1) 141 | ARGV [i] = "-" substr(opts, 2) 142 | else 143 | ARGV [i] = __getopt_fill 144 | 145 | return 1 146 | } 147 | 148 | function __getopt_process_long_opts (\ 149 | i, 150 | opt,val,prefix,eq) 151 | { 152 | opt = substr(ARGV [i], 3) 153 | eq = (opt ~ /[=]/) 154 | sub(/[=].*$/, "", opt) 155 | prefix = "--" 156 | 157 | if ((opt in __getopt_opts) && 158 | __getopt_opts [opt] != "" && 159 | __getopt_opts [opt] != takes_arg) 160 | { 161 | prefix = "-" 162 | opt = __getopt_opts [opt] 163 | } 164 | 165 | if (opt in __getopt_opts){ 166 | optopt = opt 167 | 168 | val = __getopt_opts [opt] 169 | if (val == ""){ 170 | assert(!eq, "Unexpected argument for option `" opt "'") 171 | ARGV [i] = __getopt_fill 172 | return 1 173 | }else if (val == takes_arg){ 174 | if (eq){ 175 | optarg = ARGV [i] 176 | sub(/^[^=]*=/, "", optarg) 177 | }else{ 178 | optarg = ARGV [i+1] 179 | ARGV [i+1] = __getopt_fill 180 | } 181 | ARGV [i] = __getopt_fill 182 | return 1 183 | } 184 | } 185 | 186 | __getopt_errexit("Unknown option `" prefix opt "'", 2) 187 | } 188 | 189 | function getopt (\ 190 | short_opts, no_stdin, 191 | i) # local vars 192 | { 193 | __getopt_prepare(short_opts) 194 | 195 | for (i = 1; i < ARGC; ++i){ 196 | optarg = "" 197 | 198 | if (ARGV [i] == __getopt_fill){ 199 | continue 200 | } 201 | 202 | if (ARGV [i] == "--"){ 203 | ARGV [i] = __getopt_fill 204 | __getopt_addstdin() 205 | return 0 206 | } 207 | 208 | if (ARGV [i] !~ /^-/ || ARGV [i] == "-"){ 209 | return 0 210 | } 211 | 212 | if (ARGV [i] ~ /^--/){ 213 | return __getopt_process_long_opts(i) 214 | } 215 | 216 | assert(ARGV [i] ~ /^-/) 217 | 218 | return __getopt_process_short_opts(i) 219 | } 220 | 221 | if (!no_stdin) 222 | __getopt_addstdin() 223 | 224 | return 0 225 | } 226 | -------------------------------------------------------------------------------- /modules/alt_join.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 alt_join.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # return string consisting of all keys from HASH separated by SEP. 15 | # 16 | # =item I 17 | # 18 | # return string consisting of all values from HASH separated by SEP. 19 | # 20 | # =item I 21 | # 22 | # return string consisting of all values from ARRAY 23 | # separated by SEP. Indices from START (default: 1) to END 24 | # (default: +inf) are analysed. Collecting values is stopped 25 | # on index absent in ARRAY. 26 | # 27 | # =back 28 | # 29 | 30 | function join_keys (hash, sep, k,ret,flag){ 31 | ret = "" 32 | for (k in hash){ 33 | if (flag){ 34 | ret = ret sep k 35 | }else{ 36 | flag = 1 37 | ret = k 38 | } 39 | } 40 | return ret 41 | } 42 | 43 | function join_values (hash, sep, k,ret,flag){ 44 | ret = "" 45 | for (k in hash){ 46 | if (flag){ 47 | ret = ret sep hash [k] 48 | }else{ 49 | flag = 1 50 | ret = hash [k] 51 | } 52 | } 53 | return ret 54 | } 55 | 56 | function join_by_numkeys (array, sep, start, end, ret,flag){ 57 | ret = "" 58 | 59 | if (start == "") 60 | start = 1 61 | 62 | if (end == "") 63 | end = 1.0E+37 64 | 65 | for (; start <= end && (start in array); ++start){ 66 | if (flag){ 67 | ret = ret sep array [start] 68 | }else{ 69 | flag = 1 70 | ret = array [start] 71 | } 72 | } 73 | 74 | return ret 75 | } 76 | -------------------------------------------------------------------------------- /modules/backslash_in.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 backslash_in.awk 9 | # 10 | # As the name of this module (_in suffix) says this module 11 | # reads and optionally changes input lines. 12 | # 13 | # Backslash character at the end of line is treated as a sign 14 | # that current line is continued on the next one. 15 | # Example is below. 16 | # 17 | # Input: 18 | # a b c\ 19 | # d e f g 20 | # a 21 | # b 22 | # e\ 23 | # f 24 | # 25 | # What your program using backslash_in.awk will obtain: 26 | # a b cd e f g 27 | # a 28 | # b 29 | # e f 30 | # 31 | 32 | #use "xgetline.awk" 33 | 34 | { 35 | while ($0 ~ /\\/){ 36 | assert(xgetline() > 0, "unexpected end of file") 37 | $0 = substr($0, 1, length($0)-1) __input 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /modules/basename.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | #use "has_suffix.awk" 9 | 10 | # =head2 basename.awk 11 | # 12 | # =over 2 13 | # 14 | # =item I 15 | # 16 | # return filename portion of the PATH 17 | # (the same as I) 18 | # 19 | # =back 20 | # 21 | # See example/demo_basename for the sample of usage 22 | # 23 | 24 | function basename (pathname, suffix){ 25 | sub(/\/$/, "", pathname) 26 | if (pathname == "") 27 | return "/" 28 | 29 | sub(/^.*\//, "", pathname) 30 | 31 | if (suffix != "" && has_suffix(pathname, suffix)) 32 | pathname = substr(pathname, 1, length(pathname) - length(suffix)) 33 | 34 | return pathname 35 | } 36 | -------------------------------------------------------------------------------- /modules/braceexpand.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 braceexpand.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # shell-like brace expansion. 15 | # 16 | # For example: 17 | # print braceexpand("ab{,22{,7,8}}z{8,9}") 18 | # -| abz8 abz9 ab22z8 ab22z9 ab227z8 ab227z9 ab228z8 ab228z9 19 | # 20 | # =back 21 | # 22 | 23 | #use "match_br.awk" 24 | 25 | BEGIN { 26 | __runawk_expanded = 0 27 | } 28 | 29 | function __runawk_braceexpand1 (s, 30 | 31 | left,mid,right,len,accu,i,ch,deep) 32 | { 33 | __runawk_expanded = 0 34 | 35 | if (match_br(s, "{", "}")){ 36 | left = substr(s, 1, RSTART-1) 37 | mid = substr(s, RSTART+1, RLENGTH-2) "," 38 | right = substr(s, RSTART+RLENGTH) 39 | 40 | len = RLENGTH-1 41 | 42 | s = "" 43 | 44 | accu = "" 45 | deep = 0 46 | for (i=1; i <= len; ++i){ 47 | ch = substr(mid, i, 1) 48 | 49 | if (deep == 0 && ch == ","){ 50 | if (s != ""){ 51 | s = s " " 52 | } 53 | 54 | accu = __runawk_braceexpand1(accu) 55 | 56 | if (gsub(/ /, ",", accu)){ 57 | s = s __runawk_braceexpand1(left "{" accu "}" right) 58 | }else{ 59 | s = s left accu right 60 | } 61 | 62 | accu = "" 63 | }else{ 64 | accu = accu ch 65 | 66 | if (ch == "{") 67 | ++deep 68 | else if (ch == "}") 69 | --deep 70 | } 71 | } 72 | 73 | __runawk_expanded = 1 74 | } 75 | 76 | return s 77 | } 78 | 79 | function braceexpand (s, arr,i,cnt,cont){ 80 | cont = 1 81 | 82 | while (cont){ 83 | cont = 0 84 | 85 | cnt = split(s, arr, / /) 86 | s = "" 87 | for (i=1; i <= cnt; ++i){ 88 | if (i > 1) 89 | s = s " " 90 | 91 | s = s __runawk_braceexpand1(arr [i]) 92 | cont = (cont || __runawk_expanded) 93 | } 94 | } 95 | 96 | return s 97 | } 98 | -------------------------------------------------------------------------------- /modules/dirname.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 dirname.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # return dirname portion of the PATH 15 | # (the same as I) 16 | # 17 | # =back 18 | # 19 | # See example/demo_dirname for the sample of usage 20 | # 21 | 22 | function dirname (pathname){ 23 | if (!sub(/\/[^\/]*\/?$/, "", pathname)) 24 | return "." 25 | else if (pathname != "") 26 | return pathname 27 | else 28 | return "/" 29 | } 30 | -------------------------------------------------------------------------------- /modules/embed_str.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 embed_str.awk 9 | # 10 | # This module reads a program's file, find .begin-str/.end-str pairs 11 | # and reads lines between them. 12 | # 13 | # I - Associative array with string index 14 | # 15 | # Example: 16 | # Input: 17 | # .begin-str mymsg 18 | # Line1 19 | # Line2 20 | # .end-str 21 | # Output (result) 22 | # EMBED_STR ["mymsg"]="Line1\nLine2" 23 | # 24 | # See example/demo_embed_str for the sample of usage 25 | # 26 | 27 | #use "xgetline.awk" 28 | #use "modinfo.awk" 29 | #use "xclose.awk" 30 | 31 | BEGIN { 32 | _embed_str_id = "" 33 | while (xgetline0(MODMAIN)){ 34 | if ($0 ~ /^#[.]begin-str/){ 35 | _embed_str_id = $2 36 | }else if ($0 ~ /^#[.]end-str/){ 37 | _embed_str_id = "" 38 | }else if (_embed_str_id != ""){ 39 | if (EMBED_STR [_embed_str_id] != "") 40 | _nl = "\n" 41 | else 42 | _nl = "" 43 | 44 | EMBED_STR [_embed_str_id] = EMBED_STR [_embed_str_id] _nl substr($0, 3) 45 | } 46 | } 47 | xclose(MODMAIN) 48 | } 49 | -------------------------------------------------------------------------------- /modules/exitnow.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 exitnow.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # similar to the statement 'exit' but do not run 15 | # END sections. 16 | # 17 | # =back 18 | # 19 | 20 | function exitnow (exit_status){ 21 | __runawk_exit_status = exit_status 22 | __runawk_exit = 1 23 | 24 | exit exit_status 25 | } 26 | 27 | END { 28 | if (__runawk_exit){ 29 | exit __runawk_exit_status 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /modules/fieldwidths.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 fieldwidth.awk 9 | # 10 | # By default AWK interpreter splits input lines into tokens according 11 | # to regular expression that defines "spaces" between them using 12 | # special variable FS. Sometimes it is useful to define a fixed-size 13 | # fields for tokens. This is what this module is for. The 14 | # functionality of fieldwidths.awk is very close to GNU awk's 15 | # FIELDWIDTHS variable. 16 | # 17 | # =over 2 18 | # 19 | # =item I 20 | # 21 | # extracts substrings from STRING according to FW 22 | # from the left to the right and assigns $1, $2 etc. and NF 23 | # variable. FW is a space separated list of numbers that specify 24 | # fields widths. 25 | # 26 | # =item I 27 | # 28 | # Does the the same as `fieldwidths' function but splits $0 instead. 29 | # 30 | # =item I 31 | # 32 | # global variable. If it is set to non-empty string, all input 33 | # lines are split automatically and the value of variable FS is 34 | # ignored in this case. 35 | # 36 | # =back 37 | # 38 | # See example/demo_fieldwidths for the sample of usage 39 | # 40 | 41 | function fieldwidths (s, fw, arr,cnt,i){ 42 | cnt = split(fw, arr, " ") 43 | 44 | for (i=1; s != "" && i <= cnt; ++i){ 45 | $i = substr(s, 1, arr [i]) 46 | s = substr(s, arr [i]+1) 47 | } 48 | 49 | NF = i-1 50 | } 51 | 52 | function fieldwidths0 (fw){ 53 | fieldwidths($0, fw) 54 | } 55 | 56 | { 57 | if (FW != ""){ 58 | fieldwidths0(FW) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /modules/ftrans_in.awk: -------------------------------------------------------------------------------- 1 | # ftrans.awk --- handle data file transitions 2 | # 3 | # user supplies beginfile() and endfile() functions 4 | # 5 | # Arnold Robbins, arnold@skeeve.com, Public Domain, November 1992 6 | # Aleksey Cheusov, vle@gmx.net, Public Domain, September 2010 7 | # (fix and adaptation for nawk by B.Kernighan) 8 | 9 | # =head2 ftrans_in.awk 10 | # 11 | # beginfile() function provided by user is called before file reading 12 | # 13 | # endfile() function provided by user is called after file reading 14 | # 15 | 16 | FNR == 1 { 17 | if (_filename_ != "") 18 | endfile(_filename_) 19 | 20 | _filename_ = (FILENAME == "" ? "-" : FILENAME) # for nawk 21 | 22 | beginfile(_filename_) 23 | } 24 | 25 | END { 26 | if (_filename_ != "") # fix for Arnold's version 27 | endfile(_filename_) 28 | } 29 | -------------------------------------------------------------------------------- /modules/gawk/ord.awk: -------------------------------------------------------------------------------- 1 | # ord.awk --- do ord and chr 2 | 3 | # Global identifiers: 4 | # _ord_: numerical values indexed by characters 5 | # _ord_init: function to initialize _ord_ 6 | # 7 | # Arnold Robbins, arnold@skeeve.com, Public Domain 8 | # 16 January, 1992 9 | # 20 July, 1992, revised 10 | 11 | # =head2 ord.awk 12 | # 13 | # =over 2 14 | # 15 | # =item I 16 | # 17 | # return numeral code of CHAR 18 | # 19 | # =item I 20 | # 21 | # return symbol from the CODE 22 | # 23 | # =back 24 | # 25 | 26 | BEGIN { _ord_init() } 27 | 28 | function _ord_init( low, high, i, t) 29 | { 30 | low = sprintf("%c", 7) # BEL is ascii 7 31 | if (low == "\a") { # regular ascii 32 | low = 0 33 | high = 127 34 | } else if (sprintf("%c", 128 + 7) == "\a") { 35 | # ascii, mark parity 36 | low = 128 37 | high = 255 38 | } else { # ebcdic(!) 39 | low = 0 40 | high = 255 41 | } 42 | 43 | for (i = low; i <= high; i++) { 44 | t = sprintf("%c", i) 45 | _ord_[t] = i 46 | } 47 | } 48 | function ord(str, c) 49 | { 50 | # only first character is of interest 51 | c = substr(str, 1, 1) 52 | return _ord_[c] 53 | } 54 | 55 | function chr(c) 56 | { 57 | # force c to be numeric by adding 0 58 | return sprintf("%c", c + 0) 59 | } 60 | -------------------------------------------------------------------------------- /modules/glob.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 glob.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # convert glob PATTERN 15 | # (http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13) 16 | # to equivalent extended regular expression 17 | # (http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04) 18 | # 19 | # =back 20 | # 21 | 22 | #use "multisub.awk" 23 | 24 | BEGIN { 25 | __g2e="^:\\^ $:[$] (:[(] ):[)] {:[{] }:[}]" 26 | __g2e=__g2e " \\[([^\\[\\]]|\\\\\\[|\\\\\\])*\\]:& .:[.] *:.* +:[+]" 27 | __g2e=__g2e " ?:. |:[|] \\\\:\\\\" 28 | __g2e=__g2e " \\^:\\^ \\$:[$] \\(:[(] \\):[)] \\{:[{] \\}:[}]" 29 | __g2e=__g2e " \\[:\\[ \\]:\\] \\.:[.] \\*:[*] \\+:[+] \\?:[?]" 30 | __g2e=__g2e " \\|:[|] \\:" 31 | } 32 | 33 | function glob2ere (p){ 34 | return multisub(p, __g2e, "&") 35 | } 36 | 37 | function glob (s, p, re){ 38 | if (p in __runawk_glob2ere) 39 | re = __runawk_glob2ere [p] 40 | else 41 | re = __runawk_glob2ere [p] = ("^" glob2ere(p) "$") 42 | 43 | return s ~ re 44 | } 45 | -------------------------------------------------------------------------------- /modules/has_prefix.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 has_prefix.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # return TRUE if STRING begins with PREFIX 15 | # 16 | # =back 17 | # 18 | # See example/demo_has_prefix for the sample of usage 19 | # 20 | 21 | function has_prefix (s, pre, pre_len, s_len){ 22 | pre_len = length(pre) 23 | s_len = length(s) 24 | 25 | return pre_len <= s_len && substr(s, 1, pre_len) == pre 26 | } 27 | -------------------------------------------------------------------------------- /modules/has_suffix.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 has_suffix.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # return TRUE if STRING ends with SUFFIX 15 | # 16 | # =back 17 | # 18 | # See example/demo_has_suffix for the sample of usage 19 | # 20 | 21 | function has_suffix (s, suf, suf_len, s_len){ 22 | suf_len = length(suf) 23 | s_len = length(s) 24 | 25 | return suf_len <= s_len && substr(s, s_len - suf_len + 1) == suf 26 | } 27 | -------------------------------------------------------------------------------- /modules/heapsort.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 heapsort.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # The content of `src_array' is sorted using awk's rules for 15 | # comparing values. Values with indices in range [start, end] are 16 | # sorted. `src_array' array is not changed. 17 | # Instead dest_remap array is generated such that 18 | # 19 | # Result: 20 | # src_array [dest_remap [start]] <= 21 | # <= src_array [dest_remap [start+1]] <= 22 | # <= src_array [dest_remap [start+2]] <= ... <= 23 | # <= src_array [dest_remap [end]] 24 | # 25 | # `heapsort' algorithm is used. 26 | # Examples: see demo_heapsort and demo_heapsort2 executables. 27 | # 28 | # =item I 29 | # 30 | # The same as `heapsort' described above, but hash values are sorted. 31 | # 32 | # Result: 33 | # src_array [dest_remap [1]] <= 34 | # <= src_array [dest_remap [2]] <= 35 | # <= src_array [dest_remap [3]] <= ... <= 36 | # <= src_array [dest_remap [count]] 37 | # 38 | # `count', a number of elements in `src_hash', is a return value. 39 | # 40 | # Examples: see demo_heapsort3 executable. 41 | # 42 | # =item I 43 | # 44 | # The same as `heapsort' described above, but hash indices are sorted. 45 | # 46 | # Result: 47 | # dest_remap [1] <= 48 | # <= dest_remap [2] <= 49 | # <= dest_remap [3] <= ... <= 50 | # <= dest_remap [count] 51 | # 52 | # `count', a number of elements in `src_hash', is a return value. 53 | # 54 | # Examples: demo_ini 55 | # 56 | # =item I 57 | # 58 | # The same as function "heapsort0" but $1, $2... array is sorted. 59 | # Note that $1, $2... are not changed, but dest_remap array is filled in! 60 | # The variable "start" default to 1, "end" -- to NF. 61 | # If "strnum" is set to 1, values are forcibly compared as strings. 62 | # If "strnum" is set to 2, values are forcibly compared as numbers. 63 | # 64 | # =item I 65 | # 66 | # The same as "heapsort_fields" but $1, $2... are changed. 67 | # 68 | # =back 69 | # 70 | 71 | function sift_down (array, root, start, end, index_remap, n0, v){ 72 | while (1){ 73 | n0 = root - start + 1 + root 74 | 75 | if (n0 > end) 76 | return 77 | 78 | if (n0 < end && array [index_remap [n0]] < array [index_remap [n0+1]]) 79 | ++n0 80 | 81 | if (array [index_remap [root]] >= array [index_remap [n0]]) 82 | return 83 | 84 | v = index_remap [root] 85 | index_remap [root] = index_remap [n0] 86 | index_remap [n0] = v 87 | 88 | root = n0 89 | } 90 | } 91 | 92 | function sift_up (array, root, start, end, index_remap, n0, v){ 93 | while (1){ 94 | n0 = int((root - start + 1)/2) - 1 + start 95 | 96 | if (n0 < start) 97 | return 98 | 99 | if (array [index_remap [root]] >= array [index_remap [n0]]) 100 | return 101 | 102 | v = index_remap [root] 103 | index_remap [root] = index_remap [n0] 104 | index_remap [n0] = v 105 | 106 | root = n0 107 | } 108 | } 109 | 110 | function heapsort (array, index_remap, start, end, i,v){ 111 | for (i=start; i <= end; ++i){ 112 | index_remap [i] = i 113 | } 114 | for (i=int((start+end)/2); i >= start; --i){ 115 | sift_down(array, i, start, end, index_remap) 116 | } 117 | for (; end > start; --end){ 118 | v = index_remap [start] 119 | index_remap [start] = index_remap [end] 120 | index_remap [end] = v 121 | 122 | sift_down(array, start, start, end-1, index_remap) 123 | } 124 | } 125 | 126 | function heapsort_values (hash, remap_idx, 127 | array, remap, i, cnt) 128 | { 129 | cnt = 0 130 | for (i in hash) { 131 | ++cnt 132 | array [cnt] = hash [i] 133 | remap [cnt] = i 134 | } 135 | 136 | heapsort(array, remap_idx, 1, cnt) 137 | 138 | for (i=1; i <= cnt; ++i) { 139 | remap_idx [i] = remap [remap_idx [i]] 140 | } 141 | 142 | return cnt 143 | } 144 | 145 | function heapsort_indices (hash, remap_idx, 146 | array, i, cnt) 147 | { 148 | cnt = 0 149 | for (i in hash) { 150 | ++cnt 151 | array [cnt] = i 152 | } 153 | 154 | heapsort(array, remap_idx, 1, cnt) 155 | 156 | for (i=1; i <= cnt; ++i) { 157 | remap_idx [i] = array [remap_idx [i]] 158 | } 159 | 160 | return cnt 161 | } 162 | 163 | function heapsort_fields (index_remap, start, end, str, i,arr) 164 | { 165 | if (start == "") 166 | start = 1 167 | if (end == "") 168 | end = NF 169 | 170 | if (str == 1){ 171 | for (i=start; i <= end; ++i){ 172 | arr [i] = ($i "") 173 | } 174 | }else if (str == 2){ 175 | for (i=start; i <= end; ++i){ 176 | arr [i] = $i + 0 177 | } 178 | }else{ 179 | for (i=start; i <= end; ++i){ 180 | arr [i] = $i 181 | } 182 | } 183 | heapsort(arr, index_remap, start, end) 184 | } 185 | 186 | function heapsort0 (start, end, str, i,arr,remap) 187 | { 188 | if (start == "") 189 | start = 1 190 | if (end == "") 191 | end = NF 192 | 193 | if (str == 1){ 194 | for (i=start; i <= end; ++i){ 195 | arr [i] = ($i "") 196 | } 197 | }else if (str == 2){ 198 | for (i=start; i <= end; ++i){ 199 | arr [i] = $i + 0 200 | } 201 | }else{ 202 | for (i=start; i <= end; ++i){ 203 | arr [i] = $i 204 | } 205 | } 206 | 207 | heapsort(arr, remap, start, end) 208 | for (i=start; i <= end; ++i){ 209 | $i = arr [remap [i]] 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /modules/ini.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 ini.awk 9 | # 10 | # This module provides functions for manipulating .ini files. 11 | # See example/demo_ini for the sample of use. 12 | # 13 | # =over 2 14 | # 15 | # =item I 16 | # 17 | # Reads .ini file FILENAME and fills array RESULT, e.g. 18 | # RESULT [] = etc. 19 | # If SEPARATOR is not specified, `.' symbols is used by default. 20 | # 21 | # =back 22 | # 23 | # Features: 24 | # 25 | # - spaces are allowed everywhere, i.e. at the beginning and end of 26 | # line, around `=' separator. THEY ARE STRIPPED! 27 | # - comment lines start with `;' or `#' sign. Comment lines are ignored. 28 | # - values can be surrounded by signle or double quote. In this case 29 | # spaces are presenrved, otherwise they are removed from 30 | # beginning and at the end of line and replaced with single space 31 | # in the middle of the line. 32 | # - Escape character are not supported (yet?). 33 | # 34 | 35 | #use "alt_assert.awk" 36 | #use "xgetline.awk" 37 | #use "trim.awk" 38 | 39 | function __runawk_register_value (s, n, v, r, sep){ 40 | r [s sep n] = v 41 | } 42 | 43 | function read_inifile (inifile, result, sep) { 44 | if (sep == "") 45 | sep = "." 46 | 47 | while (xgetline(inifile)){ 48 | sub(/^[[:space:]]*[;#].*$/, "", __input) 49 | sub(/[;#][^"']*$/, "", __input) 50 | sub(/^[[:space:]]+/, "", __input) 51 | sub(/[[:space:]]+$/, "", __input) 52 | 53 | if (__input == "") 54 | continue 55 | 56 | ############ [section] 57 | if (__input ~ /^\[.+\]$/){ 58 | section = substr(__input, 2, length(__input)-2) 59 | continue 60 | } 61 | 62 | ############ name = value 63 | idx = match(__input, /=/) 64 | assert(idx > 0, "`=' cannot be found") 65 | 66 | name = trim_lrc(substr(__input, 1, idx-1)) 67 | value = trim_lr(substr(__input, idx+1)) 68 | 69 | # name = "value" 70 | if (value ~ /^".*"$/ || value ~ /^'.*'$/){ 71 | __runawk_register_value(\ 72 | section, name, 73 | substr(value, 2, length(value)-2), result, sep) 74 | continue 75 | } 76 | 77 | assert(value !~ /^["']|["']$/, "wrong value") 78 | 79 | # name = v a l u e 80 | __runawk_register_value(section, name, trim_c(value), result, sep) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /modules/init_getopt.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 init_getopt.awk 9 | # 10 | # Initialization step for power_getopt.awk module. In some cases it 11 | # makes sense to process options in a while() loop. This module 12 | # allows doing this. See the documentation about how options are 13 | # initialized in power_getopt.awk module. 14 | # 15 | # =over 2 16 | # 17 | # =item I 18 | # 19 | # display help message. 20 | # 21 | # =back 22 | # 23 | 24 | #use "alt_getopt.awk" 25 | #use "embed_str.awk" 26 | 27 | function print_help ( i){ 28 | for (i = 1; i <= _help_msg_cnt; ++i){ 29 | if (_help_msg_arr [i] ~ /^[ \t]*=/){ 30 | sub(/=/, "-", _help_msg_arr [i]) 31 | } 32 | print _help_msg_arr [i] > "/dev/stderr" 33 | } 34 | } 35 | 36 | BEGIN { 37 | if ("help" in EMBED_STR){ 38 | _help_msg = EMBED_STR ["help"] 39 | _help_msg_cnt = split(_help_msg, _help_msg_arr, /\n/) 40 | for (i = 1; i <= _help_msg_cnt; ++i){ 41 | if (match(_help_msg_arr [i], /^[ \t]*[-=][^ \t]+/)){ 42 | _opt = substr(_help_msg_arr [i], RSTART, RLENGTH) 43 | sub(/^[ \t]+/, "", _opt) 44 | 45 | if (_opt ~ /^-.[|]--.+$/){ 46 | # -h|--help 47 | _sopt = substr(_opt, 1, 2) 48 | _lopt = substr(_opt, 4) 49 | }else if (_opt ~ /^=.[|]--.+$/){ 50 | # =h|--help 51 | _sopt = substr(_opt, 1, 2) 52 | _lopt = "=" substr(_opt, 5) 53 | }else if (_opt ~ /^[-=].$/){ 54 | # -h or =h 55 | _sopt = _opt 56 | _lopt = "" 57 | }else if (_opt ~ /^[-=]-.+$/){ 58 | # --help or =-help 59 | _sopt = "" 60 | _lopt = _opt 61 | } 62 | 63 | if (_sopt ~ /^-.$/){ 64 | # -h 65 | short_opts = short_opts substr(_sopt, 2, 1) 66 | }else if (_sopt ~ /^=.$/){ 67 | # =F 68 | short_opts = short_opts substr(_sopt, 2, 1) ":" 69 | } 70 | 71 | sub(/^[-=]/, "", _sopt) 72 | 73 | if (_lopt ~ /^--.+$/){ 74 | # --help 75 | long_opts [substr(_lopt, 3)] = _sopt 76 | }else if (_lopt ~ /^=-.+$/){ 77 | # =-FLAG 78 | if (_sopt != "") 79 | long_opts [substr(_lopt, 3)] = _sopt 80 | else 81 | long_opts [substr(_lopt, 3)] = takes_arg 82 | } 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /modules/io.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 io.awk 9 | # 10 | # This module provides a number of IO functions. 11 | # 12 | # =over 2 13 | # 14 | # =item I 15 | # 16 | # returns 1 if the specified FILENAME 17 | # is a regular file or 0 otherwise. 18 | # 19 | # =item I 20 | # 21 | # returns 1 if the specified FILENAME 22 | # is a socket or 0 otherwise. 23 | # 24 | # =item I 25 | # 26 | # returns 1 if the specified FILENAME 27 | # is a dir or 0 otherwise. 28 | # 29 | # =item I 30 | # 31 | # returns 1 if the specified FILENAME 32 | # is executable or 0 otherwise. 33 | # 34 | # =item I 35 | # 36 | # returns 1 if the specified FILENAME 37 | # is a FIFO or 0 otherwise. 38 | # 39 | # =item I 40 | # 41 | # returns 1 if the specified FILENAME 42 | # is a block special file or 0 otherwise. 43 | # 44 | # =item I 45 | # 46 | # returns 1 if the specified FILENAME 47 | # is a character special file or 0 otherwise. 48 | # 49 | # =item I 50 | # 51 | # returns 1 if the specified FILENAME 52 | # is a symlink or 0 otherwise. 53 | # 54 | # =item I 55 | # 56 | # returns the size of the specified FILENAME. 57 | # If USE_STAT_NOT_LSTAT is True, stat(2) is used instead of lstat(2). 58 | # 59 | # Return value: 60 | # -2 if file doesn't exist 61 | # -1 if file is not a regular file 62 | # filesize otherwise 63 | # 64 | # =item I 65 | # 66 | # returns a single letter that corrspond to the file 67 | # type. If USE_STAT_NOT_LSTAT is True, stat(2) is used instead of lstat(2). 68 | # 69 | # Return value: 70 | # - -- regular file 71 | # d -- directory 72 | # c -- character device 73 | # b -- block device 74 | # p -- FIFO 75 | # l -- symlink 76 | # s -- socket 77 | # 78 | # =back 79 | # 80 | # See example/demo_io for the sample of usage 81 | # 82 | 83 | #use "shquote.awk" 84 | 85 | function is_file (fn){ 86 | return !system("test -f " shquote(fn)) 87 | } 88 | 89 | function is_socket (fn){ 90 | return !system("test -S " shquote(fn)) 91 | } 92 | 93 | function is_dir (fn){ 94 | return !system("test -d " shquote(fn)) 95 | } 96 | 97 | function is_exec (fn){ 98 | return !system("test -x " shquote(fn)) 99 | } 100 | 101 | function is_fifo (fn){ 102 | return !system("test -p " shquote(fn)) 103 | } 104 | 105 | function is_blockdev (fn){ 106 | return !system("test -b " shquote(fn)) 107 | } 108 | 109 | function is_chardev (fn){ 110 | return !system("test -c " shquote(fn)) 111 | } 112 | 113 | function is_symlink (fn){ 114 | return !system("test -h " shquote(fn)) 115 | } 116 | 117 | function file_size (fn, stat, d0,arr,cmd){ 118 | cmd = "ls -ld " (stat ? "-L " : "") shquote(fn) " 2>/dev/null" 119 | if (0 < (cmd | getline d0)){ 120 | close(cmd) 121 | if (d0 ~ /^-/){ 122 | split(d0, arr, " ") 123 | return arr [5] 124 | }else{ 125 | return -1; 126 | } 127 | }else{ 128 | close(cmd) 129 | return -2 130 | } 131 | } 132 | 133 | function file_type (fn, stat, d0,cmd){ 134 | cmd = "ls -ld " (stat ? "-L " : "") shquote(fn) " 2>/dev/null" 135 | if (0 < (cmd | getline d0)){ 136 | close(cmd) 137 | return substr(d0, 1, 1) 138 | }else{ 139 | close(cmd) 140 | return "" 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /modules/isnum.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 isnum.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # returns 1 if an argument is a number 15 | # 16 | # =back 17 | # 18 | 19 | function isnum (v){ 20 | return v == v + 0 21 | } 22 | -------------------------------------------------------------------------------- /modules/match_br.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 match_br.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # return start position (or zero if failure) of the substring 15 | # surrounded by balanced (), [], {} or similar characters 16 | # Also sets RSTART and RLENGTH variables just like 17 | # the standard 'match' function does 18 | # 19 | # For example: 20 | # print match_br("A (B (), C(D,C,F (), 123))", "(", ")") 21 | # print RSTART, RLENGTH 22 | # -| 3 23 | # -| 3 24 | # -| 24 25 | # 26 | # =back 27 | # 28 | 29 | function match_br (s, br_open, br_close, len,i,cnt){ 30 | len = length(s) 31 | cnt = 0 32 | for (i=1; i <= len; ++i){ 33 | ch = substr(s, i, 1) 34 | 35 | if (ch == br_open){ 36 | if (cnt == 0){ 37 | RSTART = i 38 | } 39 | 40 | ++cnt 41 | }else if (ch == br_close){ 42 | --cnt 43 | 44 | if (cnt == 0){ 45 | RLENGTH=i-RSTART+1 46 | return RSTART 47 | } 48 | } 49 | } 50 | 51 | return 0 52 | } 53 | -------------------------------------------------------------------------------- /modules/max.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 max.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # maximum functions 15 | # 16 | # =item I 17 | # 18 | # returns a maximum key in HASH or DFLT if it is empty 19 | # 20 | # =item I 21 | # 22 | # returns a maximum value in HASH or DFLT if it is empty 23 | # 24 | # =item I 25 | # 26 | # returns A KEY OF maximum value in HASH or DFLT if it is empty 27 | # 28 | # =back 29 | # 30 | 31 | function max (a, b){ 32 | return (a > b ? a : b) 33 | } 34 | 35 | function max3 (a, b, c, m){ 36 | m = (a > b ? a : b) 37 | return (m > c ? m : c) 38 | } 39 | 40 | function max4 (a, b, c, d, m){ 41 | m = (a > b ? a : b) 42 | m = (m > c ? m : c) 43 | return (m > d ? m : d) 44 | } 45 | 46 | function max5 (a, b, c, d, e, m){ 47 | m = (a > b ? a : b) 48 | m = (m > c ? m : c) 49 | m = (m > d ? m : d) 50 | return (m > e ? m : e) 51 | } 52 | 53 | 54 | function max_key(hash, dflt, i){ 55 | for (i in hash){ 56 | dflt = i 57 | break 58 | } 59 | for (i in hash){ 60 | if (i > dflt) 61 | dflt = i 62 | } 63 | return dflt 64 | } 65 | 66 | function max_value(hash, dflt, i){ 67 | for (i in hash){ 68 | dflt = hash [i] 69 | break 70 | } 71 | for (i in hash){ 72 | if (hash [i] > dflt) 73 | dflt = hash [i] 74 | } 75 | return dflt 76 | } 77 | 78 | function key_of_max_value(hash, dflt, i,v){ 79 | for (i in hash){ 80 | dflt = i 81 | v = hash [i] 82 | break 83 | } 84 | for (i in hash){ 85 | if (hash [i] > v){ 86 | dflt = i 87 | v = hash [i] 88 | } 89 | } 90 | return dflt 91 | } 92 | -------------------------------------------------------------------------------- /modules/min.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 min.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # minimum functions 15 | # 16 | # =item I 17 | # 18 | # returns a minimum key in HASH or DFLT if it is empty 19 | # 20 | # =item I 21 | # 22 | # returns a minimum value in HASH or DFLT if it is empty 23 | # 24 | # =item I 25 | # 26 | # returns A KEY OF minimum value in HASH or DFLT if it is empty 27 | # 28 | # =back 29 | # 30 | 31 | function min (a, b){ 32 | return (a < b ? a : b) 33 | } 34 | 35 | function min3 (a, b, c, m){ 36 | m = (a < b ? a : b) 37 | return (m < c ? m : c) 38 | } 39 | 40 | function min4 (a, b, c, d, m){ 41 | m = (a < b ? a : b) 42 | m = (m < c ? m : c) 43 | return (m < d ? m : d) 44 | } 45 | 46 | function min5 (a, b, c, d, e, m){ 47 | m = (a < b ? a : b) 48 | m = (m < c ? m : c) 49 | m = (m < d ? m : d) 50 | return (m < e ? m : e) 51 | } 52 | 53 | function min_key(hash, dflt, i){ 54 | for (i in hash){ 55 | dflt = i 56 | break 57 | } 58 | for (i in hash){ 59 | if (i < dflt) 60 | dflt = i 61 | } 62 | return dflt 63 | } 64 | 65 | function min_value(hash, dflt, i){ 66 | for (i in hash){ 67 | dflt = hash [i] 68 | break 69 | } 70 | for (i in hash){ 71 | if (hash [i] < dflt) 72 | dflt = hash [i] 73 | } 74 | return dflt 75 | } 76 | 77 | function key_of_min_value(hash, dflt, i,v){ 78 | for (i in hash){ 79 | dflt = i 80 | v = hash [i] 81 | break 82 | } 83 | for (i in hash){ 84 | if (hash [i] < v){ 85 | dflt = i 86 | v = hash [i] 87 | } 88 | } 89 | return dflt 90 | } 91 | -------------------------------------------------------------------------------- /modules/modinfo.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 modinfo.awk 9 | # 10 | # This module provides the following variables 11 | # 12 | # =over 2 13 | # 14 | # =item I 15 | # 16 | # A number of modules (-f ) passed to an awk interpreter 17 | # 18 | # =item I 19 | # 20 | # Array with [0..MODC) indexes of those modules 21 | # 22 | # =item I 23 | # 24 | # Path to the main module, i.e. program filename 25 | # 26 | # =back 27 | # 28 | # See example/demo_modinfo for the sample of usage 29 | # 30 | 31 | BEGIN { 32 | MODC = ENVIRON ["RUNAWK_MODC"] + 0 # force to number 33 | 34 | for (i=0; i < MODC; ++i){ 35 | MODV [i] = ENVIRON ["RUNAWK_MODV_" i] 36 | } 37 | 38 | MODMAIN = MODV [MODC-1] 39 | } 40 | -------------------------------------------------------------------------------- /modules/multisub.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 multisub.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # is a substitution function. It searches for 15 | # a list of substrings, specified in SUBST_REPL 16 | # in a left-most longest order and (if found) replaces 17 | # found fragments with appropriate replacement. 18 | # SUBST_REPL format: "SUBSTRING1:REPLACEMENT1 SUBSTRING2:REPLACEMENT2...". 19 | # Three spaces separate substring:replacement pairs from each other. 20 | # If KEEP is specified and some REPLACEMENT(N) is equal to it, then 21 | # appropriate SUBSTRING(N) is treated as a regular expression 22 | # and matched text is kept as is, i.e. not changed. 23 | # 24 | # For example: 25 | # print multisub("ABBABBBBBBAAB", "ABB:c BBA:d AB:e") 26 | # |- ccBBde 27 | # 28 | # =back 29 | # 30 | 31 | #use "alt_assert.awk" 32 | #use "str2regexp.awk" 33 | 34 | BEGIN { 35 | __runawk_multisub_num = -1 36 | } 37 | 38 | function __runawk_multisub_prepare (repls, keep, 39 | arr, i, repl_left, repl_right, re) # local vars 40 | { 41 | if (!repls){ 42 | return -1 43 | }else if (repls in __runawk_multisub){ 44 | return __runawk_multisub [repls] 45 | }else{ 46 | ++__runawk_multisub_num 47 | 48 | __runawk_multisub [repls] = __runawk_multisub_num 49 | split(repls, arr, / /) 50 | 51 | for (i in arr){ 52 | # split into 'repl_left' and 'repl_right' 53 | repl_right = repl_left = arr [i] 54 | sub(/:.*$/, "", repl_left) 55 | sub(/^.*:/, "", repl_right) 56 | 57 | # whole regexp 58 | if (re != ""){ 59 | re = re "|" 60 | } 61 | 62 | # substr to repl 63 | if (repl_right != keep){ 64 | __runawk_tr_repl [__runawk_multisub_num, repl_left] = repl_right 65 | repl_left = str2regexp(repl_left) 66 | } 67 | 68 | re = re repl_left 69 | } 70 | 71 | __runawk_tr_regexp [__runawk_multisub_num] = re 72 | 73 | return __runawk_multisub_num 74 | } 75 | } 76 | 77 | function multisub (str, repls, keep, 78 | n,middle,beg,end,ret,repl) #local vars 79 | { 80 | n = __runawk_multisub_prepare(repls, keep) 81 | if (n < 0 || !match(str, __runawk_tr_regexp [n])){ 82 | return str 83 | }else{ 84 | middle = substr(str, RSTART, RLENGTH) 85 | beg = substr(str, 1, RSTART-1) 86 | end = substr(str, RSTART+RLENGTH) 87 | 88 | if ((n SUBSEP middle) in __runawk_tr_repl) 89 | ret = beg __runawk_tr_repl [n, middle] multisub(end, repls) 90 | else 91 | ret = beg middle multisub(end, repls) 92 | 93 | return ret 94 | } 95 | } 96 | 97 | BEGIN { 98 | assert("ccBBde" == multisub("ABBABBBBBBAAB", "ABB:c BBA:d AB:e"), 99 | "Email bug to the author") 100 | } 101 | -------------------------------------------------------------------------------- /modules/pod_footer.txt: -------------------------------------------------------------------------------- 1 | =head1 AUTHOR 2 | 3 | Copyright (c) 2007-2014 Aleksey Cheusov 4 | 5 | =head1 BUGS/FEEDBACK 6 | 7 | Please send any comments, questions, bug reports etc. to me by e-mail 8 | or register them at sourceforge project home. Feature requests are 9 | also welcomed. 10 | 11 | =head1 HOME 12 | 13 | L 14 | 15 | =head1 SEE ALSO 16 | L 17 | -------------------------------------------------------------------------------- /modules/pod_header.txt: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | runawk - wrapper for AWK interpreter 4 | 5 | =head1 MODULES 6 | 7 | B provides dozens of modules. 8 | Below is the documentation for them. 9 | 10 | -------------------------------------------------------------------------------- /modules/pow.awk: -------------------------------------------------------------------------------- 1 | 2 | # Written by Aleksey Cheusov , public domain 3 | # 4 | # This awk module is a part of RunAWK distribution, 5 | # http://sourceforge.net/projects/runawk 6 | # 7 | ############################################################ 8 | 9 | # =head2 pow.awk 10 | # 11 | # =over 2 12 | # 13 | # =item I 14 | # 15 | # returns the value of X to the exponent Y 16 | # 17 | # =back 18 | # 19 | 20 | function pow (x, y){ 21 | return x ^ y 22 | } 23 | -------------------------------------------------------------------------------- /modules/power_getopt.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | #use "init_getopt.awk" 9 | 10 | # =head2 power_getopt.awk 11 | # 12 | # power_getopt.awk module provides a very easy way to add options 13 | # to AWK application and follows rules from 14 | # SUS/POSIX "Utility Syntax Guidelines" 15 | # 16 | # power_getopt.awk analyses '.begin-str help/.end-str' section in 17 | # AWK program (main module), and processes options specified there. 18 | # The following strings mean options: 19 | # -X single letter option 20 | # --XXX long option 21 | # -X|--XXX single letter option with long synonym 22 | # =X single letter option with argument 23 | # =-XXX long option with argument 24 | # =X|--XXX single letter option and long synonym with argument 25 | # 26 | # If --help option was applied, usage information is printed 27 | # (lines between ".begin-str help" and ".end-str") replacing leading 28 | # `=' character with `-'. 29 | # 30 | # =over 2 31 | # 32 | # =item I 33 | # 34 | # returns either 1 (option OPT was applied) or 0 (OPT was not 35 | # applied) for options not accepting the argument, and either 36 | # specified value or DEFAULT for options accepting the argument. 37 | # 38 | # See example/demo_power_getopt for the sample of usage 39 | # 40 | # =back 41 | # 42 | 43 | function getarg (opt, dflt, tmp){ 44 | assert(opt in __getopt_opts, "Bad option `" opt "`") 45 | 46 | if (opt in long_opts){ 47 | tmp = long_opts [opt] 48 | if (tmp != "" && tmp != takes_arg) 49 | opt = tmp 50 | } 51 | 52 | if (opt in options){ 53 | return options [opt] 54 | }else if (__getopt_opts [opt] == takes_arg){ 55 | return dflt 56 | }else{ 57 | return 0 58 | } 59 | } 60 | 61 | BEGIN { 62 | # options 63 | __getopt_fill = "\001getopt_fake\002" 64 | 65 | while (getopt(short_opts)){ 66 | if (optopt in long_opts){ 67 | _i = long_opts [optopt] 68 | if (_i != "" && _i != takes_arg) 69 | optopt = _i 70 | } 71 | if (__getopt_opts [optopt] == takes_arg) 72 | options [optopt] = optarg 73 | else 74 | ++options [optopt] 75 | } 76 | 77 | __getopt_to = 1 78 | for (__getopt_from = 1; __getopt_from < ARGC; ++__getopt_from){ 79 | if (ARGV [__getopt_from] != __getopt_fill){ 80 | ARGV [__getopt_to++] = ARGV [__getopt_from] 81 | } 82 | } 83 | ARGC = __getopt_to 84 | 85 | if (("help" in options) || 86 | ("help" in long_opts) && (long_opts ["help"] in options)) 87 | { 88 | print_help() 89 | exitnow(0) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /modules/quicksort.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 quicksort.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # The content of `src_array' is sorted using awk's rules for 15 | # comparing values. Values with indices in range [start, end] are 16 | # sorted. `src_array' array is not changed. 17 | # Instead dest_remap array is generated such that 18 | # 19 | # Result: 20 | # src_array [dest_remap [start]] <= 21 | # <= src_array [dest_remap [start+1]] <= 22 | # <= src_array [dest_remap [start+2]] <= ... <= 23 | # <= src_array [dest_remap [end]] 24 | # 25 | # `quicksort' algorithm is used. 26 | # Examples: see demo_quicksort and demo_quicksort2 executables 27 | # 28 | # =item I 29 | # 30 | # The same as `quicksort' described above, but hash values are sorted. 31 | # 32 | # Result: 33 | # src_hash [dest_remap [1]] <= 34 | # <= src_hash [dest_remap [2]] <= 35 | # <= src_hash [dest_remap [3]] <= ... <= 36 | # <= src_hash [dest_remap [count]] 37 | # 38 | # `count', a number of elements in `src_hash', is a return value. 39 | # Examples: see demo_quicksort* executables. 40 | # 41 | # =item I 42 | # 43 | # The same as `quicksort' described above, but hash indices are sorted. 44 | # 45 | # Result: 46 | # dest_remap [1] <= 47 | # <= dest_remap [2] <= 48 | # <= dest_remap [3] <= ... <= 49 | # <= dest_remap [count] 50 | # 51 | # `count', a number of elements in `src_hash', is a return value. 52 | # 53 | # =back 54 | # 55 | 56 | function __quicksort (array, index_remap, start, end, 57 | MedIdx,Med,v,i,storeIdx) 58 | { 59 | if ((end - start) <= 0) 60 | return 61 | 62 | MedIdx = int((start+end)/2) 63 | Med = array [index_remap [MedIdx]] 64 | 65 | v = index_remap [end] 66 | index_remap [end] = index_remap [MedIdx] 67 | index_remap [MedIdx] = v 68 | 69 | storeIdx = start 70 | for (i=start; i < end; ++i){ 71 | if (array [index_remap [i]] < Med){ 72 | v = index_remap [i] 73 | index_remap [i] = index_remap [storeIdx] 74 | index_remap [storeIdx] = v 75 | 76 | ++storeIdx 77 | } 78 | } 79 | 80 | v = index_remap [storeIdx] 81 | index_remap [storeIdx] = index_remap [end] 82 | index_remap [end] = v 83 | 84 | __quicksort(array, index_remap, start, storeIdx-1) 85 | __quicksort(array, index_remap, storeIdx+1, end) 86 | } 87 | 88 | function quicksort (array, index_remap, start, end, i) 89 | { 90 | for (i=start; i <= end; ++i) 91 | index_remap [i] = i 92 | 93 | __quicksort(array, index_remap, start, end) 94 | } 95 | 96 | function quicksort_values (hash, remap_idx, 97 | array, remap, i, j, cnt) 98 | { 99 | cnt = 0 100 | for (i in hash) { 101 | ++cnt 102 | array [cnt] = hash [i] 103 | remap [cnt] = i 104 | } 105 | 106 | quicksort(array, remap_idx, 1, cnt) 107 | 108 | for (i=1; i <= cnt; ++i) { 109 | remap_idx [i] = remap [remap_idx [i]] 110 | } 111 | 112 | return cnt 113 | } 114 | 115 | function quicksort_indices (hash, remap_idx, 116 | array, i, cnt) 117 | { 118 | cnt = 0 119 | for (i in hash) { 120 | ++cnt 121 | array [cnt] = i 122 | } 123 | 124 | quicksort(array, remap_idx, 1, cnt) 125 | 126 | for (i=1; i <= cnt; ++i) { 127 | remap_idx [i] = array [remap_idx [i]] 128 | } 129 | 130 | return cnt 131 | } 132 | -------------------------------------------------------------------------------- /modules/readfile.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | #use "xgetline.awk" 9 | #use "xclose.awk" 10 | 11 | # =head2 readfile.awk 12 | # 13 | # =over 2 14 | # 15 | # =item I 16 | # 17 | # read entire file and return its content as a string 18 | # 19 | # =back 20 | # 21 | # See example/demo_readfile for the sample of usage 22 | # 23 | 24 | function readfile (fn, ret){ 25 | # Unfortunately there is no way portable accross all awk flavours 26 | # to read an entire file content by single 'getline' command. 27 | # This is why I use loop here. 28 | ret = "" 29 | 30 | while (xgetline(fn)){ 31 | if (ret == "") 32 | ret = __input 33 | else 34 | ret = ret "\n" __input 35 | } 36 | xclose(fn) 37 | 38 | return ret 39 | } 40 | -------------------------------------------------------------------------------- /modules/runcmd.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 runcmd.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # wrapper for system() function 15 | # that runs a command CMD with options OPTS and one filename FILE. 16 | # Unlike system(CMD " " OPTS " " FILE) the function runcmd1 handles 17 | # correctly FILE containing spaces, single quote, double quote, 18 | # tilde etc. 19 | # 20 | # =item I 21 | # 22 | # safe wrapper for 'runcmd1'. 23 | # awk exits with error if runcmd1() function failed. 24 | # 25 | # =back 26 | # 27 | 28 | #use "alt_assert.awk" 29 | #use "shquote.awk" 30 | 31 | function runcmd1 (cmd, opts, file){ 32 | return system(shquote(cmd) " " opts " " shquote(file)) 33 | } 34 | 35 | function xruncmd1 (cmd, opts, file){ 36 | assert(runcmd1(cmd, opts, file) == 0, "runcmd1() failed") 37 | } 38 | -------------------------------------------------------------------------------- /modules/shquote.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 shquote.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # transforms the string `str' by adding shell escape and 15 | # quoting characters to include it to the system() and popen() 16 | # functions as an argument, so that the arguments will have the 17 | # correct values after being evaluated by the shell. 18 | # 19 | # For example: 20 | # print shquote("file name.txt") 21 | # |- 'file name.txt' 22 | # print shquote("'") 23 | # |- \' 24 | # print shquote("Peter's") 25 | # |- 'Peter'\''s' 26 | # print shquote("*&;<>#~") 27 | # |- '*&;<>#~' 28 | # 29 | # =back 30 | # 31 | # This module was inspired by NetBSD shquote(3) 32 | # http://netbsd.gw.com/cgi-bin/man-cgi?shquote+3+NetBSD-current 33 | # and shquote(1) by Alan Barrett 34 | # http://ftp.sunet.se/pub/os/NetBSD/misc/apb/shquote.20080906/ 35 | # 36 | 37 | function shquote (str){ 38 | gsub(/'/, "'\\''", str) 39 | return "'" str "'" 40 | } 41 | -------------------------------------------------------------------------------- /modules/sort.awk: -------------------------------------------------------------------------------- 1 | 2 | # Written by Aleksey Cheusov , public domain 3 | # 4 | # This awk module is a part of RunAWK distribution, 5 | # http://sourceforge.net/projects/runawk 6 | # 7 | ############################################################ 8 | 9 | # =head2 sort.awk 10 | # 11 | # =over 2 12 | # 13 | # =item I 14 | # 15 | # Call either heapsort function from heapsort.awk (if 16 | # RUNAWK_SORTTYPE environment variable is "heapsort") or quicksort 17 | # from quicksort.awk (if RUNAWK_SORTTYPE is "quicksort"). 18 | # Sorttype defaults to "heapsort". 19 | # 20 | # =item I 21 | # 22 | # Call either heapsort_values function from heapsort.awk (if 23 | # RUNAWK_SORTTYPE environment variable is "heapsort") or 24 | # quicksort_values from quicksort.awk (if RUNAWK_SORTTYPE is 25 | # "quicksort"). Sorttype defaults to "heapsort". 26 | # 27 | # =item I 28 | # 29 | # Call either heapsort_indices function from heapsort.awk (if 30 | # RUNAWK_SORTTYPE environment variable is "heapsort") or 31 | # quicksort_indices from quicksort.awk (if RUNAWK_SORTTYPE is 32 | # "quicksort"). Sorttype defaults to "heapsort". 33 | # 34 | # =back 35 | # 36 | 37 | #use "abort.awk" 38 | #use "heapsort.awk" 39 | #use "quicksort.awk" 40 | 41 | BEGIN { 42 | if (!__sort_type) 43 | __sort_type = ENVIRON ["RUNAWK_SORTTYPE"] 44 | if (!__sort_type) 45 | __sort_type = "heapsort" 46 | } 47 | 48 | function sort (array, index_remap, start, end) 49 | { 50 | if (__sort_type == "heapsort") 51 | heapsort(array, index_remap, start, end); 52 | else if (__sort_type == "quicksort") 53 | quicksort(array, index_remap, start, end); 54 | else 55 | abort("Bad __sort_type in sort.awk") 56 | } 57 | 58 | function sort_values (src_hash, index_remap) 59 | { 60 | if (__sort_type == "heapsort") 61 | return heapsort_values(src_hash, index_remap); 62 | else if (__sort_type == "quicksort") 63 | return quicksort_values(src_hash, index_remap); 64 | else 65 | abort("Bad __sort_type in sort.awk") 66 | } 67 | 68 | function sort_indices (src_hash, index_remap) 69 | { 70 | if (__sort_type == "heapsort") 71 | return heapsort_indices(src_hash, index_remap); 72 | else if (__sort_type == "quicksort") 73 | return quicksort_indices(src_hash, index_remap); 74 | else 75 | abort("Bad __sort_type in sort.awk") 76 | } 77 | -------------------------------------------------------------------------------- /modules/str2regexp.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 str2regexp.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # returns a regular expression that matches given STRING 15 | # 16 | # =back 17 | # 18 | # For example: 19 | # print str2regexp("all special symbols: ^$(){}[].*+?|\\") 20 | # -| all special symbols: [^][$][(][)][{][}][[]\][.][*][+][?][|]\\ 21 | # 22 | 23 | #use "alt_assert.awk" 24 | 25 | function __runawk_mawk_bug_test (tmp){ 26 | # returns true if buggy MAWK 27 | tmp = "\\\\" 28 | gsub(/\\/, "\\\\", tmp) 29 | return (tmp != "\\\\\\\\") 30 | } 31 | 32 | BEGIN { 33 | __buggy_mawk = __runawk_mawk_bug_test() 34 | } 35 | 36 | function str2regexp (s){ 37 | gsub(/\[/, "---runawk-open-sq-bracket---", s) 38 | gsub(/\]/, "---runawk-close-sq-bracket---", s) 39 | 40 | gsub(/[?{}|()*+.$]/, "[&]", s) 41 | gsub(/\^/, "[\\^]", s) 42 | 43 | if (s ~ /\\/){ 44 | if (!__buggy_mawk){ 45 | # normal AWK 46 | gsub(/\\/, "\\\\", s) 47 | }else{ 48 | # MAWK /-( 49 | gsub(/\\/, "\\\\\\\\\\", s) 50 | } 51 | } 52 | 53 | gsub(/---runawk-open-sq-bracket---/, "\\[", s) 54 | gsub(/---runawk-close-sq-bracket---/, "\\]", s) 55 | 56 | return s 57 | } 58 | -------------------------------------------------------------------------------- /modules/tmpfile.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 tmpfile.awk 9 | # 10 | # This module provides a function `tmpfile' for generating temporary 11 | # filenames. All these filenames are under temporary directory created 12 | # (if necessary) by runawk(1) which is removed automatically during 13 | # normal exit or when runawk(1) reveives SIGINT, SIGQUIT, SIGTERM, 14 | # SIGHUP or SIGPIPE. 15 | # 16 | # =over 2 17 | # 18 | # =item I 19 | # 20 | # returns a temporary file name. 21 | # 22 | # =item I 23 | # 24 | # global variable that keeps tempdir created by runawk -t 25 | # 26 | # =back 27 | # 28 | # See example/demo_tmpfile for the sample of usage 29 | # 30 | 31 | #use "alt_assert.awk" 32 | 33 | BEGIN { 34 | runawk_tmpdir = ENVIRON ["_RUNAWK_TMPDIR"] 35 | assert(runawk_tmpdir != "", "_RUNAWK_TMPDIR is unset! This should not happen.") 36 | 37 | _runawk_tmpdir_cnt = 0 38 | } 39 | 40 | function tmpfile (){ 41 | return (runawk_tmpdir "/" _runawk_tmpdir_cnt++) 42 | } 43 | -------------------------------------------------------------------------------- /modules/tokenre.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 tokenre.awk 9 | # 10 | # By default AWK splits input lines into tokens according to regular 11 | # expression that defines "spaces" between tokens using special 12 | # variable FS. In many situations it is more useful to define regular 13 | # expressions for tokens themselves. This is what this module does. 14 | # 15 | # =over 2 16 | # 17 | # =item I 18 | # 19 | # extracts substrings from STRING 20 | # according to REGEXP from the left to the right and assigns $1, $2 21 | # etc. and NF variable. 22 | # 23 | # =item I 24 | # 25 | # Does the the same as `tokenre' but splits $0 instead. 26 | # 27 | # =item I 28 | # 29 | # The same as `tokenre' but ARR[1], ARR[2]... are assigned. 30 | # A number of extracted tokens is a return value. 31 | # 32 | # =item I 33 | # 34 | # global variable. If it is set to non-empty string, all input 35 | # lines are split automatically. 36 | # 37 | # =back 38 | # 39 | 40 | # See example/demo_tokenre for the sample of usage 41 | 42 | function tokenre (s, re){ 43 | NF = 0 44 | while (match(s, re)){ 45 | ++NF 46 | $NF = substr(s, RSTART, RLENGTH) 47 | s = substr(s, RSTART+RLENGTH) 48 | } 49 | } 50 | 51 | function tokenre0 (re){ 52 | tokenre($0, re) 53 | } 54 | 55 | function splitre (s, arr, re, cnt){ 56 | cnt = 0 57 | while (match(s, re)){ 58 | ++cnt 59 | arr [cnt] = substr(s, RSTART, RLENGTH) 60 | s = substr(s, RSTART+RLENGTH) 61 | } 62 | return cnt 63 | } 64 | 65 | function splitre0 (arr, re){ 66 | return splitre($0, arr, re) 67 | } 68 | 69 | { 70 | if (TRE != ""){ 71 | tokenre0(TRE) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /modules/trim.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 trim.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # Removes leading Tab and Space characters from STRING and returns 15 | # the result. 16 | # 17 | # =item I 18 | # 19 | # Removes Tab and Space characters at the end of STRING and returns 20 | # the result. 21 | # 22 | # =item I 23 | # 24 | # Replaces sequences of Tab and Space characters in STRING with REPL 25 | # and returns the result. If REPL is not specified, it defaults to 26 | # single Space character. 27 | # 28 | # =item I 29 | # 30 | # Equal to trim_l(trim_r(STRING)) 31 | # 32 | # =item I 33 | # 34 | # Equal to trim_l(trim_r(trim_c(STRING, REPL))) 35 | # 36 | # =back 37 | # 38 | # See example/demo_trim for the sample of usage 39 | # 40 | 41 | function trim_l (s){ 42 | sub(/^[ \t]+/, "", s) 43 | return s 44 | } 45 | 46 | function trim_r (s){ 47 | sub(/[ \t]+$/, "", s) 48 | return s 49 | } 50 | 51 | function trim_c (s, repl){ 52 | if (repl == "") 53 | repl = " " 54 | 55 | gsub(/[ \t][ \t]+/, repl, s) 56 | return s 57 | } 58 | 59 | function trim_lr (s){ 60 | sub(/[ \t]+$/, "", s) 61 | sub(/^[ \t]+/, "", s) 62 | return s 63 | } 64 | 65 | function trim_lrc (s, repl){ 66 | if (repl == "") 67 | repl = " " 68 | 69 | gsub(/[ \t][ \t]+/, repl, s) 70 | sub(/[ \t]+$/, "", s) 71 | sub(/^[ \t]+/, "", s) 72 | return s 73 | } 74 | -------------------------------------------------------------------------------- /modules/trim_in.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 trim_in.awk 9 | # 10 | # As the name of this module says (_in suffix) this module reads and 11 | # potentially changes input lines. 12 | # 13 | # Leading, ending spaces and/or spaces in the middle of input lines 14 | # are removed depending on TRIM variable. 15 | # TRIM values: 16 | # "l" - remove leading space characters 17 | # "r" - remove ending space characters 18 | # "c" - remove extra space characters in the middle of input lines 19 | # "lr" - See l and r 20 | # "lrc" - See l, r and c 21 | # "lc" - See l and c 22 | # "cr" - See c and r 23 | # By default TRIM variable is set to "lr". TRIM set to a single space 24 | # character means no trimming. 25 | # 26 | 27 | BEGIN { 28 | if (TRIM == ""){ 29 | TRIM = "lr" 30 | } 31 | } 32 | 33 | { 34 | if (index(TRIM, "c") > 0) 35 | gsub(/[ \t][ \t]+/, " ") 36 | if (index(TRIM, "l") > 0) 37 | sub(/^[ \t]+/, "") 38 | if (index(TRIM, "r") > 0) 39 | sub(/[ \t]+$/, "") 40 | } 41 | -------------------------------------------------------------------------------- /modules/xclose.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 xclose.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # safe wrapper for 'close'. 15 | # awk exits with error if close() function failed. 16 | # 17 | # =back 18 | # 19 | 20 | #use "alt_assert.awk" 21 | 22 | function xclose (fn){ 23 | assert(close(fn) == 0, "close(\"" fn "\") failed") 24 | } 25 | -------------------------------------------------------------------------------- /modules/xgetline.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 xgetline.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # Safe analog to 'getline < FILE' or 'getline' (if no FILE is specified). 15 | # 0 at the end means that input line is assigned to $0. 16 | # 17 | # =item I 18 | # 19 | # Safe analog to 'getline __input < FILE' and 'getline __input' 20 | # (if no FILE is specified) 21 | # 22 | # =back 23 | # 24 | # In both cases "safe" means that returned value is analysed and 25 | # if it is less than zero (file reading error happens) program will 26 | # be terminated emmidiately with appropriate error message sent to stderr. 27 | # Both functions return zero if end of file is reached or non-zero otherwise. 28 | # 29 | # Example: 30 | # while (xgetline("/etc/passwd")){ 31 | # print "user: " __input 32 | # } 33 | # 34 | 35 | #use "alt_assert.awk" 36 | 37 | function xgetline0 (fn, ret){ 38 | if (fn == ""){ 39 | ret = getline 40 | assert(ret >= 0, "getline failed") 41 | }else{ 42 | ret = (getline < fn) 43 | assert(ret >= 0, "getline < " fn " failed") 44 | } 45 | 46 | return (ret > 0) 47 | } 48 | 49 | function xgetline (fn, ret){ 50 | if (fn == ""){ 51 | ret = getline __input 52 | assert(ret >= 0, "getline failed") 53 | }else{ 54 | ret = (getline __input < fn) 55 | assert(ret >= 0, "getline < " fn " failed") 56 | } 57 | 58 | return (ret > 0) 59 | } 60 | -------------------------------------------------------------------------------- /modules/xsystem.awk: -------------------------------------------------------------------------------- 1 | # Written by Aleksey Cheusov , public domain 2 | # 3 | # This awk module is a part of RunAWK distribution, 4 | # http://sourceforge.net/projects/runawk 5 | # 6 | ############################################################ 7 | 8 | # =head2 xsystem.awk 9 | # 10 | # =over 2 11 | # 12 | # =item I 13 | # 14 | # safe wrapper for 'system'. 15 | # awk exits with error if system() function failed. 16 | # 17 | # =back 18 | # 19 | 20 | #use "alt_assert.awk" 21 | 22 | function xsystem (fn){ 23 | assert(system(fn) == 0, "system(\"" fn "\") failed") 24 | } 25 | -------------------------------------------------------------------------------- /runawk/Makefile: -------------------------------------------------------------------------------- 1 | ################################################## 2 | 3 | # default directory for creating temp files and dirs 4 | TEMPDIR = /tmp 5 | 6 | .if !defined(AWK_PROG) 7 | .if exists(/usr/xpg4/bin/awk) 8 | # Solaris' /usr/bin/awk sucks so much... :-( 9 | # /usr/xpg4/bin/awk sucks too but sucks less. 10 | # I'd recommend you use GNU awk, nawk from NetBSD cvs tree 11 | # or mawk-1.3.4 or later. 12 | AWK_PROG = /usr/xpg4/bin/awk 13 | .else 14 | MKC_REQUIRE_PROGS += awk 15 | AWK_PROG = ${PROG.awk} 16 | .endif 17 | .endif # !defined(AWK_PROG) 18 | 19 | STDIN_FILENAME?= - #/dev/stdin 20 | 21 | ################################################## 22 | 23 | WARNS ?= 4 24 | WARNERR ?= no 25 | 26 | PROG = runawk 27 | SRCS = runawk.c dynarray.c file_hier.c 28 | 29 | MAN = runawk.1 30 | 31 | MKC_FEATURES = strlcpy strlcat 32 | 33 | CFLAGS += -DAWK_PROG='"${AWK_PROG}"' 34 | CFLAGS += -DSTDIN_FILENAME='"${STDIN_FILENAME}"' 35 | CFLAGS += -DMODULESDIR='"${MODULESDIR}:${MODULESDIR}/gawk"' 36 | CFLAGS += -DRUNAWK_VERSION='"${VERSION}"' 37 | CFLAGS += -DTEMPDIR='"${TEMPDIR}"' 38 | 39 | CLEANFILES += *~ core* *.core ktrace* *.tmp tests/_* *.html1 *.cat1 *.1 40 | CLEANFILES += ChangeLog runawk.html _test.res 41 | 42 | .PHONY: _manpages 43 | _manpages: ${MAN} 44 | 45 | ################################################## 46 | 47 | .include 48 | -------------------------------------------------------------------------------- /runawk/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2014 Aleksey Cheusov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef _COMMON_H_ 25 | #define _COMMON_H_ 26 | 27 | #include 28 | #include 29 | 30 | #ifndef PATH_MAX 31 | #define PATH_MAX 4096 /* Some systems doesn't have PATH_MAX */ 32 | #endif 33 | 34 | #endif // _COMMON_H_ 35 | -------------------------------------------------------------------------------- /runawk/dynarray.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2014 Aleksey Cheusov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #include "common.h" 28 | 29 | #include "dynarray.h" 30 | 31 | void da_init (dynarray_t * array) 32 | { 33 | array->size = 0; 34 | array->allocated = 0; 35 | array->array = NULL; 36 | } 37 | 38 | void da_push (dynarray_t * array, char *item) 39 | { 40 | if (array->allocated == array->size){ 41 | array->allocated = array->allocated * 4 / 3 + 100; 42 | array->array = realloc ( 43 | array->array, array->allocated * sizeof (*array->array)); 44 | } 45 | 46 | array->array [array->size++] = item; 47 | } 48 | 49 | void da_push_dup (dynarray_t * array, const char *item) 50 | { 51 | char *dup = (item ? strdup (item) : NULL); 52 | da_push (array, dup); 53 | } 54 | 55 | void da_free_items (dynarray_t * array) 56 | { 57 | size_t i; 58 | for (i=0; i < array->size; ++i){ 59 | if (array->array [i]){ 60 | free ((void *) array->array [i]); 61 | array->array [i] = NULL; 62 | } 63 | } 64 | } 65 | 66 | void da_destroy (dynarray_t * array) 67 | { 68 | if (array->array) 69 | free (array->array); 70 | array->array = NULL; 71 | array->size = 0; 72 | array->allocated = 0; 73 | } 74 | -------------------------------------------------------------------------------- /runawk/dynarray.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2012 Aleksey Cheusov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef _DYNARRAY_H_ 25 | #define _DYNARRAY_H_ 26 | 27 | typedef struct { 28 | size_t size; 29 | size_t allocated; 30 | char **array; 31 | } dynarray_t; 32 | 33 | void da_init (dynarray_t * array); 34 | void da_push (dynarray_t * array, char *item); 35 | void da_push_dup (dynarray_t * array, const char *item); 36 | void da_destroy (dynarray_t * array); 37 | void da_free_items (dynarray_t * array); 38 | 39 | #endif /* _DYNARRAY_H_ */ 40 | -------------------------------------------------------------------------------- /runawk/file_hier.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2014 Aleksey Cheusov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "common.h" 32 | 33 | #include "file_hier.h" 34 | 35 | void file_hier ( 36 | const char *dir, 37 | void (*proc_file) (const char *fn), 38 | void (*proc_dir) (const char *fn)) 39 | { 40 | char buffer [PATH_MAX]; 41 | size_t dir_len = strlen (dir)+1; 42 | struct stat sb; 43 | struct dirent *dp; 44 | DIR *dirp; 45 | 46 | if (dir_len+1 > sizeof (buffer)){ 47 | return; 48 | } 49 | 50 | dirp = opendir(dir); 51 | if (dirp != NULL) { 52 | strlcpy (buffer, dir, sizeof (buffer)); 53 | strlcat (buffer, "/", sizeof (buffer)); 54 | 55 | while (dp = readdir(dirp), dp != NULL){ 56 | if (dp->d_name [0] == '.'){ 57 | if (dp->d_name [1] == 0 || 58 | (dp->d_name [1] == '.' && dp->d_name [2] == 0)) 59 | { 60 | /* ignore . and .. */ 61 | continue; 62 | } 63 | } 64 | 65 | buffer [dir_len] = 0; 66 | strlcat (buffer, dp->d_name, sizeof (buffer)); 67 | if (0 == lstat (buffer, &sb)){ 68 | if (S_ISDIR (sb.st_mode)){ 69 | file_hier (buffer, proc_file, proc_dir); 70 | }else{ 71 | proc_file (buffer); 72 | } 73 | }else{ 74 | fprintf (stderr, "stat(\"%s\") failed: %s\n", buffer, strerror (errno)); 75 | } 76 | } 77 | 78 | closedir (dirp); 79 | 80 | proc_dir (dir); 81 | }else{ 82 | fprintf (stderr, "opendir(\"%s\") failed: %s\n", dir, strerror (errno)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /runawk/file_hier.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Aleksey Cheusov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef _FILE_HIER_H_ 25 | #define _FILE_HIER_H_ 26 | 27 | void file_hier ( 28 | const char *dir, 29 | void (*proc_file) (const char *fn), 30 | void (*proc_dir) (const char *fn)); 31 | 32 | #endif /* _FILE_HIER_H_ */ 33 | -------------------------------------------------------------------------------- /runawk/runawk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2014 Aleksey Cheusov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "common.h" 36 | 37 | #include "dynarray.h" 38 | #include "file_hier.h" 39 | 40 | #ifndef BUFSIZ 41 | #define BUFSIZ 4096 42 | #endif 43 | 44 | #ifndef STDIN_FILENAME 45 | #define STDIN_FILENAME "/dev/stdin" 46 | #endif 47 | 48 | #ifndef AWK_PROG 49 | #define AWK_PROG "awk" 50 | #endif 51 | 52 | #ifndef ARRAY_SZ 53 | #define ARRAY_SZ 1000 54 | #endif 55 | 56 | #ifndef RUNAWK_VERSION 57 | #define RUNAWK_VERSION "x.y.z" 58 | #endif 59 | 60 | #ifndef TEMPDIR 61 | #define TEMPDIR "/tmp" 62 | #endif 63 | 64 | #ifndef MODULESDIR 65 | #define MODULESDIR "/usr/local/share/runawk" 66 | #endif 67 | 68 | static void usage (void) 69 | { 70 | puts ("\ 71 | runawk - wrapper for an AWK interpreter\n\ 72 | usage: runawk [OPTIONS] program_file [arguments...]\n\ 73 | runawk [OPTIONS] -e program [arguments...]\n\ 74 | OPTIONS:\n\ 75 | -h display this screen\n\ 76 | -V display version information\n\ 77 | -d debugging mode, list argv array for awk and\n\ 78 | do not run AWK interpreter\n\ 79 | -e program to run\n\ 80 | -v assign the value val to the variable var\n\ 81 | -f add awk_module to a program\n\ 82 | -t create a temporary directory and pass it to\n\ 83 | AWK interpreter subprocess\n\ 84 | "); 85 | } 86 | 87 | static const char *runawk_version = RUNAWK_VERSION; 88 | 89 | static void version (void) 90 | { 91 | printf ("runawk %s written by Aleksey Cheusov\n", runawk_version); 92 | } 93 | 94 | static int create_tmpdir = 0; 95 | 96 | static pid_t awk_pid = -1; 97 | 98 | static int killing_sig = 0; 99 | 100 | static int double_dash = 0; 101 | 102 | static char temp_fn [PATH_MAX] = "/tmp/runawk.XXXXXX"; 103 | static int temp_fn_created = 0; 104 | 105 | static char *temp_dir = NULL; 106 | 107 | static char *awkpath = NULL; 108 | static size_t awkpath_len = 0; 109 | 110 | static char cwd [PATH_MAX]; 111 | 112 | static const char *interp = AWK_PROG; 113 | static const char *sys_awkdir = MODULESDIR; 114 | 115 | static char *interp_var = NULL; 116 | 117 | static int debug = 0; 118 | 119 | static dynarray_t new_argv; 120 | static dynarray_t includes; 121 | 122 | static void remove_file (const char *fn) 123 | { 124 | if (unlink (fn)){ 125 | fprintf (stderr, "unlink(\"%s\") failed: %s\n", fn, strerror (errno)); 126 | } 127 | } 128 | 129 | static void remove_dir (const char *dir) 130 | { 131 | if (rmdir (dir)){ 132 | fprintf (stderr, "rmdir(\"%s\") failed: %s\n", dir, strerror (errno)); 133 | } 134 | } 135 | 136 | static void clean_and_exit (int status) 137 | { 138 | const char *keep = getenv ("RUNAWK_KEEPTMP"); 139 | 140 | if (!keep && temp_fn_created) 141 | unlink (temp_fn); 142 | 143 | if (temp_dir){ 144 | if (!keep) 145 | file_hier (temp_dir, remove_file, remove_dir); 146 | 147 | if (temp_dir) 148 | free (temp_dir); 149 | } 150 | 151 | if (awkpath) 152 | free (awkpath); 153 | 154 | if (killing_sig) 155 | exit (128 + killing_sig); 156 | else 157 | exit (status); 158 | } 159 | 160 | static void mktempdir (void) 161 | { 162 | const char *dir = getenv ("RUNAWK_TMPDIR"); 163 | if (!dir) 164 | dir = getenv ("TMPDIR"); 165 | if (!dir) 166 | dir = TEMPDIR; 167 | 168 | temp_dir = tempnam (dir, "awk."); 169 | if (!temp_dir){ 170 | perror ("tempnam(3) failed"); 171 | clean_and_exit (52); 172 | } 173 | 174 | if (mkdir (temp_dir, 0700)){ 175 | perror ("mkdir(3) failed"); 176 | clean_and_exit (53); 177 | } 178 | } 179 | 180 | static char *xstrdup (const char *s) 181 | { 182 | char *ret = strdup (s); 183 | if (!ret){ 184 | perror ("strdup(3) failed"); 185 | clean_and_exit (33); 186 | } 187 | 188 | return ret; 189 | } 190 | 191 | static void *xmalloc (size_t size) 192 | { 193 | char *ret = malloc (size); 194 | if (!ret){ 195 | perror ("malloc(3) failed"); 196 | clean_and_exit (33); 197 | } 198 | 199 | return ret; 200 | } 201 | 202 | static void xputenv (char *s) 203 | { 204 | if (putenv (s)){ 205 | perror ("putenv(3) failed"); 206 | clean_and_exit (43); 207 | } 208 | } 209 | 210 | static const char *search_file (const char *dir, const char *name) 211 | { 212 | /* search in AWKPATH env. */ 213 | const char *curr_dir = NULL; 214 | char buf [PATH_MAX]; 215 | size_t i; 216 | 217 | /* dir argument */ 218 | snprintf (buf, sizeof (buf), "%s/%s", dir, name); 219 | if (!access (buf, R_OK)){ 220 | return xstrdup (buf); 221 | } 222 | 223 | /* AWKPATH env. and system paths */ 224 | for (i = 0; i < awkpath_len; ++i){ 225 | if (awkpath [i] && (i == 0 || awkpath [i-1] == 0)){ 226 | curr_dir = awkpath + i; 227 | snprintf (buf, sizeof (buf), "%s/%s", curr_dir, name); 228 | if (!access (buf, R_OK)){ 229 | return xstrdup (buf); 230 | } 231 | } 232 | } 233 | 234 | return NULL; 235 | } 236 | 237 | static void invalid_directive (int num, const char *line, const char *fn) 238 | { 239 | char *copy = xstrdup (line); 240 | char * nl = strchr (copy, '\n'); 241 | 242 | if (nl) 243 | *nl = 0; 244 | 245 | fprintf (stderr, 246 | "error: invalid directive at line #%d,\n line=`%s`\n file=`%s`\n", 247 | num, copy, fn); 248 | 249 | free (copy); 250 | } 251 | 252 | static int add_file_uniq (const char *dir, const char *name, int safe_use); 253 | 254 | static char *extract_qstring ( 255 | const char *line, int line_num, const char *fn, const char *s) 256 | { 257 | const char *p = NULL; 258 | const char *n = NULL; 259 | char *ret = NULL; 260 | size_t len = 0; 261 | 262 | p = s + strspn (s, " "); 263 | if (*p != '"'){ 264 | invalid_directive (line_num, line, fn); 265 | clean_and_exit (37); 266 | } 267 | 268 | ++p; 269 | 270 | n = strpbrk (p, "\"\n"); 271 | if (!n || *n == '\n'){ 272 | invalid_directive (line_num, line, fn); 273 | clean_and_exit (37); 274 | } 275 | 276 | len = n - p; 277 | ret = xmalloc (len+1); 278 | memcpy ((void *) ret, (const void *) p, len); 279 | ret [len] = 0; 280 | 281 | return ret; 282 | } 283 | 284 | static int add_file_uniq_safe (const char *dir, const char *name) 285 | { 286 | char buffer [4000]; 287 | const char *fn; 288 | 289 | if (name [0] == '~'){ 290 | snprintf (buffer, sizeof (buffer), "%s%s", getenv ("HOME"), name+1); 291 | fn = buffer; 292 | }else{ 293 | fn = name; 294 | } 295 | 296 | return add_file_uniq (dir, fn, 1); 297 | } 298 | 299 | typedef enum { 300 | qstr_spaces, 301 | qstr_str 302 | } qstr_state_t; 303 | 304 | static void list_of_qstrings ( 305 | const char *dir, 306 | int line_num, 307 | const char *fn, 308 | const char *line, 309 | const char *s, 310 | int (*fun) (const char *dir, const char *name)) 311 | { 312 | char buffer [2000]; 313 | const char *p; 314 | const char *start = NULL; 315 | size_t len; 316 | int state = qstr_spaces; 317 | int success = 0; 318 | 319 | for (p=s; *p; ++p){ 320 | switch (state){ 321 | case qstr_spaces: 322 | switch (*p){ 323 | case ' ': 324 | case '\t': 325 | case '\r': 326 | break; 327 | case '\n': 328 | goto eol; 329 | case '"': 330 | state = qstr_str; 331 | start = p+1; 332 | break; 333 | default: 334 | invalid_directive (line_num, line, fn); 335 | clean_and_exit (51); 336 | } 337 | break; 338 | case qstr_str: 339 | switch (*p){ 340 | case '"': 341 | len = p-start; 342 | if (len+1 > sizeof (buffer)) 343 | len = sizeof (buffer) - 1; 344 | 345 | memcpy (buffer, start, len); 346 | buffer [len] = 0; 347 | state = qstr_spaces; 348 | 349 | success = (success || fun (dir, buffer)); 350 | break; 351 | default: 352 | break; 353 | } 354 | break; 355 | } 356 | } 357 | 358 | eol: 359 | if (state == qstr_str){ 360 | invalid_directive (line_num, line, fn); 361 | clean_and_exit (52); 362 | } 363 | } 364 | 365 | static void scan_buffer ( 366 | const char *name, const char *dir, 367 | const char *buffer, off_t sz, 368 | int allow_spaces) 369 | { 370 | char *env_str = NULL; 371 | const char *p = buffer; 372 | int line_num = 1; 373 | 374 | for (; sz--; ++p){ 375 | if (*p == '\n') 376 | ++line_num; 377 | 378 | if (*p != '#') 379 | continue; 380 | 381 | if (p != buffer){ 382 | if (allow_spaces){ 383 | switch (p[-1]){ 384 | case '\n': 385 | case ' ': 386 | case '\t': 387 | break; 388 | default: 389 | continue; 390 | } 391 | }else{ 392 | if (p [-1] != '\n') 393 | continue; 394 | } 395 | } 396 | 397 | if (!strncmp (p, "#use ", 5)){ 398 | add_file_uniq (dir, extract_qstring (p, line_num, name, p + 5), 0); 399 | }else if (!strncmp (p, "#safe-use ", 10)){ 400 | list_of_qstrings (dir, line_num, name, p, p+10, add_file_uniq_safe); 401 | }else if (!strncmp (p, "#interp ", 8)){ 402 | interp = extract_qstring (p, line_num, name, p + 8); 403 | }else if (!strncmp (p, "#interp-var ", 12)){ 404 | interp_var = extract_qstring (p, line_num, name, p + 12); 405 | }else if (!strncmp (p, "#env ", 5)){ 406 | env_str = (char *) extract_qstring (p, line_num, name, p + 5); 407 | xputenv (env_str); 408 | } 409 | } 410 | } 411 | 412 | static int scan_file (const char *name, int safe_use) 413 | { 414 | char dir [PATH_MAX]; 415 | FILE *fd = NULL; 416 | size_t len = 0; 417 | struct stat stat_buf; 418 | char *buffer = NULL; 419 | off_t file_size = 0; 420 | size_t n = 0; 421 | 422 | /**/ 423 | len = strlen (name); 424 | strncpy (dir, name, sizeof (dir)); 425 | while (len--){ 426 | if (dir [len] == '/'){ 427 | dir [len] = 0; 428 | break; 429 | } 430 | } 431 | 432 | /**/ 433 | if (stat (name, &stat_buf)){ 434 | if (safe_use) 435 | return 0; 436 | 437 | fprintf (stderr, "stat(\"%s\") failed: %s\n", name, strerror (errno)); 438 | clean_and_exit (35); 439 | } 440 | file_size = stat_buf.st_size; 441 | 442 | fd = fopen (name, "r"); 443 | if (!fd){ 444 | fprintf (stderr, "fopen(%s) failed: %s\n", name, strerror (errno)); 445 | clean_and_exit (35); 446 | } 447 | 448 | buffer = xmalloc (file_size + 1); 449 | 450 | n = fread (buffer, 1, file_size, fd); 451 | if (n < (size_t) file_size){ 452 | perror ("fread(3) failed"); 453 | clean_and_exit (35); 454 | } 455 | buffer [file_size] = 0; 456 | 457 | scan_buffer (name, dir, buffer, file_size, 0); 458 | 459 | free (buffer); 460 | 461 | if (fclose (fd)){ 462 | perror ("fclose(3) failed"); 463 | clean_and_exit (36); 464 | } 465 | 466 | return 1; 467 | } 468 | 469 | static void add_buffer (const char *buffer, size_t len) 470 | { 471 | int fd = -1; 472 | 473 | /* recursive snanning for #xxx directives */ 474 | scan_buffer ("", "-", buffer, len, 1); 475 | 476 | if (includes.size == 0 && !double_dash){ 477 | da_push_dup (&new_argv, buffer); 478 | }else{ 479 | fd = mkstemp (temp_fn); 480 | temp_fn_created = 1; 481 | if (fd == -1){ 482 | perror ("mkstemp(3) failed"); 483 | clean_and_exit (40); 484 | } 485 | if (write (fd, buffer, len) != (ssize_t) len){ 486 | perror ("write(2) failed"); 487 | clean_and_exit (40); 488 | } 489 | if (close (fd)){ 490 | perror ("close(2) failed"); 491 | clean_and_exit (40); 492 | } 493 | 494 | /* add to queue */ 495 | da_push_dup (&new_argv, "-f"); 496 | da_push_dup (&new_argv, temp_fn); 497 | da_push_dup (&includes, temp_fn); 498 | } 499 | } 500 | 501 | static int add_file (const char *dir, const char *name, int safe_use) 502 | { 503 | const char *new_name = NULL; 504 | size_t len; 505 | 506 | if (name [0] != '/'){ 507 | /* name -> path */ 508 | new_name = search_file (dir, name); 509 | if (!new_name){ 510 | if (safe_use) 511 | return 0; 512 | 513 | fprintf (stderr, "Cannot find module `%s`, check AWKPATH environment variable\n", name); 514 | clean_and_exit (34); 515 | } 516 | name = new_name; 517 | } 518 | 519 | /* recursive snanning for #xxx directives */ 520 | if (scan_file (name, safe_use)){ 521 | /* add to queue */ 522 | len = strlen (name); 523 | if (len > 11 && !strcmp (name+len-12, "/tmpfile.awk")){ 524 | create_tmpdir = 1; 525 | } 526 | 527 | da_push_dup (&new_argv, "-f"); 528 | da_push_dup (&new_argv, name); 529 | da_push_dup (&includes, name); 530 | return 1; 531 | }else{ 532 | return 0; 533 | } 534 | } 535 | 536 | static int add_file_uniq ( 537 | const char *dir, const char *name, int safe_use) 538 | { 539 | size_t i; 540 | const char *p; 541 | const char *inc; 542 | 543 | for (i=0; i < includes.size; ++i){ 544 | inc = includes.array [i]; 545 | p = strstr (inc, name); 546 | 547 | if (p && (p == inc || (p [-1] == '/' && p [strlen (p)] == 0))){ 548 | return 1; 549 | } 550 | } 551 | 552 | return add_file (dir, name, safe_use); 553 | } 554 | 555 | static void handler (int sig) 556 | { 557 | killing_sig = sig; 558 | if (awk_pid != -1){ 559 | kill (awk_pid, sig); 560 | } 561 | } 562 | 563 | static void set_sig_handler (void) 564 | { 565 | static const int sigs [] = { 566 | SIGINT, SIGQUIT, SIGTERM, 567 | SIGHUP, SIGPIPE 568 | }; 569 | 570 | struct sigaction sa; 571 | size_t i; 572 | 573 | sa.sa_handler = handler; 574 | sigemptyset (&sa.sa_mask); 575 | sa.sa_flags = 0; 576 | for (i=0; i < sizeof (sigs)/sizeof (sigs [0]); ++i){ 577 | int sig = sigs [i]; 578 | sigaction (sig, &sa, NULL); 579 | } 580 | } 581 | 582 | static void putenv_RUNAWK_MODx (void) 583 | { 584 | size_t i; 585 | char buf [30 + PATH_MAX]; 586 | 587 | /* RUNAWK_MODC */ 588 | snprintf (buf, sizeof (buf), "RUNAWK_MODC=%u", 589 | (unsigned) includes.size); 590 | xputenv (xstrdup (buf)); 591 | 592 | /* RUNAWK_MODV */ 593 | for (i=0; i < includes.size; ++i){ 594 | snprintf (buf, sizeof (buf), "RUNAWK_MODV_%u=%s", 595 | (unsigned) i, includes.array [i]); 596 | xputenv (xstrdup (buf)); 597 | } 598 | } 599 | 600 | int main (int argc, char **argv) 601 | { 602 | char buffer [4000]; 603 | const char *progname = NULL; 604 | int child_status = 0; 605 | const char *env_interp = getenv ("RUNAWK_AWKPROG"); 606 | const char *prog_specified = NULL; 607 | const char *awkpath_env = NULL; 608 | int c; 609 | 610 | size_t i; 611 | size_t j; 612 | 613 | da_init (&new_argv); 614 | da_init (&includes); 615 | 616 | set_sig_handler (); 617 | 618 | /* environment RUNAWK_AWKPROG overrides compile-time option */ 619 | if (env_interp){ 620 | interp = env_interp; 621 | } 622 | 623 | if (argc == 0){ 624 | usage (); 625 | return 30; 626 | } 627 | 628 | /* AWKPATH env. */ 629 | awkpath_env = getenv ("AWKPATH"); 630 | if (!awkpath_env) 631 | awkpath_env = ""; 632 | 633 | awkpath_len = strlen (awkpath_env) + 1 + strlen (sys_awkdir); 634 | awkpath = xmalloc (awkpath_len + 1); 635 | snprintf (awkpath, awkpath_len + 1, "%s:%s", awkpath_env, sys_awkdir); 636 | 637 | for (j=0; j < awkpath_len; ++j){ 638 | if (awkpath [j] == ':'){ 639 | awkpath [j] = 0; 640 | } 641 | } 642 | 643 | /* cwd */ 644 | if (!getcwd (cwd, sizeof (cwd))){ 645 | perror ("getcwd (3) failed"); 646 | clean_and_exit (32); 647 | } 648 | 649 | da_push_dup (&new_argv, NULL); /* progname */ 650 | 651 | while (c = getopt (argc, argv, "+de:f:F:htTVv:"), c != EOF){ 652 | switch (c){ 653 | case 'd': 654 | debug = 1; 655 | break; 656 | case 'e': 657 | if (prog_specified){ 658 | fprintf (stderr, "multiple -e are not allowed\n"); 659 | clean_and_exit (39); 660 | } 661 | 662 | prog_specified = optarg; 663 | break; 664 | case 'F': 665 | da_push_dup (&new_argv, "-F"); 666 | da_push_dup (&new_argv, optarg); 667 | break; 668 | case 'f': 669 | add_file (cwd, optarg, 0); 670 | break; 671 | case 'h': 672 | usage (); 673 | clean_and_exit (0); 674 | break; 675 | case 't': 676 | create_tmpdir = 1; 677 | break; 678 | case 'T': 679 | da_push_dup (&new_argv, "-F"); 680 | da_push_dup (&new_argv, "\t"); 681 | break; 682 | case 'v': 683 | da_push_dup (&new_argv, "-v"); 684 | da_push_dup (&new_argv, optarg); 685 | break; 686 | case 'V': 687 | version (); 688 | clean_and_exit (0); 689 | break; 690 | default: 691 | usage (); 692 | exit (1); 693 | } 694 | } 695 | 696 | argv += optind; 697 | argc -= optind; 698 | 699 | progname = interp; 700 | 701 | /* */ 702 | if (prog_specified){ 703 | add_buffer (prog_specified, strlen (prog_specified)); 704 | }else{ 705 | /* program_file */ 706 | if (argc < 1){ 707 | usage (); 708 | clean_and_exit (30); 709 | } 710 | 711 | add_file (cwd, *argv, 0); 712 | progname = *argv; 713 | #if 0 714 | setprogname (*argv); 715 | setproctitle (*argv); 716 | #endif 717 | --argc; 718 | ++argv; 719 | } 720 | 721 | /* AWK interpreter name */ 722 | if (interp_var) 723 | interp_var = getenv (interp_var); 724 | 725 | if (interp_var && interp_var [0]) 726 | interp = interp_var; 727 | 728 | /* exec */ 729 | new_argv.array [0] = xstrdup (progname); 730 | 731 | if (includes.size) 732 | da_push_dup (&new_argv, "--"); 733 | 734 | for (i=0; i < (size_t) argc; ++i){ 735 | da_push_dup (&new_argv, argv [i]); 736 | } 737 | 738 | da_push_dup (&new_argv, NULL); 739 | 740 | putenv_RUNAWK_MODx (); 741 | 742 | /* create temporary directory */ 743 | if (create_tmpdir){ 744 | mktempdir (); 745 | snprintf (buffer, sizeof (buffer), "_RUNAWK_TMPDIR=%s", temp_dir); 746 | xputenv (buffer); 747 | } 748 | 749 | /**/ 750 | if (debug){ 751 | for (i=0; i < new_argv.size - 1; ++i){ 752 | printf ("new_argv [%u] = %s\n", (unsigned) i, new_argv.array [i]); 753 | } 754 | }else{ 755 | awk_pid = fork (); 756 | switch (awk_pid){ 757 | case -1: 758 | perror ("fork(2) failed"); 759 | clean_and_exit (42); 760 | break; 761 | 762 | case 0: 763 | /* child */ 764 | execvp (interp, (char *const *) new_argv.array); 765 | fprintf (stderr, "running '%s' failed: %s\n", interp, strerror (errno)); 766 | exit (1); 767 | break; 768 | 769 | default: 770 | /* parent */ 771 | waitpid (-1, &child_status, 0); 772 | 773 | da_free_items (&new_argv); 774 | da_destroy (&new_argv); 775 | 776 | da_free_items (&includes); 777 | da_destroy (&includes); 778 | 779 | if (killing_sig){ 780 | clean_and_exit (0); 781 | }else if (WIFSIGNALED (child_status)){ 782 | clean_and_exit (128 + WTERMSIG (child_status)); 783 | }else if (WIFEXITED (child_status)){ 784 | clean_and_exit (WEXITSTATUS (child_status)); 785 | }else{ 786 | clean_and_exit (200); 787 | } 788 | } 789 | } 790 | 791 | clean_and_exit (0); 792 | return 0; /* this should not happen but fixes gcc warning */ 793 | } 794 | -------------------------------------------------------------------------------- /runawk/runawk.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | runawk - wrapper for AWK interpreter 4 | 5 | =head1 SYNOPSIS 6 | 7 | B I<[options]> I 8 | 9 | B I<-e> I 10 | 11 | =head1 MOTIVATION 12 | 13 | After years of using AWK for programming I've found that despite of 14 | its simplicity and limitations AWK is good enough for scripting a wide 15 | range of different tasks. AWK is not as poweful as their bigger 16 | counterparts like Perl, Ruby, TCL and others but it has their own 17 | advantages like compactness, simplicity and availability on almost all 18 | UNIX-like systems. I personally also like its data-driven nature and 19 | token orientation, very useful techniques for text processing 20 | utilities. 21 | 22 | Unfortunately awk interpreters lacks some important features and 23 | sometimes do not work as good as they could do. 24 | 25 | Problems I see (some of them, of course) 26 | 27 | =over 2 28 | 29 | =item 1 30 | 31 | AWK lacks support for modules. Even if I create small programs, I 32 | often want to use functions created earlier and already used in 33 | other scripts. That is, it whould great to organise functions into 34 | so called libraries (modules). 35 | 36 | =item 2 37 | 38 | In order to pass arguments to C<#!/usr/bin/awk -f> script (not to awk 39 | interpreter), it is necessary to prepend a list of 40 | arguments with -- (two minus signes). In my view, this looks badly. 41 | Also such behaviour violates POSIX/SUS "Utility Syntax Guidelines". 42 | 43 | Example: 44 | 45 | awk_program: 46 | 47 | #!/usr/bin/awk -f 48 | 49 | BEGIN { 50 | for (i=1; i < ARGC; ++i){ 51 | printf "ARGV [%d]=%s\n", i, ARGV [i] 52 | } 53 | } 54 | 55 | Shell session: 56 | 57 | % awk_program --opt1 --opt2 58 | /usr/bin/awk: unknown option --opt1 ignored 59 | 60 | /usr/bin/awk: unknown option --opt2 ignored 61 | 62 | % awk_program -- --opt1 --opt2 63 | ARGV [1]=--opt1 64 | ARGV [2]=--opt2 65 | % 66 | 67 | In my opinion I script should work like this 68 | 69 | % awk_program --opt1 --opt2 70 | ARGV [1]=--opt1 71 | ARGV [2]=--opt2 72 | % 73 | 74 | =item 3 75 | 76 | When C<#!/usr/bin/awk -f> script handles arguments (options) and wants 77 | to read from stdin, it is necessary to add 78 | /dev/stdin (or `-') as a last argument explicitly. 79 | 80 | Example: 81 | 82 | awk_program: 83 | 84 | #!/usr/bin/awk -f 85 | 86 | BEGIN { 87 | if (ARGV [1] == "--flag"){ 88 | flag = 1 89 | ARGV [1] = "" # to not read file named "--flag" 90 | } 91 | } 92 | 93 | { 94 | print "flag=" flag " $0=" $0 95 | } 96 | 97 | Shell session: 98 | 99 | % echo test | awk_program -- --flag 100 | % echo test | awk_program -- --flag /dev/stdin 101 | flag=1 $0=test 102 | % 103 | 104 | Ideally I should work like this 105 | 106 | % echo test | awk_program --flag 107 | flag=1 $0=test 108 | % 109 | 110 | =item 4 111 | 112 | igawk(1) which is shipped with GNU awk can not be used in shebang. 113 | On most (all?) UNIXes scripts beginning with 114 | 115 | #!/usr/local/bin/igawk -f 116 | 117 | will not work. 118 | 119 | =back 120 | 121 | B was created to solve all these problems 122 | 123 | =head1 OPTIONS 124 | 125 | =over 6 126 | 127 | =item B<-d> 128 | 129 | Turn on a debugging mode. 130 | 131 | =item B<-e> I 132 | 133 | Specify program. If I<-e> is not specified, the AWK code is read from 134 | I. 135 | 136 | =item B<-f> I 137 | 138 | Activate I. This works the same way as 139 | 140 | #use "awk_module.awk" 141 | 142 | directive in the code. Multiple B<-f> options are allowed. 143 | 144 | =item B<-F> I 145 | 146 | Set the input field separator FS to the regular expression I. 147 | 148 | =item B<-h> 149 | 150 | Display help information. 151 | 152 | =item B<-t> 153 | 154 | If this option is applied, a temporary directory is created by 155 | B and path to it is passed to B child process. Temporary 156 | directory is created under ${RUNAWK_TMPDIR} (if it is set), or ${TMPDIR} 157 | (if it is set) or /tmp directory otherwise. 158 | If I<#use "tmpfile.awk"> is detected 159 | in a program this option is activated automatically. 160 | 161 | =item B<-T> 162 | 163 | Set FS to TAB character. This is equivalent to B<-F'\t'> 164 | 165 | =item B<-V> 166 | 167 | Display version information. 168 | 169 | =item B<-v> I=I 170 | 171 | Assign the value I to the variable I 172 | before execution of the program begins. 173 | 174 | =back 175 | 176 | =head1 DETAILS/INTERNALS 177 | 178 | =head2 Standalone script 179 | 180 | Under UNIX-like OS-es you can use B 181 | by beginning your script with 182 | 183 | #!/usr/local/bin/runawk 184 | 185 | line or something like this instead of 186 | 187 | #!/usr/bin/awk -f 188 | 189 | or similar. 190 | 191 | =head2 AWK modules 192 | 193 | In order to activate modules you should add them into awk script like this 194 | 195 | #use "module1.awk" 196 | #use "module2.awk" 197 | 198 | that is the line that specifies module name is treated as a comment line 199 | by normal AWK interpreter but is processed by B especially. 200 | 201 | Unless you run B with option B<-e>, I<#use> must begin with 202 | column 0, that is no spaces or tabs symbols are allowed before it and 203 | no symbols are allowed between I<#> and I. 204 | 205 | Also note that AWK modules can also "use" another modules and so forth. 206 | All them are collected in a depth-first order 207 | and each one is added to the list of 208 | awk interpreter arguments prepanded with -f option. 209 | That is I<#use> directive is *NOT* similar to I<#include> in 210 | C programming language, 211 | runawk's module code is not inserted into the place of I<#use>. 212 | Runawk's modules are closer to Perl's "use" command. 213 | In case some module is mentioned more than once, only one -f 214 | will be added for it, i.e duplications are removed automatically. 215 | 216 | Position of I<#use> directive in a source file does matter, i.e. 217 | the earlier module is mentioned, the earlier -f will be generated for it. 218 | 219 | Example: 220 | 221 | file prog: 222 | #!/usr/local/bin/runawk 223 | 224 | #use "A.awk" 225 | #use "B.awk" 226 | #use "E.awk" 227 | 228 | PROG code 229 | ... 230 | 231 | file B.awk: 232 | #use "A.awk" 233 | #use "C.awk" 234 | B code 235 | ... 236 | 237 | file C.awk: 238 | #use "A.awk" 239 | #use "D.awk" 240 | 241 | C code 242 | ... 243 | 244 | A.awk and D.awk don't contain #use directive 245 | 246 | If you run 247 | 248 | runawk prog file1 file2 249 | 250 | or 251 | 252 | /path/to/prog file1 file2 253 | 254 | the following command 255 | 256 | awk -f A.awk -f D.awk -f C.awk -f B.awk -f E.awk -f prog -- file1 file2 257 | 258 | will actually run. 259 | 260 | You can check this by running 261 | 262 | runawk -d prog file1 file2 263 | 264 | =head2 Module search strategy 265 | 266 | Modules are first searched in a directory where main 267 | program (or module in which #use directive is specified) is placed. 268 | If it is not found there, then 269 | AWKPATH environment variable is 270 | checked. AWKPATH keeps a colon separated 271 | list of search directories. 272 | Finally, module is searched in system runawk modules directory, 273 | by default PREFIX/share/runawk but this can be changed at compile time. 274 | 275 | An absolute path to the module can also be specified. 276 | 277 | =head2 Program as an argument 278 | 279 | Like some other interpreters 280 | B can obtain the script from a command line like this 281 | 282 | /path/to/runawk -e ' 283 | #use "alt_assert.awk" 284 | 285 | { 286 | assert($1 >= 0 && $1 <= 10, "Bad value: " $1) 287 | 288 | # your code below 289 | ... 290 | }' 291 | 292 | B can also be used for writing oneliners 293 | 294 | runawk -f abs.awk -e 'BEGIN {print abs(-1)}' 295 | 296 | =head2 Selecting a preferred AWK interpreter 297 | 298 | For some reason you may prefer one AWK interpreter or another. The 299 | reason may be efficiency for a particular task, useful but not 300 | standard extensions or enything else. To tell B what AWK 301 | interpreter to use, one can use I<#interp> directive 302 | 303 | file prog: 304 | #!/usr/local/bin/runawk 305 | 306 | #use "A.awk" 307 | #use "B.awk" 308 | 309 | #interp "/usr/pkg/bin/nbawk" 310 | 311 | # your code here 312 | ... 313 | 314 | Note that I<#interp> directive should also begin with column 0, 315 | no spaces are allowed before it and between I<#> and I. 316 | 317 | Sometimes it also makes sense to give users ability to select their 318 | preferred AWK interpreter without changing the source code. In 319 | B it is possible using special directive I<#interp-var> which 320 | sets an environment variable name assignable by user that specifies an 321 | AWK interpreter. For example, the following script 322 | 323 | file foobar: 324 | #!/usr/bin/env runawk 325 | 326 | #interp-var "FOOBAR_AWK" 327 | 328 | BEGIN { 329 | print "This is a FooBar application" 330 | } 331 | 332 | can be run as 333 | 334 | env FOOBAR_AWK=mawk foobar 335 | 336 | or just 337 | 338 | foobar 339 | 340 | In the former case B will be used as AWK interpreter, in the 341 | latter -- the default AWK interpreter. 342 | 343 | =head2 Using existing modules only 344 | 345 | In UNIX world it is common practise to write configuration files in a 346 | programming language of the application. That is, if application is 347 | written in Bourne shell, configuration files for such application are 348 | often written in Bourne as well. Using RunAWK one can do the same for 349 | applications written in AWK. For example, the following code will use 350 | ~/.foobarrc file if it exists otherwise /etc/foobar.conf will be used 351 | if it exists. 352 | 353 | file foobar: 354 | #!/usr/bin/env runawk 355 | 356 | #safe-use "~/.foobarrc" "/etc/foobar.conf" 357 | 358 | BEGIN { 359 | print foo, bar, baz 360 | } 361 | 362 | file ~/.foobarrc: 363 | BEGIN { 364 | foo = "foo10" 365 | bar = "bar20" 366 | baz = 123 367 | } 368 | 369 | Of course, I<#safe-use> directive may be used for other purposes as 370 | well. I<#safe-use> directive accepts as much modules as you want, but 371 | at most one can be included using awk option -f, others are silently 372 | ignored, also note that modules are analysed from left to 373 | right. Leading tilde in the module name is replaced with user's home 374 | directory. Another example: 375 | 376 | file foobar: 377 | #!/usr/bin/env runawk 378 | 379 | #use "/usr/share/foobar/default.conf" 380 | #safe-use "~/.foobarrc" "/etc/foobar.conf" 381 | 382 | your code is here 383 | 384 | Here the default settings are set in /usr/share/foobar/default.conf, 385 | and configuration files (if any) are used for overriding them. 386 | 387 | =head2 Setting environment 388 | 389 | In some cases you may want to run AWK interpreter with a 390 | specific environment. For example, your script may be oriented to 391 | process ASCII text only. In this case you can run AWK with LC_CTYPE=C 392 | environment and use regexp ranges. 393 | 394 | B provides I<#env> directive for this. String inside double quotes 395 | is passed to L libc function. 396 | 397 | Example: 398 | 399 | file prog: 400 | #!/usr/local/bin/runawk 401 | 402 | #env "LC_ALL=C" 403 | 404 | $1 ~ /^[A-Z]+$/ { # A-Z is valid if LC_CTYPE=C 405 | print $1 406 | } 407 | 408 | =head1 EXIT STATUS 409 | 410 | If AWK interpreter exits normally, B exits with its exit 411 | status. If AWK interpreter was killed by signal, B 412 | exits with exit status 128+signal. 413 | 414 | =head1 ENVIRONMENT 415 | 416 | =over 6 417 | 418 | =item I 419 | 420 | Colon separated list of directories where B modules are searched. 421 | 422 | =item I 423 | 424 | Sets the path to the AWK interpreter, used by default, 425 | i.e. this variable overrides the compile-time default. 426 | Note that #interp directive overrides this. 427 | 428 | 429 | =item I 430 | 431 | If set, temporary files are not deleted. 432 | 433 | =back 434 | 435 | =head1 AUTHOR 436 | 437 | Copyright (c) 2007-2014 Aleksey Cheusov 438 | 439 | =head1 BUGS/FEEDBACK 440 | 441 | Please send any comments, questions, bug reports etc. to me by e-mail 442 | or register them at sourceforge project home. Feature requests are 443 | also welcomed. 444 | 445 | =head1 HOME 446 | 447 | L 448 | 449 | =head1 SEE ALSO 450 | L 451 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | DIFF_PROG ?= diff -u 2 | 3 | TESTTEMPDIR = ${.CURDIR}/.. 4 | .export TESTTEMPDIR 5 | 6 | .PHONY: test_one 7 | test_one: 8 | @echo 'running tests...'; \ 9 | export SRCDIR=${.CURDIR}/..; \ 10 | if { ${.CURDIR}/test.sh > ${.OBJDIR}/_test.res; \ 11 | ${DIFF_PROG} ${.CURDIR}/test.out ${.OBJDIR}/_test.res; };\ 12 | then echo ' succeeded'; \ 13 | else echo ' failed'; false; \ 14 | fi 15 | 16 | # test using all available awk version except mawk which 17 | # is definitely buggy, oawk is also NOT supported 18 | 19 | AWK_PROGS ?= /usr/bin/awk /usr/bin/nawk /usr/bin/gawk \ 20 | /usr/pkg/bin/nawk /usr/pkg/bin/gawk /usr/pkg/bin/nbawk #\ 21 | # /usr/pkg/heirloom/bin/posix/awk /usr/pkg/heirloom/bin/posix2001/awk 22 | # /usr/pkg/heirloom/bin/nawk 23 | # /usr/bin/mawk /usr/pkg/bin/mawk /usr/pkg/bin/mawk-uxre 24 | 25 | .PHONY: all 26 | all: 27 | .for awk in ${AWK_PROGS} 28 | @if test -x ${awk}; then \ 29 | echo testing ${awk}; \ 30 | export RUNAWK_AWKPROG=${awk}; cd ${.CURDIR} && ${MAKE} test_one; \ 31 | echo ''; \ 32 | fi 33 | .endfor 34 | 35 | CLEANFILES += ${TESTTEMPDIR}/temp1.awk ${TESTTEMPDIR}/temp2.awk ${TESTTEMPDIR}/temp3.awk 36 | CLEANDIRFILES += _test.res 37 | 38 | .include 39 | -------------------------------------------------------------------------------- /test/mods1/module1.1.awk: -------------------------------------------------------------------------------- 1 | #use "module1.2.awk" 2 | #use "module1.3.awk" 3 | 4 | BEGIN { 5 | print "Hello World1.1" 6 | } 7 | -------------------------------------------------------------------------------- /test/mods1/module1.2.awk: -------------------------------------------------------------------------------- 1 | BEGIN { 2 | print "Hello World1.2" 3 | } 4 | -------------------------------------------------------------------------------- /test/mods1/module1.3.awk: -------------------------------------------------------------------------------- 1 | #use "module1.2.awk" 2 | 3 | BEGIN { 4 | print "Hello World1.3" 5 | } 6 | -------------------------------------------------------------------------------- /test/mods1/test_modinfo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runawk 2 | 3 | #use "module1.1.awk" 4 | #use "module2.1.awk" 5 | #use "module2.3.awk" 6 | 7 | #use "modinfo.awk" 8 | 9 | BEGIN { 10 | print "MODC=" MODC 11 | 12 | for (i=0; i < MODC; ++i){ 13 | printf "MODV [%d]=%s\n", i, MODV [i] 14 | } 15 | 16 | print "MODMAIN=" MODMAIN 17 | } 18 | -------------------------------------------------------------------------------- /test/mods2/module2.1.awk: -------------------------------------------------------------------------------- 1 | #use "module2.2.awk" 2 | 3 | BEGIN { 4 | print "Hello World 2.1" 5 | } 6 | -------------------------------------------------------------------------------- /test/mods2/module2.2.awk: -------------------------------------------------------------------------------- 1 | BEGIN { 2 | print "Hello World 2.2" 3 | } 4 | -------------------------------------------------------------------------------- /test/mods2/module2.3.awk: -------------------------------------------------------------------------------- 1 | #use "module2.2.awk" 2 | 3 | BEGIN { 4 | print "Hello World 2.3" 5 | } 6 | -------------------------------------------------------------------------------- /test/mods3/failed1.awk: -------------------------------------------------------------------------------- 1 | #use 2 | -------------------------------------------------------------------------------- /test/mods3/failed2.awk: -------------------------------------------------------------------------------- 1 | 2 | #interp "trtrtr 3 | 4 | -------------------------------------------------------------------------------- /test/mods3/failed3.awk: -------------------------------------------------------------------------------- 1 | 2 | 3 | #env 'LC_ALL=C' 4 | -------------------------------------------------------------------------------- /test/mods3/failed4.awk: -------------------------------------------------------------------------------- 1 | #interp "/invalid/path" 2 | 3 | BEGIN { 4 | print "Hello World!" 5 | } 6 | -------------------------------------------------------------------------------- /test/mods3/test5.awk: -------------------------------------------------------------------------------- 1 | #env "LC_ALL=C" 2 | #env "FOO=BAR" 3 | 4 | BEGIN { 5 | print ("y" ~ /^[a-z]$/) 6 | print ("Y" ~ /^[a-z]$/) 7 | print ("z" ~ /^[a-z]$/) 8 | print ("Z" ~ /^[a-z]$/) 9 | 10 | print ("env FOO=" ENVIRON ["FOO"]) 11 | print ("env TESTVAR=" ENVIRON ["TESTVAR"]) 12 | } 13 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | LC_ALL=C 6 | export LC_ALL 7 | 8 | cd ${SRCDIR}/test 9 | 10 | unify_paths (){ 11 | sed -e "s,/.*/test/,/ROOT/," \ 12 | -e 's,/tmp/runawk[.]......,/tmp/runawk.NNNNNN,' \ 13 | -e 's,new_argv \[0\] = .*awk.*,new_argv [0] = awk,' \ 14 | -e 's,ARGV\[0\]=.*awk.*,ARGV[0]=awk,' \ 15 | -e 's,FILENAME=-$,FILENAME=,' \ 16 | -e 's,/tmp/awk[.][^/]*/,/tmp/awk.XXXX/,' 17 | } 18 | 19 | runtest_header (){ 20 | echo '--------------------------------------------------' 21 | printf ' ------ args: %s\n' "$*" | unify_paths 22 | } 23 | 24 | runtest_main (){ 25 | $OBJDIR_runawk/runawk "$@" 2>&1 | grep -v '/_test_program' | unify_paths 26 | } 27 | 28 | runtest (){ 29 | runtest_header "$@" 30 | runtest_main "$@" 31 | } 32 | 33 | runtest_nostderr (){ 34 | runtest_header "$@" 35 | # runtest_main "$@" '2>/dev/null' 36 | $OBJDIR_runawk/runawk "$@" 2>/dev/null | grep -v '/_test_program' | unify_paths 37 | } 38 | 39 | PATH=${SRCDIR}/examples:${SRCDIR}/a_getopt:${OBJDIR_runawk}:$PATH 40 | export PATH 41 | 42 | #################### 43 | 44 | AWKPATH=${SRCDIR}/modules 45 | export AWKPATH 46 | 47 | trap 'rm -f _test_program _test.tmp' 0 1 2 3 15 48 | touch _test_program 49 | 50 | runtest -d _test_program 51 | runtest -d _test_program --long-option 52 | runtest -d _test_program -o=file 53 | runtest -d _test_program fn1 fn2 54 | runtest -d -f abs.awk -e 'BEGIN {print abs(-123), abs(234); exit}' 55 | runtest -d -f alt_assert.awk -e 'BEGIN {exit}' 56 | runtest -V | awk 'NR <= 2 {print $0} NR == 3 {print "xxx"}' 57 | runtest -h | awk 'NR <= 3' 58 | runtest -e 'BEGIN {print "Hello World"}' 59 | runtest -v one=1 -e 'BEGIN {print one} {print "unbelievably"}' /dev/null 60 | runtest -v two=2 -e 'BEGIN {print two}' 61 | runtest -e 'BEGIN {print "Hello World"}' /dev/null 62 | runtest -v var1=123 -v var2=321 -e 'BEGIN {print var1, var2}' 63 | 64 | unset AWKPATH || true 65 | 66 | ################## use 67 | cat > _test.tmp <&1 | sed 's/failed:.*/failed/' 122 | } 123 | 124 | # success 125 | interp_var_test 126 | 127 | # failure 128 | INTERP_VAR_TEST=/invalid/path 129 | export INTERP_VAR_TEST 130 | interp_var_test 131 | unset INTERP_VAR_TEST 132 | 133 | #################### 134 | runtest_header '#safe-use directive' 135 | 136 | touch $TESTTEMPDIR/temp1.awk 137 | touch $TESTTEMPDIR/temp2.awk 138 | 139 | safe_use_test (){ 140 | echo "--- #safe-use test: ---" 141 | $OBJDIR_runawk/runawk -d -e " 142 | #safe-use \"$1\" \"$2\" \"$3\" 143 | 144 | BEGIN { 145 | print 123 146 | exit 0 147 | } 148 | " | 149 | unify_paths | 150 | sed -e 's,/[^ ]*/temp1.awk,/path/temp1.awk,' \ 151 | -e 's,/[^ ]*/temp2.awk,/path/temp2.awk,' 152 | } 153 | 154 | safe_use_test "/bad/path1" "$TESTTEMPDIR/temp1.awk" "/bad/path2" 155 | safe_use_test "$TESTTEMPDIR/temp1.awk" "/bad/path2" "/bad/path1" 156 | safe_use_test "/bad/path1" "$TESTTEMPDIR/temp3.awk" "$TESTTEMPDIR/temp2.awk" 157 | safe_use_test "/bad/path3" "/bad/path2" "/bad/path1" 158 | 159 | #################### 160 | runtest -e ' 161 | #use "/invalid/path/file.awk" 162 | ' 163 | 164 | #################### 165 | runtest -d -e ' 166 | BEGIN { 167 | for (i=0; i < ARGC; ++i){ 168 | printf "ARGV [%s]=%s\n", i, ARGV [i] 169 | } 170 | }' -- -a -b -c 171 | 172 | #################### 173 | runtest -e ' 174 | BEGIN { 175 | for (i=1; i < ARGC; ++i){ 176 | printf "ARGV [%s]=%s\n", i, ARGV [i] 177 | } 178 | }' -- file1 file2 file3 179 | 180 | #################### 181 | runtest -d -e ' 182 | #env "LC_ALL=C" 183 | #env "FOO2=bar2" 184 | 185 | BEGIN { 186 | ... 187 | }' 188 | 189 | runtest -e ' 190 | #env "LC_ALL=C" 191 | #env "FOO2=bar2" 192 | 193 | BEGIN { 194 | print ("y" ~ /^[a-z]$/) 195 | print ("Y" ~ /^[a-z]$/) 196 | print ("z" ~ /^[a-z]$/) 197 | print ("Z" ~ /^[a-z]$/) 198 | 199 | print ("env FOO2=" ENVIRON ["FOO2"]) 200 | } 201 | ' 202 | 203 | runtest -e 'BEGIN {print 1}' -e 'BEGIN {print 2}' 204 | 205 | #################### 206 | AWKPATH=${SRCDIR}/modules 207 | export AWKPATH 208 | runtest -d -e ' 209 | #use "alt_assert.awk" 210 | 211 | BEGIN { 212 | print "Hello world!" 213 | abort("just a test", 7) 214 | } 215 | ' /dev/null 216 | 217 | runtest -e ' 218 | #use "alt_assert.awk" 219 | 220 | BEGIN { 221 | abort("just a test", 7) 222 | } 223 | ' 224 | 225 | runtest -f abs.awk -e 'BEGIN {print abs(-123), abs(234); exit}' 226 | 227 | runtest -f alt_assert.awk -e 'BEGIN {assert(0, "Hello assert!")}' 228 | 229 | #################### 230 | printf ' a b c \na\t b \tc \n' | 231 | runtest -Te ' 232 | { 233 | for (i=1; i <= NF; ++i){ 234 | printf "$%d=%s\n", i, $i 235 | } 236 | }' 237 | 238 | ############################################################ 239 | AWKPATH=${SRCDIR}/modules:${SRCDIR}/modules/gawk 240 | export AWKPATH 241 | #################### multisub 242 | runtest ../examples/demo_multisub 243 | 244 | #################### tokenre 245 | runtest ../examples/demo_tokenre ../examples/demo_tokenre.in 246 | runtest ../examples/demo_tokenre2 ../examples/demo_tokenre2.in 247 | runtest ../examples/demo_tokenre3 ../examples/demo_tokenre3.in 248 | runtest ../examples/demo_tokenre4 ../examples/demo_tokenre3.in 249 | 250 | #################### splitre 251 | runtest ../examples/demo_splitre ../examples/demo_tokenre2.in 252 | 253 | #################### getopt 254 | runtest ../examples/demo_alt_getopt -h - 255 | runtest ../examples/demo_alt_getopt --help 256 | runtest ../examples/demo_alt_getopt -h --help -v --verbose -V -o 123 -o234 257 | runtest ../examples/demo_alt_getopt --output 123 --output 234 -n 999 -n9999 --len 5 --fake /dev/null 258 | runtest ../examples/demo_alt_getopt -hVv -- -1 -2 -3 259 | runtest ../examples/demo_alt_getopt --fake -v -- -1 -2 -3 260 | runtest ../examples/demo_alt_getopt - -1 -2 -3 261 | runtest ../examples/demo_alt_getopt --fake -v - -1 -2 -3 262 | runtest ../examples/demo_alt_getopt -1 -2 -3 263 | runtest ../examples/demo_alt_getopt -hvV 264 | runtest ../examples/demo_alt_getopt -ho 123 265 | runtest ../examples/demo_alt_getopt -hoV 123 266 | runtest ../examples/demo_alt_getopt --unknown 267 | runtest ../examples/demo_alt_getopt --output='file.out' -nNNN --len=LENGTH 268 | runtest ../examples/demo_alt_getopt --output --file-- 269 | 270 | #################### modinfo 271 | runtest ../examples/demo_modinfo 272 | 273 | #################### has_suffix 274 | runtest ../examples/demo_has_suffix ../examples/demo_has_suffix.in 275 | 276 | #################### has_prefix 277 | runtest ../examples/demo_has_prefix ../examples/demo_has_prefix.in 278 | 279 | #################### dirname 280 | runtest ../examples/demo_dirname 281 | runtest ../examples/demo_dirname /path/to/file 282 | runtest ../examples/demo_dirname file.txt 283 | runtest ../examples/demo_dirname / 284 | runtest ../examples/demo_dirname /dir/ 285 | 286 | #################### basename 287 | runtest ../examples/demo_basename 288 | runtest ../examples/demo_basename /path/to/file 289 | runtest ../examples/demo_basename file.txt 290 | runtest ../examples/demo_basename / 291 | runtest ../examples/demo_basename /dir/ 292 | 293 | runtest ../examples/demo_basename /path/to/file.txt .txt 294 | runtest ../examples/demo_basename file.txt .txt 295 | runtest ../examples/demo_basename / .txt 296 | runtest ../examples/demo_basename /dir/ .txt 297 | 298 | runtest ../examples/demo_basename /path/to/file.txt .log 299 | runtest ../examples/demo_basename file.txt .log 300 | runtest ../examples/demo_basename / .log 301 | runtest ../examples/demo_basename /dir/ .log 302 | 303 | #################### shquote 304 | runtest ../examples/demo_shquote ../examples/demo_shquote.in 305 | 306 | #################### alt_join 307 | runtest_nostderr ../examples/demo_alt_join 308 | 309 | #################### readfile 310 | runtest ../examples/demo_readfile 311 | runtest ../examples/demo_readfile ../examples/demo_readfile.in 312 | 313 | #################### power_getopt 314 | runtest ../examples/demo_power_getopt 315 | echo ////////////////////////////////////////////////// 316 | runtest ../examples/demo_power_getopt -ffss --flag --long-flag | 317 | sort 318 | echo ////////////////////////////////////////////////// 319 | runtest ../examples/demo_power_getopt -h 2>&1 | 320 | sort 321 | echo ////////////////////////////////////////////////// 322 | runtest ../examples/demo_power_getopt -f --long-flag -s -F123 --FLAG=234 --LONG-FLAG 345 -S 456 2>&1 | 323 | sort 324 | echo ////////////////////////////////////////////////// 325 | runtest ../examples/demo_power_getopt -f --long-flag -s -F123 --FLAG=234 --LONG-FLAG 345 -S 456 -P arg1 '' arg3 arg4 '' 2>&1 | 326 | sort 327 | echo ////////////////////////////////////////////////// 328 | 329 | runtest ../examples/demo_power_getopt2 --help 330 | runtest ../examples/demo_power_getopt2 -? 331 | 332 | #################### runcmd 333 | runtest ../examples/demo_runcmd 334 | 335 | #################### heapsort 336 | runtest_header ../examples/demo_heapsort2 337 | for i in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0; do 338 | runtest_main ../examples/demo_heapsort2 339 | done 340 | 341 | runtest ../examples/demo_heapsort < ../examples/demo_heapsort.in 342 | runtest ../examples/demo_heapsort3 < ../examples/demo_heapsort3.in 343 | runtest ../examples/demo_heapsort4 < ../examples/demo_heapsort4.in 344 | 345 | #################### quicksort 346 | runtest_header ../examples/demo_quicksort2 347 | for i in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0; do 348 | runtest_main ../examples/demo_quicksort2 349 | done 350 | 351 | runtest ../examples/demo_quicksort < ../examples/demo_heapsort.in 352 | runtest ../examples/demo_quicksort3 < ../examples/demo_heapsort3.in 353 | 354 | #################### fieldwidths 355 | runtest ../examples/demo_fieldwidths < ../examples/demo_fieldwidths.in 356 | 357 | #################### tmpfile 358 | runtest ../examples/demo_tmpfile 359 | 360 | #################### trim 361 | runtest ../examples/demo_trim ../examples/demo_trim.in 362 | 363 | #################### trim_in 364 | runtest ../examples/demo_trim_in ../examples/demo_trim.in 365 | runtest -v TRIM=' ' ../examples/demo_trim_in ../examples/demo_trim.in 366 | runtest -v TRIM='c' ../examples/demo_trim_in ../examples/demo_trim.in 367 | 368 | #################### glob.awk 369 | runtest ../examples/demo_glob2ere 370 | 371 | #################### backslash_in 372 | runtest ../examples/demo_backslash_in ../examples/demo_backslash_in.in 373 | 374 | #################### ini 375 | runtest ../examples/demo_ini : ../examples/demo_ini.in 376 | runtest ../examples/demo_ini '' ../examples/demo_ini.in 377 | 378 | #################### alt_getopt(1) 379 | test_process_args (){ 380 | alt_getopt \ 381 | 'v|verbose' 'verbose=1' \ 382 | 'h help' help \ 383 | 'fake' fake_flag=1 \ 384 | '=len' len= \ 385 | '=o output' output= \ 386 | '=m msg' "msg=" \ 387 | 'V version' "echo 'alt_getopt-0-1-0 written by Aleksey Cheusov '" \ 388 | =n number= \ 389 | -- "$@" 390 | } 391 | 392 | runtest_header 'alt_getopt #1' 393 | test_process_args \ 394 | -h --help -v --verbose -V -o 123 -o234 --output 'file with spaces' -n 999 -n9999 --len 5 --fake \ 395 | -hVv --len 10 --len=100 -m "Aleksey's cat is female" \ 396 | --msg="backslashes (\) is not a problem too" -- -1 -2 -3 397 | 398 | #################### demo_alt_getopt.sh 399 | runtest_header 'demo_alt_getopt.sh #1' 400 | ../examples/demo_alt_getopt.sh 401 | 402 | #################### demo_alt_getopt.sh 403 | runtest_header 'demo_alt_getopt.sh #1.5' 404 | ../examples/demo_alt_getopt.sh arg1 arg2 arg3 405 | 406 | #################### demo_alt_getopt.sh 407 | runtest_header 'demo_alt_getopt.sh #2' 408 | ../examples/demo_alt_getopt.sh -n123 -m "Aleksey's cat is female" arg1 409 | 410 | #################### demo_alt_getopt.sh 411 | runtest_header 'demo_alt_getopt.sh #3' 412 | ../examples/demo_alt_getopt.sh --len=123 -m "Aleksey's cat is female" arg1 arg2 413 | 414 | #################### demo_alt_getopt.sh 415 | runtest_header 'demo_alt_getopt.sh #4' 416 | ../examples/demo_alt_getopt.sh --len 123 -f -m "Aleksey's cat is female" -- -a1 -a2 417 | 418 | #################### demo_alt_getopt.sh 419 | runtest_header 'demo_alt_getopt.sh #5' 420 | ../examples/demo_alt_getopt.sh -n123 --o file.txt -fhFvvv --len=100 \ 421 | -o/path/to/file.out 2>&1 422 | 423 | #################### demo_alt_getopt.sh 424 | runtest_header 'demo_alt_getopt.sh #5' 425 | ../examples/demo_alt_getopt.sh -n 123 --o file.txt -fhFvq --len=100 \ 426 | -o/path/to/file.out 2>&1 427 | 428 | #################### demo_alt_getopt.sh 429 | runtest_header 'demo_alt_getopt2.sh #1' 430 | ../examples/demo_alt_getopt2.sh -h 2>&1 431 | #################### demo_alt_getopt.sh 432 | runtest_header 'demo_alt_getopt2.sh #2' 433 | ../examples/demo_alt_getopt2.sh --version 2>&1 434 | #################### demo_alt_getopt.sh 435 | runtest_header 'demo_alt_getopt2.sh #3' 436 | ../examples/demo_alt_getopt2.sh -o '/path/to/file with spaces.txt' 2>&1 437 | #################### demo_alt_getopt.sh 438 | runtest_header 'demo_alt_getopt2.sh #4' 439 | ../examples/demo_alt_getopt2.sh -ofile.txt arg1 arg2 arg3 2>&1 440 | #################### demo_alt_getopt.sh 441 | runtest_header 'demo_alt_getopt2.sh #5' 442 | ../examples/demo_alt_getopt2.sh -- -ofile.txt arg1 2>&1 443 | #################### demo_alt_getopt.sh 444 | runtest_header 'demo_alt_getopt2.sh #6' 445 | ../examples/demo_alt_getopt2.sh -- -o file.txt arg1 2>&1 446 | #################### demo_alt_getopt.sh 447 | runtest_header 'demo_alt_getopt2.sh #7' 448 | ../examples/demo_alt_getopt2.sh -o file.txt arg1 2>&1 449 | 450 | #################### minmax 451 | runtest_header 'demo_minmax #1' 452 | ../examples/demo_minmax 1>&2 453 | 454 | #################### ftrans 455 | runtest ../examples/demo_ftrans /dev/null 456 | runtest ../examples/demo_ftrans \ 457 | ../examples/demo_readfile.in 458 | runtest ../examples/demo_ftrans \ 459 | ../examples/demo_readfile.in ../examples/demo_shquote.in \ 460 | ../examples/demo_fieldwidths.in 461 | runtest ../examples/demo_ftrans <<'EOF' 462 | foo 463 | bar 464 | baz 465 | EOF 466 | #################### -F 467 | runtest_header '-F #1' 468 | $OBJDIR_runawk/runawk -F: -d -e '{print}' | unify_paths 469 | runtest_header '-F #2' 470 | $OBJDIR_runawk/runawk -d -F: -e '{print}' | unify_paths 471 | runtest_header '-F #3' 472 | echo '1:2:3:4' | $OBJDIR_runawk/runawk -F: -v a=1 -e '{print "a=" a, NF ":" $1, $2, $3, $4}' 473 | echo '1:2:3:4' | $OBJDIR_runawk/runawk -v b=2 -F: -e '{print "b=" b, NF ":" $1, $2, $3, $4}' 474 | 475 | #################### xargs + runawk 476 | runtest_header 'xargs + runawk #1' 477 | awk 'BEGIN {for (i=0; i < 100000; ++i){print (i % 1000)}}' | 478 | xargs $OBJDIR_runawk/runawk -e 'BEGIN {for (i=1; i < ARGC; ++i) print ARGV [i]}' | 479 | awk '{cnt += $1} END {print cnt}' 480 | 481 | #################### exitnow.awk 482 | runtest_header 'exitnow.awk #1' 483 | echo '' | $OBJDIR_runawk/runawk -f exitnow.awk \ 484 | -e 'BEGIN {exitnow(0)} END {print "Bad!"}' | unify_paths 485 | 486 | #################### exitnow.awk 487 | demo_io_input_data (){ 488 | cat << 'EOF' 489 | ../Makefile 490 | /nonexistent 491 | /dev/null 492 | . 493 | symlink.tmp 494 | fifo.tmp 495 | EOF 496 | } 497 | 498 | ln -s test.sh symlink.tmp 499 | mkfifo fifo.tmp 500 | demo_io_input_data | runtest ../examples/demo_io 501 | rm symlink.tmp fifo.tmp 502 | --------------------------------------------------------------------------------