unixcw-3.6.0/0000755000175000017500000000000014001105772010033 500000000000000unixcw-3.6.0/install-sh0000755000175000017500000003546314000344554011774 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2014-09-12.12; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) # $RANDOM is not portable (e.g. dash); use it when possible to # lower collision chance tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 # As "mkdir -p" follows symlinks and we work in /tmp possibly; so # create the $tmpdir first (and fail if unsuccessful) to make sure # that nobody tries to guess the $tmpdir name. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: unixcw-3.6.0/compile0000755000175000017500000001624514000344554011343 00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: unixcw-3.6.0/NEWS0000644000175000017500000007142714001105427010462 00000000000000version 3.6.0 / 2021-01-17 libcw: - libcw no longer uses signals (and SIGALRM in particular) for any internal purpose. Internal communication between generator and queue has been re-implemented using other mechanism: pthread_cond_wait() and pthread_cond_broadcast(). - in relation to the above, in order to avoid calling of pthread_cond_wait() and pthread_cond_broadcast() in signal handlers, the SIG_IGN and SIG_DFL values registered with cw_register_signal_handler() are completely ignored. It is strongly advised to avoid any libcw cleanup or calls to exit() in signal handlers (registered with either with cw_register_signal_handler() or with more direct methods). - enabling ALSA's configuration of HW period size. libcw's ALSA code now tries to configure HW period size to be smaller than in previous versions of code. Normally the period size could be in range of hundreds of frames, now it's possible to have the period size of tens, or low hundreds of frames. Decreased period size should lead to better behaviour of ALSA's writei() function: the function should now properly block during write of samples to ALSA handle in all circumstances. Correct blocking behaviour of writei() should lead to correct behaviour of libcw's internal keying mechanism, responsible e.g. for calling keying callbacks registered by programs using libcw (such as cwdaemon). Such callbacks will be now called at proper time intervals that match duration of enqueued dots/dashes/spaces. Initial report of incorrect behaviour, initial debugging, and confirmation of a fix come from Matthew M5EVT. Thanks Matthew! - ALSA period size (in samples) used by applications (cw, cwcp, xcwcp) can be specified in command line through --alsa-period-size command line arg. - modifying PulseAudio parameters to decrease XRUNS and improve behaviour of callback registered by client code in library. Integrating these two commits from Matthew M5EVT https://github.com/m5evt/unixcw-3.5.1/commit/2d5491a461587ac4686e2d1b897619c98be05c9e https://github.com/m5evt/unixcw-3.5.1/commit/c86785b595a6d711aae915150df2ccb848ace05c The result is a better behaviour of PulseAudio sound sink, a bit similar to ALSA improvements mentioned above. Thanks again, Matthew! - Behaviour of cw_lookup_phonetic() library function has been modified: the function now accepts NULL as a second argument (output buffer). - Several items in libcw_debug.h are marked as deprecated. These items should be used only internally, during development of libcw, and should not be used by client code. They will be removed from the header in the future. - Declaration of cw_debug_print_flags() has been removed from libcw_debug.h header. The function was not defined in the library, so no one could have used the function. - Function cw_generator_set_tone_slope() from libcw.h is now marked as deprecated. The function was/is unusable because there is no way that client code can pass a cw_gen_t variable as first argument. - Replacing some usages of double type with float type. There is very little (or maybe even nothing) in the library that would require precision or range of double. - Adding new function: cw_generator_remove_last_character(). The function removes last character enqueued to generator, it can be called multiple times to remove a group of characters. The function can be used e.g. in reaction to Backspace character pressed in User Interface. This functionality was implemented by Zoltan Csahok and merged here: https://sourceforge.net/p/unixcw/code/merge-requests/1/, but it has been rewritten a bit: removing of character must be explicit, through the function call. The initial solution was affecting which characters are classified by libcw as valid and which aren't, and it may have broken some legacy code. Also using Backspace to remove a character is a User Interface concept, which in cwcp and xcwcp is handled explicitly by UI code, not implicitly by library code. Therefore I decided to rework the initial solution and add the new function to library. - modifications to receiver functions: cw_receive_character(), cw_receive_representation(), cw_start_receive_tone() and cw_end_receive_tone() are now actually setting errno to EINVAL (as described in documentation) when they detect that input timestamp (when the timestamp is non-NULL) is in some way invalid. cw_receive_buffer_dot() and cw_receive_buffer_dash() set additional value of errno (EINVAL) when they detect that input timestamp (when the timestamp is non-NULL) is in some way invalid. Please review information in man page about errno values set by these functions. - cw_wait_for_tone_queue_critical() now sets errno to EINVAL and returns CW_FAILURE if function's argument is negative. - there is a hard limit on length of name of sound device: 127 characters (+1 for terminating NUL). - fixing Debian bug #979113 (https://bugs.debian.org/979113). - library soname/version changed from 6.6.1 to 7.0.0. xcwcp: - program's command line option "--sound" has been changed to "--system", for consistency with similar option in other unixcw programs. Plans for next release: - improve quality and quantity of tests of libcw functions. - fix some of FIXMEs and do some of TODOs that don't break compatibility. - do full review of cwgen's code. - introduce fully automatic tests of cwgen's output. - perhaps improve PulseAudio parameters. ---------------------------------------------------------------------------- version 3.5.1 / 2017-02-XX libcw: - fixing a bug in libcw/generator that may have lead to a key being permanently in "down" state upon silencing a generator. If cwdaemon using libcw with NULL or Console output was generating tones from user input, and received ESC-4 escape request ("Abort currently sent message"), it was possible that while the message was interrupted, the key remained in "down" state. The fix in libcw makes sure that upon request to stop generating tones, the key always goes up, regardless of audio output type. The bug was reported and investigated, and fix was provided by Csahok Zoltan. Many thanks Zoli! - Library soname/version changed from 6.5.1 to 6.6.1. ---------------------------------------------------------------------------- version 3.5.0 / 2015-10-06 libcw: - adding few more small tests. - trying to fix a problem of short space. The problem occurs when client application has registered low-tone-queue callback, the threshold for the callback is set to 1, and a single end-of-word space has been enqueued by client application. When the eow space is enqueued as a single tone-queue tone (or even as two tones) ("short space"), older versions of libcw may miss the event of passing of tone queue level from 2 to 1 and will not call the callback. - Library soname/version changed from 6.4.1 to 6.5.1. xcwcp - porting the application from Qt4 to Qt5. ---------------------------------------------------------------------------- version 3.4.2 / 2015-06-17 libcw: - fixing one possible cause of segfaults in generator code (segfaults occurred when pthread_kill() was called with invalid thread ID in some specific circumstances). - fixing another problem with libcw: when a generator is created, then is *not* started, and then is deleted, libcw called pthread_kill(&id, ...) on uninitialized id in cw_gen_delete(). This caused a program to crash. - fixing problems with OSS and generator modules that surfaced during tests on Alpine Linux. - expanding unit tests. - libcw is now compiled with -DNDEBUG flag by default (asserts are "off"). The asserts are still "on" by default in "unit tests" libcw. Asserts are switched "on" in base libcw with "./configure --enable-dev". - cw_generator_set_tone_slope(): some corner cases of handling function's arguments were cleared in function's top level comments, and implementation has been updated accordingly. - Library soname/version changed from 6.3.1 to 6.4.1. ---------------------------------------------------------------------------- version 3.4.1 / 2015-01-02 libcw: - The remainder of code from libcw.c has been moved to respective module files. New module file has been created: libcw_rec.c - it contains receiver code. I think that number of modules won't change anymore. libcw.c has been removed, all functionality is now implemented by module files. - A number of unit test functions has been moved from libcw_test_public.c to respective module files. These functions can be executed independently, so there is no reason to keep them in libcw_test_public.c. They can be kept in module files, close to tested library functions. libcw_test_public.c is now less difficult to work with. The moved test functions are now executed while building "make check" target, so they should be called more often, increasing the chances of catching problems early. - Data sets for some of receiver's unit tests are now generated by helper functions. This allowed me to increase size of the data sets and increase variation of the data (both for fixed speed receiving and for adaptive receiving). The data sets are basically timing tables used to drive the receiver - to test main functions of the receiver, responsible for accepting mark/space events from client code. The increased size and variation of the test data gives me more confidence that the core functionality of the receiver works as expected. - If you run libcw in debugging mode and you depend on Receiver State names (strings starting with RS_) printed by library's debug messages, you may want to check changed values of RS enum and corresponding strings on top of libcw_rec.c - they have been changed. - Some changes have been made to library functions (mainly in generator and receiver modules) to ensure that the code separated between modules can be compiled and that it works as expected. Since receiver code has been put in a separate file, it was possible to review and refactor the receiver code a bit. No functionality of the library has been changed, with an exception described in next point. - Data type of receiver's speed is now internally a float instead of int. This change improves behaviour of adaptive receiving algorithm a bit. It's not a big change, but it may positively impact client code using receiver functions. - libcw_test_public test executable now accepts '-m' command line option (for selecting module(s)) to test. Check output of 'libcw_test_public -h' for more details. - Since the modification described above does not change how the library works, nor does it change any API, there is no reason for any major revision changes. Library soname/version has been changed from 6.2.1 to 6.3.1. ---------------------------------------------------------------------------- version 3.4.0 / 2014-11-11 unixcw: - Build system files have been modified to correctly configure and compile unixcw (or at least parts of it) on following platforms (in addition to Debian GNU/Linux): - FreeBSD 10.0 (x86_64-unknown-freebsd10.0, with clang 3.3); - FreeBSD 9.3 (freebsd9.3 with gcc 4.2.1); - OpenBSD 5.5 (amd64-unknown-openbsd5.5 with gcc 4.2.1); See INSTALL file for more information. - QT4 configuration flags (CFLAGS, LIBS, MOC) are now detected only by using pkg-config. My hand-made script that searched for QT4 files has been reduced to simple call to PKG_CHECK_MODULES(). This means that the following arguments are no longer accepted by configure script: --with-qt-cflags= --with-qt-libraries= If you want to override pkg-config settings, you can still use environment variables. Check output of "./configure --help" for details libcw: - Large parts of libcw.c have been moved to new files: - libcw_gen.c (generator) - libcw_tq.c (tone queue) - libcw_data.c (representations and lookup) - libcw_key.c (straight key, iambic keyer) - libcw_signal.c (signal handling) - libcw_utils.c (utility functions) Benefits of this move: - I won't have to use a table of contents to move around in libcw.c (the file had 10kLOC before this operation); - it will be easier to define boundaries between functionality of modules, and it will be easier to reason about how these modules should function and interact with each other; Check ChangeLog entry from 2014-07-28 for more information on this subject. Separation of libcw modules is not yet completed. Receiver code is still mixed with generator code, and there are still some functions left in libcw.c. I expect to finish the separation in one or two development cycles. - "make check" now builds three executables in libcw directory: - libcw_test_internal - libcw_test_public - libcw_test_simple_gen main() functions for test executables have been moved outside of libcw.c. - fixed bug in OSS code on BSD systems (found under FreeBSD 10). As part of configuring OSS device, libcw made this call: ioctl(*fd, SNDCTL_DSP_POST, ¶meter) It turns out that this call was unnecessary and returned error value. This prevented libcw from correctly opening OSS device. Now OSS device should be again available to unixcw applications. Notice that this bug most probably affected only BSD systems. - Library soname/version changed from 6.1.1 to 6.2.1. All these changes described above did not change API of the library in any way. Functionality of the library is intact. libcw.h header file has not been changed at all. cwcp: - cwcp ignored CWCP_OPTIONS environment variable. Now it should read it and use it as intended. - cwcp code has been reviewed. Code has been modified to ensure that each call to ncurses' newwin() is matched with delwin(). valgrind may still complain, but now the situation is a bit better. ---------------------------------------------------------------------------- version 3.3.1 / 2014-04-23 libcw: - Fixing bug discovered when fixing a problem with cwdaemon, reported by Pino Zollo ZP4KFX. Condition for calling "low water mark in tone queue" callback was invalid. This resulted in calling the callback too early or too often. - Library version changed from 6.0.1 to 6.1.1. Big *thank you* to Pino Zollo ZP4KFX for reporting a problem with cwdaemon and for helping to solve it in cwdaemon and in libcw. ---------------------------------------------------------------------------- version 3.3.0 / 2013.12.10 unixcw: - Thomas Beierlein has reported that some combinations of '--enable-*'/'--disable-*' flags result in a code that fails to compile. He also provided a patch that fixes it. I've created a tool ('unixcw/tools/test_configure_flags.sh') that tests all combinations of configure flags, which also detected one more faulty configuration, in addition to the ones discovered by Thomas. Thanks Thomas! - FreeBSD patches: Diane Bruce provided a set of patches that fix compilation on FreeBSD (thank you!). Thanks to the patches the ./configure script now should support two new flags: --with-qt-cflags= --with-qt-libraries= The state of build system on FreeBSD may still not be perfect (I didn't test this myself on FreeBSD), but should now be better than it was. libcw: - new functions: adding new functions: cw_character_is_valid() (replaces cw_check_character()) and cw_string_is_valid() (replaces cw_check_string()). The old functions are still available, but have been marked as deprecated. - unit tests: improvements and expansion of unit test code testing public API. - unit tests: improvements and expansion of unit test code testing libcw's internal functions. - receiver code (responsible for receiving and recognizing Morse code keyed with iambic keyer or straight key) has been reviewed. - code handling incoming events for receiving iambic keyer has been improved. iambic keyer events sent from client code to libcw are recognized with better precision, which leads to better recognition of keyed characters. These changes don't change public API nor "external" behaviour of libcw - these are just improvements of existing functionality. - new function: cw_iambic_keyer_register_timer(): to be used by client code that wants to receive Morse code sent with mouse or keyboard keys emulating iambic key. The function is related to the change mentioned one point above. At this point the interface to iambic keying becomes a bit more complicated. It will take me some more time to figure it out completely. For now I've made some internal fixes and changes that improve one aspect of this functionality, and as a result I had to add this one function. Expect more changes in future. If you want to see an example of usage of the keyer API, see xcwcp source code. xcwcp: - xcwcp takes advantage of changes in how libcw handles iambic keying, and - with some additional modifications in xcwcp's code - gets improved handling of iambic keying. cwgen: - improving randomness of generated strings; until now if cwgen was called twice within the same second, it produced the same result (since the random function has been seeded with value returned by time()). Now the seed comes from gettimeofday() (the milliseconds part), so randomness has been improved a bit. cwutils: - unit tests: adding first unit test in cwutils/dictionary module. - bugfix: usage of __attribute__ ((deprecated("message"))) construct in cwutils/dictionary.h may have lead to compilation failure. This has been fixed by getting rid of "message" argument. ---------------------------------------------------------------------------- version 3.2.0 / 2013.01.11 unixcw: - Fixing faulty logic checking "--disable-feature" flags in configure.ac. Bug reported by Thomas Beierlein. Thanks Thomas! libcw: - Debugging facilities of the library are being rewritten and have been moved to new file: src/lib/libcw_debug.h. See the file for details of new interface. The interface may still change, and it isn't documented yet. List of CW_DEBUG_* flags has been modified. The old interface is being deprecated. - libcw now can generate tones with slopes shaped as sine function or as raised cosine function. The 'raised cosine' slope is now default. - Rewriting usage of "__attribute__ ((deprecated(msg)))" facility in libcw.h. A user has reported errors related to the attribute when compiling libcw with gcc 4.3.3. - Improving the method of queuing a space character. This helps me avoid problems in some corner cases (e.g. when a single space is queued, and tone queue's low watermark is set to 1). - Improving behaviour of cw_generator_new(). Under some circumstances it is less likely to fail to create new generator. There was also possibility of causing a client application to crash if the function failed, but this has been fixed. - Library version changed from 4.1.1 to 5.0.0. cwcp: - Fixing one small issue: in every new session with cwcp the application printed a space at the beginning of played text. ---------------------------------------------------------------------------- version 3.1.1 / 2012.07.04 libcw: - Fixing problem with interlocks in thread code. Library version changed from 4.0.0 to 4.1.1. ---------------------------------------------------------------------------- version 3.1.0 / 2012.06.30 unixcw: - Added support for PulseAudio. All applications from unixcw that produce audio output benefit from this. PulseAudio is the default audio backend on machines with installed and accessible PulseAudio server. If audio backend hasn't been specified by user, the applications try to access different sound backends in following order: PulseAudio -> OSS -> ALSA -> console buzzer. The usual restrictions for console buzzer output (root privileges required) still apply. Running application from unixcw package (cw, cwcp, xcwcp) with ALSA selected as sound backend while PulseAudio server is running will result in all kinds of different problems. User is warned about such situation by the application. - Added 'Null' audio system as an optional audio backend. No sound is being played if this system is selected, libcw is spending its time only on generating tones of proper length and sending then in proper moments to a pseudo-device. Pass '-s n' option to unixcw application to use this 'audio' backend. - Thanks to changes in libcw, unixcw doesn't 'require' ALSA or PulseAudio libraries, or PulseAudio server on target system, even if support for the two audio backends was compiled in. The two audio backends are now only recommended, thanks to linking to the libraries at run time. - Build system now uses libtool. 'libtool' script is in release archive, so there are no new compile-time requirements related to this (at least 'make distcheck' completes successfully with libtool package uninstalled). - It is possible to explicitly disable support for audio backends at compile time. Pass --disable-{console|oss|alsa|pulseaudio} to ./configure to do this. - It is possible to explicitly disable compilation of cwcp and xcwcp. Pass --disable-{cwcp|xcwcp} to ./configure to do this. - Build system now uses Makefile.am files as a basis to generate Makefile files. - Build system now supports and correctly builds "make distcheck" target. libcw: - Added support for PulseAudio. There are still some things to be improved in code implementing support for PulseAudio, but pretty solid basics are there. - Added support for Null pseudo-audio-system. Not sure if it will be useful, I just thought that someone would like to use libcw without generating any sound. Perhaps some simple platforms without audio devices, perhaps practicing sending/keying without playback. Who knows. It was easy to do, so I did it. - If you compare libcw.c files from this and previous release, you will notice lots of changes. This is because I've changed the algorithm that controls time periods when generating tones (generating sine wave). Until now the periods were measured with itimers. The tricky part was to keep two processed in sync: turning sine wave on and off, and sending the content of audio buffer with sine wave to audio backend. I'm calling it 'tricky' because it works fine only for small sizes of audio buffer, as was is in case of OSS (~128 samples). With larger buffer sizes forced by ALSA (~1k samples) the two processes de-synchronize. I had to come up with better design, and the side effect of the design is total rewrite of parts of libcw.c (for my own purposes I call the rewrite a 'butchering', but that is a different story). New design relies on blocking writes to audio systems, and their property of being able to play only X samples (Y milliseconds) at a time (with given sample rate). - Following functions are deprecated: cw_check_representation(), cw_lookup_representation(), cw_lookup_character(). Use these instead: cw_representation_is_valid(), cw_representation_to_character(), cw_character_to_representation(). - library's soname changed from 3.0.1 to 4.0.0 Library's version becomes independent from version of unixcw. cwcp: - Fixing small bug in user interface: till now modifying practice time didn't work correctly, any attempts to do so resulted in resetting the time to zero. Now this is fixed. ---------------------------------------------------------------------------- version 3.0.2 / 2012.06.18 New version. You won't find it in git repo, but only in SourceForge download area and on Debian webpage. This is a special release with two goals related to Debian packaging: debian/: - fixing Debian's FTBFS bug #676752 - removing debian/ from unixcw_X.Y.Z.orig.tar.gz. Contents of debian/ can be found in separately provided unixcw_X.Y.Z-W.debian.tar.gz Thanks to Kamal Mostafa for patiently explaining to me some details of Debian/Ubuntu packaging. ---------------------------------------------------------------------------- version 3.0.1 / 2012.01.08 Thanks to Kamal Mostafa for providing patches that are the reason for 3.0.1 release. Content of upstream package unixcw-3.0.1 is approximately the same as of Debian source package unixcw_3.0-5. debian/: - added proper "Section" field in control file; - fixed library dependencies in control file; - fixed "Replaces:"/"Conflicts:" fields in control file; - fixed Debian bug #653411: changes in *.preinst files to remove dangling symlinks left by old packaging (suggested by Bob Proulx); general: - fixed library dependencies in "configure" script; - fixed library dependencies provided by libcw.pc file; libcw: - proper detection of capabilities of liboss-salsa-dev (kfreebsd's libasound implementation); libcw now can be built properly on Debian/kFreeBSD; - fixing awk scripts producing man pages: the awk scripts now work with mawk too; cwcp: - fixed list of libraries in Makefile; ---------------------------------------------------------------------------- version 3.0 / 2011.12.13 Thanks to Simon Baldwin for creating this software in the first place, and to Kamal Mostafa for helping me with packaging unixcw 3.0 for Debian. unixcw: - Changed major version of unixcw package to '3'. Changes listed below probably justify this. - New main developer: Kamil Ignacak - New website: http://unixcw.sourceforge.net - Scripts building Debian packages now build 'libcw3' package instead of 'unixcw' package, and 'libcw3-dev' Debian package instead of 'unixcw-dev' Debian package. libcw: - Renamed 'cwlib' part of unixcw package to 'libcw'. - Renamed library's header file from cwlib.h to libcw.h. - Changed library's soname from 0 to 3 (/usr/lib/libcw.so.3.0.0 / libcw.so.3). - Added support for ALSA. OSS is still the default backend. - As a consequence, libasound2 is a new dependency. There is no possibility (yet) to disable or reconfigure this at build time. It is possible to select sound backend in applications that use libcw. - Added implicit dependency on pthread library (which is a part of libc library, so it should be available on target system by default). Main routine generating sine wave operates as a separate thread. - Added actual slopes to dot and dash sounds produced by the library. This should result in less of (or even none) audible pops. Unfortunately current implementation makes dots and dashes a tiny bit longer than they would be without the slopes. The difference is not significant, but I plan to re-implement it in the future to make this feature 100% correct. The slopes are linear. - Rewritten management of sound volume so that the library doesn't use OSS mixer. This should resolve Debian bug #567394 (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=567394). Code that controls volume level is common for OSS and ALSA. - Since libcw doesn't touch the mixer device anymore, then Debian bug #567392 should be resolved as well (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=567392). - Added code that attempts to discover proper audio buffer size for both OSS and ALSA. This should resolve Debian bug #567395 (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=567395). - Fix of Debian bug #567395 should also solve Debian bug #567397 (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=567397). - Changed library's API. There will be more API changes in 3.1, so consider current API to be unstable. - Changed volume change step from 5% to 1%. - Changed frequency change step from 100Hz to 20Hz. - The library no longer can produce sound on console buzzer and on sound card at the same time. - The library knows default names of OSS, ALSA and console buzzer devices. The names are used to open sound devices if no other names are provided explicitly as function arguments. - Introduced symbolic names of int values returned by most of library's function: CW_FAILURE / CW_SUCCESS. xcwcp: - Ported xcwcp to QT4. This should resolve Debian bug #604386 (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=604386). - Added auto-detection of QT4 headers and QT4 moc by build files. unixcw-3.6.0/unixcw-2.3.spec0000644000175000017500000000457711665754531012501 00000000000000# # Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Normal descriptive preamble. Summary: UnixCW Morse Code Utilities Name: unixcw Version: 2.3 Release: 2 Group: Applications/Hamradio Copyright: GPL Packager: Simon Baldwin URL: http://www.geocities.com/simon_baldwin/ Source: ftp://metalab.unc.edu/pub/Linux/apps/ham/morse/unixcw-2.3.tgz BuildRoot: /tmp/unixcw-2.3 %description The UnixCW utilities add a general purpose CW library to your system, and a small set of applications based around this library. These applications form a Morse code tutor suite, useful for Amateur and Marine radio operators. # Straightforward preparation for building. %prep %setup # To build, first configure, then make. Here we set "prefix" to the build # root, suffixed with "/usr". So, unlike our natural locations under # "/usr/local", we build and install the RPM packaged version in "/usr", # leaving "/usr/local" free for, well, the usual "/usr/local" stuff. %build ./configure make prefix="$RPM_BUILD_ROOT/usr" # Install with "prefix" tweaked in the same way. %install make prefix="$RPM_BUILD_ROOT/usr" install # Clean up our build root. %clean rm -rf $RPM_BUILD_ROOT # Postinstall and postuninstall. %post /sbin/ldconfig %postun /sbin/ldconfig # List of packaged files. %files /usr/bin/cw /usr/bin/cwcp /usr/bin/cwgen /usr/bin/xcwcp /usr/include/libcw.h /usr/lib/libcw.a /usr/lib/libcw.so /usr/lib/libcw.so.0 /usr/lib/libcw.so.0.0.0 /usr/man/man1/cw.1.gz /usr/man/man1/cwcp.1.gz /usr/man/man1/cwgen.1.gz /usr/man/man1/xcwcp.1.gz /usr/man/man3/libcw.3.gz /usr/man/man7/CW.7.gz /usr/man/man7/cw.7.gz /usr/lib/pkgconfig/libcw.pc %doc COPYING %doc README %doc INSTALLING unixcw-3.6.0/aclocal.m40000644000175000017500000014646714001105751011632 00000000000000# generated automatically by aclocal 1.15 -*- Autoconf -*- # Copyright (C) 1996-2014 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- dnl serial 11 (pkg-config-0.29) dnl dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR # Copyright (C) 2002-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.15], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.15])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each '.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) unixcw-3.6.0/Makefile.inc.in0000644000175000017500000000333214000344554012573 00000000000000# Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) # Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Autoconfigure definitions. AC_CC = @CC@ AC_CFLAGS = @CFLAGS@ AC_CXX = @CXX@ AC_CXXFLAGS = @CXXFLAGS@ AC_DEFS = @DEFS@ AC_GZIP = @GZIP@ AC_LDCONFIG = @LDCONFIG@ AC_LIBS = @LIBS@ AC_AWK = @AWK@ AC_QT5_CFLAGS = @QT5_CFLAGS@ AC_QT5_LIBS = @QT5_LIBS@ AC_QT5_MOC = @MOC@ AC_SRC_SUBDIRS = @SRC_SUBDIRS@ AC_CFLAG_PIC = @CFLAG_PIC@ AC_CC_LINKS_SO = @CC_LINKS_SO@ AC_LD_LINKS_SO = @LD_LINKS_SO@ AC_LD = @LD@ AC_LN_S = @LN_S@ # Portability values. INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ prefix = @prefix@ exec_prefix = @exec_prefix@ includedir = @includedir@ bindir = @bindir@ mandir = @mandir@ libdir = @libdir@ # http://www.gnu.org/s/hello/manual/autoconf/Changed-Directory-Variables.html datarootdir = @datarootdir@ AM_CPPFLAGS = -I$(top_srcdir)/src/ \ -I$(top_srcdir)/src/libcw/ \ -I$(top_srcdir)/src/libcw/tests/ \ -I$(top_srcdir)/src/cwutils/ unixcw-3.6.0/ltmain.sh0000644000175000017500000117147414000344554011614 00000000000000#! /bin/sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2014-01-03.01 # libtool (GNU libtool) 2.4.6 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAM=libtool PACKAGE=libtool VERSION="2.4.6 Debian-2.4.6-2" package_revision=2.4.6 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2015-01-20.17; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # Copyright (C) 2004-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # As a special exception to the GNU General Public License, if you distribute # this file as part of a program or library that is built using GNU Libtool, # you may include this file under the same distribution terms that you use # for the rest of that program. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some retarded systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # We should try to minimise forks, especially on Windows where they are # unreasonably slow, so skip the feature probes when bash or zsh are # being used: if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then : ${_G_HAVE_ARITH_OP="yes"} : ${_G_HAVE_XSI_OPS="yes"} # The += operator was introduced in bash 3.1 case $BASH_VERSION in [12].* | 3.0 | 3.0*) ;; *) : ${_G_HAVE_PLUSEQ_OP="yes"} ;; esac fi # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. test -z "$_G_HAVE_PLUSEQ_OP" \ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ && _G_HAVE_PLUSEQ_OP=yes if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1+=\\ \$func_quote_for_eval_result" }' else func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1=\$$1\\ \$func_quote_for_eval_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_for_eval ARG... # -------------------------- # Aesthetically quote ARGs to be evaled later. # This function returns two values: # i) func_quote_for_eval_result # double-quoted, suitable for a subsequent eval # ii) func_quote_for_eval_unquoted_result # has all characters that are still active within double # quotes backslashified. func_quote_for_eval () { $debug_cmd func_quote_for_eval_unquoted_result= func_quote_for_eval_result= while test 0 -lt $#; do case $1 in *[\\\`\"\$]*) _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; *) _G_unquoted_arg=$1 ;; esac if test -n "$func_quote_for_eval_unquoted_result"; then func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" else func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" fi case $_G_unquoted_arg in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_quoted_arg=\"$_G_unquoted_arg\" ;; *) _G_quoted_arg=$_G_unquoted_arg ;; esac if test -n "$func_quote_for_eval_result"; then func_append func_quote_for_eval_result " $_G_quoted_arg" else func_append func_quote_for_eval_result "$_G_quoted_arg" fi shift done } # func_quote_for_expand ARG # ------------------------- # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { $debug_cmd case $1 in *[\\\`\"]*) _G_arg=`$ECHO "$1" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; *) _G_arg=$1 ;; esac case $_G_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_arg=\"$_G_arg\" ;; esac func_quote_for_expand_result=$_G_arg } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_for_expand "$_G_cmd" eval "func_notquiet $func_quote_for_expand_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_for_expand "$_G_cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # Set a version string for this script. scriptversion=2014-01-07.03; # UTC # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # Copyright (C) 2010-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# warranty; '. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # to the main code. A hook is just a named list of of function, that can # be run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of functions called by FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It is assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook funcions.n" ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do eval $_G_hook '"$@"' # store returned options list back into positional # parameters for next 'cmd' execution. eval _G_hook_result=\$${_G_hook}_result eval set dummy "$_G_hook_result"; shift done func_quote_for_eval ${1+"$@"} func_run_hooks_result=$func_quote_for_eval_result } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list in your hook function, remove any # options that you action, and then pass back the remaining unprocessed # options in '_result', escaped suitably for # 'eval'. Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # func_quote_for_eval ${1+"$@"} # my_options_prep_result=$func_quote_for_eval_result # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # # Note that for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # ;; # *) set dummy "$_G_opt" "$*"; shift; break ;; # esac # done # # func_quote_for_eval ${1+"$@"} # my_silent_option_result=$func_quote_for_eval_result # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # # func_quote_for_eval ${1+"$@"} # my_option_validation_result=$func_quote_for_eval_result # } # func_add_hook func_validate_options my_option_validation # # You'll alse need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd func_options_prep ${1+"$@"} eval func_parse_options \ ${func_options_prep_result+"$func_options_prep_result"} eval func_validate_options \ ${func_parse_options_result+"$func_parse_options_result"} eval func_run_hooks func_options \ ${func_validate_options_result+"$func_validate_options_result"} # save modified positional parameters for caller func_options_result=$func_run_hooks_result } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propogate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before # returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} # save modified positional parameters for caller func_options_prep_result=$func_run_hooks_result } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd func_parse_options_result= # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} # Adjust func_parse_options positional parameters to match eval set dummy "$func_run_hooks_result"; shift # Break out of the loop if we already parsed every option. test $# -gt 0 || break _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) test $# = 0 && func_missing_arg $_G_opt && break case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac done # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} func_parse_options_result=$func_quote_for_eval_result } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE # save modified positional parameters for caller func_validate_options_result=$func_run_hooks_result } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables after # splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} test "x$func_split_equals_lhs" = "x$1" \ && func_split_equals_rhs= }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /(C)/!b go :more /\./!{ N s|\n# | | b more } :go /^# Written by /,/# warranty; / { s|^# || s|^# *$|| s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| p } /^# Written by / { s|^# || p } /^warranty; /q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.4.6' # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { $debug_cmd $warning_func ${1+"$@"} } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --mode=MODE use operation mode MODE --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname $scriptversion Debian-2.4.6-2 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func__fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_preserve_dup_deps=false opt_quiet=false nonopt= preserve_args= # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac # Pass back the list of options. func_quote_for_eval ${1+"$@"} libtool_options_prep_result=$func_quote_for_eval_result } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $_G_opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac done # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} libtool_parse_options_result=$func_quote_for_eval_result } func_add_hook func_parse_options libtool_parse_options # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote_for_eval ${1+"$@"} libtool_validate_options_result=$func_quote_for_eval_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then func_quote_for_eval "$arg2" fi func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_for_eval "$install_override_mode" func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module=$wl-multi_module continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "'-no-install' is ignored for $host" func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -os2dllname) prev=os2dllname continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang # -fsanitize=* Clang/GCC memory and address sanitizer -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ -specs=*|-fsanitize=*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; -Z*) if test os2 = "`expr $host : '.*\(os2\)'`"; then # OS/2 uses -Zxxx to specify OS/2-specific options compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case $arg in -Zlinker | -Zstack) prev=xcompiler ;; esac continue else # Otherwise treat like 'Some other compiler flag' below func_quote_for_eval "$arg" arg=$func_quote_for_eval_result fi ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result test none = "$pic_object" || { # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object } # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the '$prevarg' option requires an argument" if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname=$func_basename_result libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" # Definition is injected by LT_CONFIG during libtool generation. func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" func_dirname "$output" "/" "" output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs=$tmp_deplibs fi if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass"; then libs=$deplibs deplibs= fi if test prog = "$linkmode"; then case $pass in dlopen) libs=$dlfiles ;; dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs=$dlprefiles fi if test dlopen = "$pass"; then # Collect dlpreopened libraries save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= found=false case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test lib != "$linkmode" && test prog != "$linkmode"; then func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib=$searchdir/lib$name$search_ext if test -f "$lib"; then if test .la = "$search_ext"; then found=: else found=false fi break 2 fi done done if $found; then # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll=$l done if test "X$ll" = "X$old_library"; then # only static version available found=false func_dirname "$lib" "" "." ladir=$func_dirname_result lib=$ladir/$old_library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi else # deplib doesn't seem to be a libtool library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi ;; # -l *.ltframework) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=: fi ;; pass_all) valid_a_lib=: ;; esac if $valid_a_lib; then echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." fi ;; esac continue ;; prog) if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test conv = "$pass"; then deplibs="$deplib $deplibs" elif test prog = "$linkmode"; then if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=: continue ;; esac # case $deplib $found || test -f "$lib" \ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir=$func_dirname_result dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass" || { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done elif test prog != "$linkmode" && test lib != "$linkmode"; then func_fatal_error "'$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test yes = "$prefer_static_libs" || test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib=$l done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. if test dlopen = "$pass"; then test -z "$libdir" \ && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || test yes != "$dlopen_support" || test no = "$build_libtool_libs" then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir=$ladir fi ;; esac func_basename "$lib" laname=$func_basename_result # Find the relevant object directory and library name. if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library '$lib' was moved." dir=$ladir absdir=$abs_ladir libdir=$abs_ladir else dir=$lt_sysroot$libdir absdir=$lt_sysroot$libdir fi test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir=$ladir absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else dir=$ladir/$objdir absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test dlpreopen = "$pass"; then if test -z "$libdir" && test prog = "$linkmode"; then func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=false if test no != "$link_all_deplibs" || test -z "$library_names" || test no = "$build_libtool_libs"; then linkalldeplibs=: fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && { { test no = "$prefer_static_libs" || test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if $alldeplibs && { test pass_all = "$deplibs_check_method" || { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule=$dlpremoduletest break fi done if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test lib = "$linkmode" && test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result versuffix=-$major ;; esac eval soname=\"$soname_spec\" else soname=$realname fi # Make a new name for the extract_expsyms_cmds to use soroot=$soname func_basename "$soroot" soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test no = "$hardcode_direct"; then add=$dir/$linklib case $host in *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add=$dir/$old_library fi elif test -n "$old_library"; then add=$dir/$old_library fi fi esac elif test no = "$hardcode_minus_L"; then case $host in *-*-sunos*) add_shlibpath=$dir ;; esac add_dir=-L$dir add=-l$name elif test no = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; relink) if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$dir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name elif test yes = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; *) lib_linked=no ;; esac if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test yes != "$hardcode_direct" && test yes != "$hardcode_minus_L" && test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$libdir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$libdir add=-l$name elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add=-l$name elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib"; then add=$inst_prefix_dir$libdir/$linklib else add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name fi if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test unsupported != "$hardcode_direct"; then test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test yes = "$build_libtool_libs"; then # Not a shared library if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test lib = "$linkmode"; then if test -n "$dependency_libs" && { test yes != "$hardcode_into_libs" || test yes = "$build_old_libs" || test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of '$dir'" absdir=$dir fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names"; then for tmp in $deplibrary_names; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl"; then depdepl=$absdir/$objdir/$depdepl darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) path=-L$absdir/$objdir ;; esac else eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "'$deplib' seems to be moved" path=-L$absdir fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test link = "$pass"; then if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs=$newdependency_libs if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test dlopen != "$pass"; then test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= } if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" else vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Add Sun CC postdeps if required: test CXX = "$tagname" && { case $host_os in linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; solaris*) func_cc_basename "$CC" case $func_cc_basename_result in CC* | sunCC*) func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; esac } # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i= ;; esac if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test prog = "$linkmode"; then dlfiles=$newdlfiles fi if test prog = "$linkmode" || test lib = "$linkmode"; then dlprefiles=$newdlprefiles fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs=$output func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test no = "$module" \ && func_fatal_help "libtool library '$output' must begin with 'lib'" if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test pass_all != "$deplibs_check_method"; then func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test no = "$dlself" \ || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test 1 -lt "$#" \ && func_warning "ignoring multiple '-rpath's for a libtool library" install_libdir=$1 oldlibs= if test -z "$rpath"; then if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift IFS=$save_ifs test -n "$7" && \ func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major=$1 number_minor=$2 number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # that has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|freebsd-elf|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_revision ;; freebsd-aout|qnx|sunos) current=$number_major revision=$number_minor age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_minor lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type '$version_type'" ;; esac ;; no) current=$1 revision=$2 age=$3 ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT '$current' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION '$revision' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE '$age' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE '$age' is greater than the current interface number '$current'" func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" # On Darwin other compilers case $CC in nagfor*) verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" ;; *) verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; esac ;; freebsd-aout) major=.$current versuffix=.$current.$revision ;; freebsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; irix | nonstopux) if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring_prefix$major.$iface:$verstring done # Before this point, $major must not contain '.'. major=.$major versuffix=$major.$revision ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=.$current.$age.$revision verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring:$iface.0 done # Make executables depend on our current version. func_append verstring ":$current.0" ;; qnx) major=.$current versuffix=.$current ;; sco) major=.$current versuffix=.$current ;; sunos) major=.$current versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result versuffix=-$major ;; *) func_fatal_configuration "unknown library version type '$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring=0.0 ;; esac if test no = "$need_version"; then versuffix= else versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided if test yes,no = "$avoid_version,$need_version"; then major= versuffix= verstring= fi # Check to see if the archive will have undefined symbols. if test yes = "$allow_undefined"; then if test unsupported = "$allow_undefined_flag"; then if test yes = "$build_old_libs"; then func_warning "undefined symbols not allowed in $host shared libraries; building static only" build_libtool_libs=no else func_fatal_error "can't build $host shared library unless -no-undefined is specified" fi fi else # Don't allow undefined symbols. allow_undefined_flag=$no_undefined_flag fi fi func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" test " " = "$libobjs" && libobjs= if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release= versuffix= major= newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib= ;; esac fi if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test yes = "$allow_libtool_libs_with_static_runtimes"; then for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test yes = "$droppeddeps"; then if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test yes = "$build_libtool_libs"; then # Remove $wl instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath=$finalize_rpath test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath=$finalize_shlibpath test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname=$realname fi if test -z "$dlname"; then dlname=$soname fi lib=$output_objdir/$realname linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS=$save_ifs if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi ${skipped_export-false} && { func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi } libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs=$IFS; IFS='~' for cmd in $cmds; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs # Restore the uninstalled library and exit if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. dlname=$soname fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ func_warning "'-version-info' is ignored for objects" test -n "$release" && \ func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj=$output ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # if reload_cmds runs $LD directly, get rid of -Wl from # whole_archive_flag_spec and hope we can get by with turning comma # into space. case $reload_cmds in *\$LD[\ \$]*) wl= ;; esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS } if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "'-version-info' is ignored for programs" test -n "$release" && \ func_warning "'-release' is ignored for programs" $preload \ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " $wl-bind_at_load" func_append finalize_command " $wl-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath=$rpath rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath=$rpath if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=false ;; *cygwin* | *mingw* ) test yes = "$build_libtool_libs" || wrappers_required=false ;; *) if test no = "$need_relink" || test yes != "$build_libtool_libs"; then wrappers_required=false fi ;; esac $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.$objext"; then func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test yes = "$no_install"; then # We don't need to create a wrapper script. link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi case $hardcode_action,$fast_install in relink,*) # Fast installation is not supported link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath func_warning "this platform does not like uninstalled shared libraries" func_warning "'$output' will be relinked during installation" ;; *,yes) link_command=$finalize_var$compile_command$finalize_rpath relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` ;; *,no) link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath ;; *,needless) link_command=$finalize_var$compile_command$finalize_rpath relink_command= ;; esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource=$output_path/$objdir/lt-$output_name.c cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do case $build_libtool_libs in convenience) oldobjs="$libobjs_save $symfileobj" addlibs=$convenience build_libtool_libs=no ;; module) oldobjs=$libobjs_save addlibs=$old_convenience build_libtool_libs=no ;; *) oldobjs="$old_deplibs $non_pic_objects" $preload && test -f "$symfileobj" \ && func_append oldobjs " $symfileobj" addlibs=$old_convenience ;; esac if test -n "$addlibs"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test yes = "$installed"; then if test -z "$install_libdir"; then break fi output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name=$func_basename_result func_resolve_sysroot "$deplib" eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } if test link = "$opt_mode" || test relink = "$opt_mode"; then func_mode_link ${1+"$@"} fi # func_mode_uninstall arg... func_mode_uninstall () { $debug_cmd RM=$nonopt files= rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic for arg do case $arg in -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir=$func_dirname_result if test . = "$dir"; then odir=$objdir else odir=$dir/$objdir fi func_basename "$file" name=$func_basename_result test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif $rmforce; then continue fi rmfiles=$file case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.$objext" if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name"; then func_append rmfiles " $odir/lt-$noexename.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then func_mode_uninstall ${1+"$@"} fi test -z "$opt_mode" && { help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: unixcw-3.6.0/Makefile.in0000644000175000017500000006210114001105753012017 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/src/config.h CONFIG_CLEAN_FILES = Makefile.inc CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.inc.in \ AUTHORS COPYING ChangeLog INSTALL NEWS README THANKS compile \ config.guess config.sub depcomp install-sh ltmain.sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CC_LINKS_SO = @CC_LINKS_SO@ CFLAGS = @CFLAGS@ CFLAG_PIC = @CFLAG_PIC@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DL_LIB = @DL_LIB@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ GZIP = @GZIP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTL_LIB = @INTL_LIB@ LD = @LD@ LDCONFIG = @LDCONFIG@ LDFLAGS = @LDFLAGS@ LD_LINKS_SO = @LD_LINKS_SO@ LIBCW_NDEBUG = @LIBCW_NDEBUG@ LIBCW_VERSION = @LIBCW_VERSION@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MOC = @MOC@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OSS_LIB = @OSS_LIB@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QT5_CFLAGS = @QT5_CFLAGS@ QT5_LIBS = @QT5_LIBS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SRC_SUBDIRS = @SRC_SUBDIRS@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src EXTRA_DIST = \ icon_unixcw.svg icon_unixcw.xpm \ unixcw-2.3.spec unixcw-3.6.0.lsm \ po/UnixCW.po \ THANKS HISTORY \ patches # debian all: all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): Makefile.inc: $(top_builddir)/config.status $(srcdir)/Makefile.inc.in cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-libtool \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ clean-libtool cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ dist-xz dist-zip distcheck distclean distclean-generic \ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: unixcw-3.6.0/HISTORY0000644000175000017500000001145114000344554011043 00000000000000*** This file is no longer updated. *** *** See NEWS file for description of changes. *** This is HISTORY file for unixcw. It describes major changes in unixcw releases. For detailed description of changes see NEWS file. The original UnixCW package, version 1, grew out of a desire to have a program available under Linux that was very similar to the one I was used to under DOS. While several Linux Morse Tutors were, in fact, still are, available, I never quite found one that did the stuff I wanted. From the original UnixCW package came version 2. This was almost a total rewrite, although it included the same basic binaries as were in version 1. The two major changes for this version were the addition of an extensive, general purpose CW library, and also an X Window-based CW tutor program. With version 2.1, finally, came full sound card support, built into the CW library itself. All three main user level programs that use the library were enhanced to allow control over the sound card tone volume. Console speaker sound is still there, if needed, but sound card tones became the default mode. Version 2.2 was a bug-fixed version of 2.1. It seems that some sound card drivers (some later OSS drivers) couldn't handle the volume control ioctls that the CW library uses, and this meant sound card tones would not work for these systems. To solve this, version 2.2 uses the /dev/mixer device to control volumes if it finds that it cannot do this with the main sound card device. Version 2.3 is a thorough overhaul, cleanup, and refactoring of version 2.2. All the code has been reformatted for correct Gnu style, and programs now take a lot more care over what they do, and don't do, in signal handler context. cwcp and xcwcp now offer fully configurable modes and dictionaries through a configuration file option, and xcwcp has been rewritten in proper C++ style. Version 3.0 has been prepared and released by new lead developer, Kamil Ignacak. The takeover of unixcw has been discussed with and approved by Simon Baldwin. Main changes in 3.0 are: o rewriting parts of libcw so that using OSS is less probable to cause problems (this should solve at least one Debian bug) o adding support for ALSA to libcw; libcw uses OSS as default audio system, but there are no differences in support of OSS and ALSA in terms of functionality o porting xcwcp to Qt4 The fact that there is a new maintainer should take some burden off of shoulders of Kamal Mostafa, who was responsible for (among other things) patching unixcw. Version 3.0 introduces some changes in API, and there should be some more API changes in 3.1. Hopefully things will be more stable in 3.2. Version 3.0.1 contains small fixes that don't influence core capabilities of any application, but are still important. Patches for this release were provided mainly by Kamal Mostafa (thanks Kamal!). Version 3.1.0 adds support for PulseAudio. Plenty of other changes in library code improve timings of tones produced by the library. Few library functions are deprecated, they are replaced by new functions. Version 3.1.1 fixes small problem with interlocks in thread code. Version 3.2.0 introduces small fixes and improvements in libcw, and implements new debug facilities (the old ones are being deprecated). It also fixes faulty logic checking "--disable-feature" flags in configure.ac. The problem was spotted by Thomas Beierlein. Version 3.3.0 is mostly fixes. More fixes in configuration flags (thanks again to Thomas Beierlein). Patches fixing compilation under FreeBSD (thanks to Diane Bruce). Fixes and improvements of handling of iambic keying in libcw and xcwcp. Fixing compilation error in cwutils/dictionary.h. Version 3.3.1 is just one fix in libcw. Thanks to Pino Zollo ZP4KFX for reporting a bug and testing solution. Version 3.4.0 contains two changes in source code: - refreshment of cwcp code; - splitting libcw.c into parts. Additionally the build system has been updated to correctly build unixcw on: - FreeBSD 10 (x86_64-unknown-freebsd10.0, with clang 3.3); - FreeBSD 9.3 (freebsd9.3 with gcc 4.2.1); - OpenBSD 5.5 (amd64-unknown-openbsd5.5 with gcc 4.2.1); Version 3.4.1 focused on internal changes in libcw. All library code is now split between separate module files. Some test code from libcw_test_public.c has been moved to respective modules. Version 3.4.2 focused on internal changes in libcw. Improving separation of modules in the library. Taking steps to separate library's global variables (generator, receiver, key) and functions implicitly operating on these variables from functions taking a generator, receiver, or key as an argument. Improving unit tests code. Testing unixcw on Alpine Linux (because of musl libc) and FreeBSD 10.0 (because this is *unix*cw). *** This file is no longer updated. *** *** See NEWS file for description of changes. ***unixcw-3.6.0/patches/0000755000175000017500000000000013674212270011471 500000000000000unixcw-3.6.0/patches/2018_08_FreeBSD/0000755000175000017500000000000014000344554013657 500000000000000unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_cw_cw.c0000644000175000017500000000112213674212270017012 00000000000000--- src/cw/cw.c.orig 2018-08-08 04:24:25 UTC +++ src/cw/cw.c @@ -598,7 +598,9 @@ int main (int argc, char *const argv[]) } } +#ifndef __FreeBSD__ if (config->audio_system == CW_AUDIO_ALSA + ) { && cw_is_pa_possible(NULL)) { fprintf(stdout, "Selected audio system is ALSA, but audio on your system is handled by PulseAudio. Expect problems with timing.\n"); @@ -607,6 +609,7 @@ int main (int argc, char *const argv[]) fprintf(stdout, "Press Enter key to continue\n"); getchar(); } +#endif generator = cw_generator_new_from_config(config); if (!generator) { unixcw-3.6.0/patches/2018_08_FreeBSD/patch-configure.ac0000644000175000017500000000165613674212270017177 00000000000000--- configure.ac.orig 2017-02-12 10:41:29 UTC +++ configure.ac @@ -22,6 +22,8 @@ AC_PREREQ(2.57) AC_INIT([unixcw], [3.5.1]) AC_CONFIG_SRCDIR([src/libcw/libcw_gen.c]) AM_INIT_AUTOMAKE +AC_CONFIG_MACRO_DIR([m4]) +AC_USE_SYSTEM_EXTENSIONS # Libtool initialization, added during tests on FreeBSD LT_INIT @@ -41,6 +43,9 @@ AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_LN_S AM_PROG_CC_C_O +AC_GNU_SOURCE +AM_GNU_GETTEXT(external) +AM_GNU_GETTEXT_VERSION([0.18]) @@ -409,8 +414,6 @@ AC_SUBST(LIBCW_NDEBUG) - - # ##### # end # ##### @@ -427,7 +430,7 @@ AC_HEADER_STDC AC_HEADER_STDBOOL AC_HEADER_TIME AC_CHECK_HEADERS([fcntl.h limits.h stdlib.h string.h strings.h sys/ioctl.h \ - sys/param.h sys/time.h unistd.h locale.h libintl.h]) + sys/param.h sys/time.h unistd.h locale.h]) AC_CHECK_HEADERS([getopt.h]) AC_CHECK_HEADERS([string.h strings.h]) if test "$ac_cv_header_string_h" = 'no' \ unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_cwutils_Makefile.am0000644000175000017500000000102313674212270021352 00000000000000--- src/cwutils/Makefile.am.orig 2018-08-07 19:05:29 UTC +++ src/cwutils/Makefile.am @@ -29,7 +29,7 @@ cw_dictionary_tests_SOURCES = dictionary cw_dictionary_tests_CPPFLAGS = $(AM_CPPFLAGS) -DCW_DICTIONARY_UNIT_TESTS # target-specific linker flags (objects to link) -cw_dictionary_tests_LDADD=-L$(top_builddir)/src/libcw/.libs -lcw #-lm -lpthread -ldl +cw_dictionary_tests_LDADD=$(LTLIBINTL) -L$(top_builddir)/src/libcw/.libs -lcw #-lm -lpthread -ldl # target-specific compiler flags cw_dictionary_tests_CFLAGS = -rdynamic unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_cwgen_Makefile.am0000644000175000017500000000107513674212270020772 00000000000000--- src/cwgen/Makefile.am.orig 2018-08-07 19:09:07 UTC +++ src/cwgen/Makefile.am @@ -26,7 +26,7 @@ cwgen_SOURCES = cwgen.c # target-specific preprocessor flags (#defs and include dirs) #cwgen_CPPFLAGS = -I$(top_srcdir)/src/cwutils/ -I$(top_srcdir)/src/libcw/ # target-specific linker flags (objects to link) -cwgen_LDADD = -L$(top_builddir)/src/libcw/.libs -lcw $(top_builddir)/src/cwutils/lib_cwgen.a +cwgen_LDADD = $(LTLIBINTL) -L$(top_builddir)/src/libcw/.libs -lcw $(top_builddir)/src/cwutils/lib_cwgen.a # copy man page to proper directory during installation unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_cwcp_cwcp.c0000644000175000017500000000067413674212270017673 00000000000000--- src/cwcp/cwcp.c.orig 2018-08-08 04:24:32 UTC +++ src/cwcp/cwcp.c @@ -1732,6 +1732,7 @@ int main(int argc, char **argv) } } +#ifndef __FreeBSD__ if (config->audio_system == CW_AUDIO_ALSA && cw_is_pa_possible(NULL)) { @@ -1741,6 +1742,7 @@ int main(int argc, char **argv) fprintf(stdout, "Press Enter key to continue\n"); getchar(); } +#endif generator = cw_generator_new_from_config(config); if (!generator) { unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_cwcp_Makefile.am0000644000175000017500000000110613674212270020616 00000000000000--- src/cwcp/Makefile.am.orig 2018-08-07 19:10:59 UTC +++ src/cwcp/Makefile.am @@ -26,7 +26,7 @@ cwcp_SOURCES = cwcp.c # target-specific preprocessor flags (#defs and include dirs) #cwcp_CPPFLAGS = -I$(top_srcdir)/src/cwutils/ -I$(top_srcdir)/src/libcw/ # target-specific linker flags (objects to link) -cwcp_LDADD = -lcurses -L$(top_builddir)/src/libcw/.libs -lcw $(top_builddir)/src/cwutils/lib_cwcp.a +cwcp_LDADD = -lcurses $(LTLIBINTL) -L$(top_builddir)/src/libcw/.libs -lcw $(top_builddir)/src/cwutils/lib_cwcp.a # copy man page to proper directory during installation unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_xcwcp_application.cc0000644000175000017500000000102613674212270021565 00000000000000--- src/xcwcp/application.cc.orig 2018-08-08 04:24:39 UTC +++ src/xcwcp/application.cc @@ -1126,6 +1126,7 @@ void Application::make_auxiliaries_end(v void Application::check_audio_system(cw_config_t *config) { +#ifndef __FreeBSD__ if (config->audio_system == CW_AUDIO_ALSA && cw_is_pa_possible(NULL)) { @@ -1137,6 +1138,7 @@ void Application::check_audio_system(cw_ msgBox.setText(message1 + " " + message2 + message3.arg(config->program_name).arg(config->program_name)); msgBox.exec(); } +#endif return; } unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_cwutils_i18n.h0000644000175000017500000000037213674212270020254 00000000000000--- src/cwutils/i18n.h.orig 2018-08-07 18:50:31 UTC +++ src/cwutils/i18n.h @@ -20,7 +20,7 @@ #ifndef _CWI18N_H #define _CWI18N_H -#if defined(HAVE_LIBINTL_H) +#if defined(HAVE_GETTEXT) # include # define _(STR) i18n_gettext (STR) unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_cw_Makefile.am0000644000175000017500000000076013674212270020300 00000000000000--- src/cw/Makefile.am.orig 2018-08-07 19:08:14 UTC +++ src/cw/Makefile.am @@ -26,7 +26,7 @@ cw_SOURCES = cw.c cw.h # target-specific preprocessor flags (#defs and include dirs) # cw_CPPFLAGS = -I$(top_srcdir)/src/cwutils -I$(top_srcdir)/src/libcw/ # target-specific linker flags (objects to link) -cw_LDADD = -L$(top_builddir)/src/libcw/.libs -lcw $(top_builddir)/src/cwutils/lib_cw.a +cw_LDADD = $(LTLIBINTL) -L$(top_builddir)/src/libcw/.libs -lcw $(top_builddir)/src/cwutils/lib_cw.a unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_cwutils_i18n.c0000644000175000017500000000043713674212270020251 00000000000000--- src/cwutils/i18n.c.orig 2018-08-07 18:50:26 UTC +++ src/cwutils/i18n.c @@ -50,7 +50,7 @@ i18n_initialize (void) const char * i18n_gettext (const char *msgid) { -#if defined(HAVE_LIBINTL_H) +#if defined(HAVE_GETTEXT) static int is_initialized = FALSE; if (!is_initialized) unixcw-3.6.0/patches/2018_08_FreeBSD/patch-src_xcwcp_Makefile.am0000644000175000017500000000051313674212270021007 00000000000000--- src/xcwcp/Makefile.am.orig 2018-08-07 21:13:40 UTC +++ src/xcwcp/Makefile.am @@ -61,13 +61,8 @@ CLEANFILES = moc_application.cc # Qt5 magic -if HOST_IS_FREEBSD -moc_application.cc: application.h - $(AC_QT5_MOC) application.h -o application.cc -else moc_application.cc: application.h $(AC_QT5_MOC) $< -o $@ -endif unixcw-3.6.0/icon_unixcw.svg0000644000175000017500000001426111663010242013023 00000000000000 image/svg+xml unixcw-3.6.0/missing0000755000175000017500000001533014000344554011356 00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2013-10-28.13; # UTC # Copyright (C) 1996-2014 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: unixcw-3.6.0/ChangeLog0000644000175000017500000011321314000344554011530 000000000000002019-10-02 Kamil Ignacak * libcw: big part of work on test framework has been completed. There is now a single test framework to execute all unit tests in the same fashion, and to collect and display test statistic in the same manner. The next big step in test code is to review test functions to ensure good code quality and clarity of tests. 2019-09-21 Kamil Ignacak * libcw: beginning to create libcw2.h in master branch with declarations of functions similar to those existing in libcw.h. Those new functions will not operate on library's global generator, key or receiver values, but on values passed as arguments. This will allow client code to use multiple generators, keys or receivers at the same time. These functions were already existing in code, but were used internally only. Now they will become public. 2017-02-11 Kamil Ignacak * xcwcp: fixing problem with compilation of application.cc file. 2016-05-03 Kamil Ignacak * libcw/bugfix: fixing a bug in libcw/generator that may have lead to a key being permanently in "down" state upon silencing a generator. If cwdaemon using libcw with NULL or Console output was generating tones from user input, and received ESC-4 escape request ("Abort currently sent message"), it was possible that while the message was interrupted, the key remained in "down" state. The fix in libcw makes sure that upon request to stop generating tones, the key always goes up, regardless of audio output type. The bug was reported and investigated, and fix was provided by Csahok Zoltan. Many thanks Zoli! 2016-03-30 Kamil Ignacak * debian: adding pt_BR.po file, this should close Debian bug #816939. 2016-01-18 Kamil Ignacak * cw: bug discovered in cw and fix provided by Rob Wortman: cw didn't read options from CW_OPTIONS environment variable. 2015-09-29 Kamil Ignacak * bugfix: fixing code that resets tone queue on flushing. A bug was found on FreeBSD: after pressing Ctrl+C, application that wanted to stop and delete generator as part of SIGINT handling procedure, got stuck in cw_tq_flush_internal() function, waiting for tone queue to go idle. This never happened. Resetting all tone queue state variables in flush function ensures that the function completes and returns, and that client application can exit. 2015-09-12 Kamil Ignacak * xcwcp: the application souce code files are now ported to Qt5. Build system files have been modified to use Qt5 to build xcwcp. Discovery and adding -fPIC to compiler flags for xcwcp is right now very naive, perhaps that will have to be improved in the future. 2015-09-09 Kamil Ignacak * alternative IPC: now that we have experimental branch, I decided to remove "alternative IPC" code from master branch and leave that just in "experimental" branch. master will be for small very incremental, and only necessary changes. At this point libcw code in master looks pretty much like it was in around commit d02491ee32668d5f5ca3904193f306956e72468d, i.e. pre-semaphores. 2015-09-08 Kamil Ignacak * git: created experimental_receiver branch, where all changes related to new ideas for receiver will be developed. master branch will be for boring, non-experimental code. 2015-09-03 Kamil Ignacak * xcwcp: fixed handling of backspace key in keyboard mode. For some time now the backspace key didn't remove yet-unplayed characters from text area. Now the key works correctly. 2015-09-01 Kamil Ignacak * xcwcp: simplifying class design in display.h. The file is now called textarea.h, the class/widget derived from QTextEdit is now called TextArea, and the change is reflected in few of xcwcp's source files. I hope to have less work in future thanks to this simplification. A bit less code to maintain, a bit less logic to understand. 2015-08-31 Kamil Ignacak * xcwcp: beginning of review of the the code, refactoring, updating comments. 2015-08-30 Kamil Ignacak * libcw/signals: improving signals code already existing in cw_gen and cw_tq modules. In particular change in cw_gen (in dequeue_and_play_internal()) greatly improves correctness of usage of semaphores so far. Fixing a FIXME in cw_tq_wait_for_tone_internal(): using new semaphore deq_semaphore for another aspect of communication between tq and generator makes the function fully ready for use of alternative IPC mechanism (the function doesn't need signals anymore). * libcw/ipc: adding new header file for new libcw module: libcw_ipc (Inter Process Communication). For now the module consists only of a header file with a single macro. * libcw/ipc: implementing cw_tq_wait_for_level_internal() using semaphores. Improving the part of cw_tq_wait_for_tone_queue_internal that used semaphores. This means that tq module can work without signals, with semaphores only. * libcw/tests: adding test_cw_tq_wait_for_level_internal() unit test function 2015-08-28 Kamil Ignacak * libcw/signals: Initial commit of semaphores code. The code enables communication between generator and tone queue using semaphores instead of signals. It may be desirable for libcw library to not to use signals, so that client application can use them freely, without any interference between application and libcw. This is the first step to replace signals with other IPC mechanisms. This alternative mechanism may be enabled with "--enable-signals-alternative" passed to ./configure script. 2015-08-02 Kamil Ignacak * tests: moving selected "make" test build targets from src/libcw/Makefile.am to src/libcw/tests/Makefile.am. Updating flags for some of the targets, so they may be built a bit differently * tests: libcw_test_public.c no longer tests "forever" feature. Since this is a test of public API, it shouldn't use LIBCW_UNIT_TESTS flag for compilation, nor should it use any internal or test functions. 2015-07-31 Kamil Ignacak * gen/tq: creating small client application that tests for presence of the problem described on 2015-07-29 ("short space" problem). The client application is located in src/libcw/tests/ and is executed as part of standard test suite ("make check"). 2015-07-29 Kamil Ignacak * gen/tq: one more attempt in cw_gen_play_eow_space_internal() to ensure that eow spaces are enqueued so that client software can have tq low level threshold set to 1. It should be stressed in release notes that client software cannot assume that eow space will be one tone. There may have been such implicit assumption in the past, but it was only implicit, and it became invalid some time ago. 2015-06-14 Kamil Ignacak * pre-release: updating information in selected files before release (updating version numbers, adding notes new releases etc.) 2015-03-19 Kamil Ignacak * fixes: Few minor changes in main code and test code that solve problems found during tests on FreeBSD 10. 2015-03-13 Kamil Ignacak * generator: fixing one possible cause of segfaults: limiting number of executions of cw_gen_silence_internal() function that somewhere down in call stack also called pthread_kill(). On some occasions (during library cleanup) the thread id passed to pthread_kill becomes invalid, and any subsequent calls to cw_gen_silence_internal() resulted in segfault. I have discovered this problem when testing src/cw/cw on Alpine Linux. Other parts of unixcw package may have been affected by this problem as well. I don't know how old the problem was (since when it was possible to create segfaults when exiting from cw on Alpine Linux). The fix was tested on Alpine Linux and on Debian GNU/Linux. 2015-03-10 Kamil Ignacak * tests: testing unixcw on Alpine Linux (with musl libc). Fixing problems in cw_oss and cw_gen modules. One important change in libcw made during tests on Alpine Linux: generator's thread function is now a joinable thread (it was a detached thread until now). I don't think that it will have any impact on libcw's client code, but I thought that I should mention this in ChangeLog. Also adding one (unit ?) test function to libcw_test_internal. 2015-03-05 Kamil Ignacak * cw_gen: today I've added second and third brand new unit test function for cw_gen module in libcw_gen.c file. At the beginning of this development cycle there were zero functions defined in libcw_gen.c (there were, and still are, functions testing cw_gen module in libcw_test_public.c). I think that's a progress :) During creation of these functions I had an opportunity to improve code and comments of generator's base functions. 2015-03-04 Kamil Ignacak * asserts: base libcw is now compiled with -DNDEBUG flag by default (asserts are "off"). The asserts are still "on" by default in "unit tests" libcw. Asserts are switched "on" in base libcw with "./configure --enable-dev". 2015-03-02 Kamil Ignacak * generator: First unit test intended to be executed on building "make check" target. The unit test function uncovered one error: when a generator is created, then is *not* started, and then is deleted, libcw called pthread_kill(&id, ...) on uninitialized id in cw_gen_delete(). This caused a program to crash. This has been fixed by adding a flag to cw_gen_t and additional code in cw_gen.c. 2015-02-25 Kamil Ignacak * tq: improving handling of tones in general, and "forever" tones in particular. I have created CW_TONE_INIT and CW_TONE_COPY macros that (re-)initialize a tone variable (all of its fields), and that copy values of fields from one cw_tone_t variable to other. There are so many places where I need to set values of fields of cw_tone_t, that I thought it would be beneficial to have some sort of constructor/initializer. A full constructor that mallocs() tone would be too much, but simple macro doing assignments is quite ok. Until now the tone queue manager and generator used special value CW_AUDIO_FOREVER_USECS assigned to cw_tone_t.usecs to indicate that a tone is "forever" tone. This had one downside: a regular, plain variable had special value that I had to constantly be aware of. Now the "usecs" is again a regular variable - nothing more than the length of tone. Information whether or not a tone is "forever" tone is now conveyed by new field in cw_tone_t: cw_tone_t.forever. 2015-02-23 Kamil Ignacak * cw_gen: Lots of small updates. Mostly in comments, but I have also removed few unused (commented out) lines of code and renamed some internal functions. One noticeable change was in cw_generator_set_tone_slope() - some corner cases of handling function's arguments were cleared in function's top level comments, and implementation has been updated accordingly. 2015-02-22 Kamil Ignacak * key, generator, tone queue: Small improvements at intersection of these modules. One example: after changing third argument to cw_key_ik_enqueue_symbol_internal(), the function no longer has to explicitly refer to length of Space/Dot/Dash stored in generator. Enqueueing symbols with proper length is now exclusively job of generator (at least in case of this function call). Also modifying names of some variables (so that they follow a convention used already in receiver) and functions (so that their purpose is more obvious). So in a nutshell: small improvements in key, generator and tone queue. 2015-02-22 Kamil Ignacak * libcw.c: after populating the file with "global" functions for generator, tone queue and receiver modules, I have also moved here the "global" functions from cw_key module. At this point the file contains definitions of global variables cw_receiver, cw_generator and cw_key (cw_key is static in this file). 2015-02-19 Kamil Ignacak * libcw.c: I have (re)created the file. It will contain all the functions that use global variables cw_generator, cw_receiver and maybe cw_key as implicit arguments. After moving such functions from other modules into libcw.c, other modules major should be completely free of global variables, and should only contain functions that operate on generators, receivers and keys as explicit function arguments. Then the function will have the old interface, and the new interface. And it will be easy to have multiple generators (or receivers) in the same application. 2014-12-30 Kamil Ignacak * valgrind: running valgrind on libcw_test_internal reveals few small problems in test code, and one small problem in "production" code. Fixing the problems. Thanks valgrind! 2014-12-20 Kamil Ignacak * tests: moving as much code as possible from libcw_test_public.c to respective module files. The tests that are still in libcw_test_public.c test library functions that depend on different modules (mostly tone queue and generator). Those library functions that can be tested in separation are tested in their module files. This should shorten the libcw_test_public.c file and ensure that test functions are defined as close to library functions as possible, so that library code and related test code are grouped together (thus improving modularization of library). There is still some work to be done in this area, but a good progress has been made. In the process of moving test functions to their modules, some test functions have been improved a bit. 2014-12-02 Kamil Ignacak * tests: improving unit tests for receiver module. The receiver's test code from libcw_test_public.c is being slowly moved to libcw_rec.c. receiver module is fairly independent from a generator or audio sinks, so it's ok to mat the tests internal in the libcw_rec.c file. They can be executed with simple "make check", and the execution takes very little time. 2014-11-28 Kamil Ignacak * tests: improving unit tests for receiver module. Working on functions that will automatically generate test data (input data for cw_start_receive_tone() and cw_end_receive_tone() functions) with different parameters 2014-11-24 Kamil Ignacak * tests: Test executables for libcw now accept -m option, which allows me to specify libcw module(s) to be tested. Option's values are: 'r' - receiver 't' - tone queue 'g' - generator 'k' - Morse key 'o' - other * cwutils: reformatting code in src/cwutils/cmdline.c. No functional changes. 2014-11-19 Kamil Ignacak More changes in receiver module. Refactoring and moving public functions' code to _internal() functions that accept pointer to receiver as first argument. Updating comments. If you depend on Receiver State names (strings starting with RS_) printed by library's debug messages, you may want to check changed values of RS enum and corresponding strings on top of libcw_rec.c - they have been changed. Making some terminology changes in libcw_rec.c - some things are now named in more consistent way (variable names, names of things). 2014-11-12 Kamil Ignacak Proceeding with changes, simplifications and refactoring in receiver module. Generator module is also affected. 2014-11-11 Kamil Ignacak Creating new module: libcw_rec. Moving code related to receiver from libcw.c to libcw_rec.c. Moving other code from libcw.c to other modules. libcw.c has been emptied and is now removed. 2014-08-17 Kamil Ignacak * build system: changes in build system files (configure.ac and src/libcw/Makefile.am) introduced to improve compilation on OpenBSD 5.5. 2014-08-16 Kamil Ignacak * README: reviewed contents of README file. Lots of stuff accumulated in the file over the time, making a first-to-read document a bit too large. Moved parts of the file to INSTALL, THANKS and HISTORY files. * INSTALL: reviewed contents of INSTALL file. Less unnecessary sentences, more details. Also added a generic installation instructions as usually found in INSTALL files. * files: added new files to dist package: THANKS HISTORY added new files to git repository only: qa_build_debian.sh qa_compile_headers.sh qa_test_configure_flags.sh (well this one is moved from ./tools) qa_valgrind_cwcp.sh 2014-08-12 Kamil Ignacak * libcw/utils: three more functions have been moved to libcw_utils: cw_version, cw_license and cw_get_audio_system_label, because, well, these functions look like utility functions. * libcw/signal: I couldn't resist and I moved signal-related functions from libcw.c to new module: libcw_signal. Bodies of these functions have not been modified, so you can compare contents of libcw_signal.c with contents of libcw.c from last release to see how the move has been done. * libcw/generator: I have moved some more code from libcw.c to libcw_gen.c. There is still some generator-related code left in libcw.c, but I promise not to touch it in this release. I think that I have enough of moving code. There is still some code left in libcw.c. Big part of this code is related to receiver. The next big task in my work on libcw should be understanding (or at least attempting to understand) the receiver code and its relation to other modules (especially to libcw_gen and libcw_key modules). Receiver code is one of last areas that I haven't really tried to digest. 2014-08-07 Kamil Ignacak * QT4: Simplifying configuration of QT4 in configure.ac: using PKG_CHECK_MODULES instead of custom shell script. Tests on Linux and FreeBSD ongoing. * FreeBSD & clang 3.3: successful build of "make check" target. Successful execution of libcw_test_simple_gen and libcw_test_internal. Tests on FreeBSD ongoing. 2014-08-06 Kamil Ignacak * FreeBSD: I managed to configure and build the package on FreeBSD 10. It worked! I only had to slightly adjust configure.ac and src/libcw/Makefile.am (dl library). There is still a bit of work on FreeBSD left to do in configure.ac. * OSS: During tests of OSS on FreeBSD I've found a bug that prevented libcw from correctly opening OSS sink (unnecessary function call). This bug has been fixed. This fix should affect Linux and FreeBSD. 2014-08-02 Kamil Ignacak * libcw/key: lots of changes in libcw_key module. After some trials and errors I've implemented a code with single data type for all types of code (cw_key_t) while maintaining separate states of straight key, iambic keyer, and tone-queue-key. The key and code handling it is not visible to users of public API. Behaviour of public API functions has not been changed. 2014-07-28 Kamil Ignacak * libcw: I think that for now I'm done with splitting libcw.c into parts. In about a week I have moved large portions of code from libcw.c to separate files. Different parts of libcw.c, implementing different functionality, are now in their own *.c/*.h files. These new modules are: libcw_data.c libcw_gen.c libcw_key.c libcw_tq.c libcw_utils.c It's very possible that in future I will move some more code from libcw.c to new files. Up until now the code in new files is pretty much the same as it was in libcw.c (the same order of functions, the same bodies of functions, pretty much copy-and-paste). If you want to check correctness of moving the functions to new files, you could do this by comparing libcw/*.c files from 28.07.2014 with original libcw.c from previous release (I did such comparing using KDE application 'kompare', everything looks ok to me). After today I may make some changes in *.c files that will make the movement of code less transparent (but of course keeping everything in git). In addition to moving parts core functionality of the library from libcw.c to new files, I have also reviewed test code and build targets in Makefile.am for building test applications. You can now build three test executables with "make check": libcw_test_internal libcw_test_public libcw_test_simple_gen C code used to build these test executables is now a bit more simple - it uses a bit less #ifdefs, and main() function for each executable is in its own separate *.c file. There is no main() function in libcw.c file anymore. The main goal for the splitting of libcw.c was to introduce more order into the project. libcw.c file contained almost 10k LOC. Almost all modules (with exception of audio systems) were in a single file. Functions implementing these modules were sometimes mixed. Mental navigation in the file was becoming difficult. The split should help me solve these problems. The description above was given to explain why and how the libcw.c has been modified so heavily in this release. The code is still in *.c files. No functionality has been removed. No functions have been added or removed. Looking from outside, the library hasn't changed. It has the same set of functions implementing the same functionality. The libcw.h and libcw_debug.h files haven't changed at all. Changes to libcw in this release, from users' and developers' point of view, are none. 2014-07-26 Kamil Ignacak * cwcp: fixing a stupid error that I introduced in one of previous releases: cwcp didn't use "combined" argv, so it didn't process CWCP_OPTIONS from env. Now it should work. Today's commit ends my main part of work on cwcp code. There were three main changes made to cwcp: first: fixing the bug mentioned above; second: calling few delwin()s to plug memory leaks; third: bringing the code into shape when it comes to project's indentation and comments style. 2014-07-23 Kamil Ignacak * libcw/test code: moving some code related to testing from libcw.c into other files. This little change triggered a larger change in test utilities for libcw. Now we have three libcw test executables built with "make check": libcw_test_internal, libcw_test_public, and libcw_test_simple. Each of them has its own .c file with its own main(). There is no main() function in libcw.c anymore. Explicit "standalone" target has been removed from src/libcw/Makefile.am. It has been replaced by libcw_test_simple executable built with "make check". 2014-07-22 Kamil Ignacak * libcw/keying code: Moving most of code related to iambic keyer, straight key, and general keying control into new file: libcw_key.c. Having separate things in separate files (instead of having everything in a single 10kLOC file) should make some things easier for me as a maintainer of libcw. 2014-07-21 Kamil Ignacak * libcw/tone queue: Moving most of code related to tone queue into new file: libcw_tq.c. * libcw/data: Moving code related to characters (and their representation), prosigns and phonetics to new file: libcw_data.c Having separate things in separate files (instead of having everything in a single 10kLOC file) should make some things easier for me as a maintainer of libcw. 2014-04-23 Kamil Ignacak * committing changes before a new release; the new release contains a fix of problem reported by Pino Zollo ZP4KFX. Se NEWS file for more details. 2013-12-08 Kamil Ignacak * cwgen: improving seed for random function. 2013-10-26 Kamil Ignacak * clocking of iambic keyer. There has been a problem with clocking iambic keyer when the state machine for the keyer was in motion. The clocking has been receiving imprecise timestamps, and so recognizing characters (e.g. in xcwcp's receiver) was difficult. Today I've committed code that fixes timing problems in clocking iambic keyer. The code updates receiver's timer with lengths of dequeued tones in cw_generator_dequeue_and_play_internal(). Receiver uses this updated timer as an argument to cw_{start|stop}_receive_tone(), and since the timer is updated with exact time values, the lengths of spaces and marks are more precise. Fixing the problem also required some changes in xcwcp/receiver.cc and xcwcp/application.cc. 2013-10-22 Kamil Ignacak * unit tests: adding first unit test in cwutils/dictionary module. 2013-10-19 Kamil Ignacak * unit tests: adding "tests::" and "testedin::" markings in functions' top level comments to indicate which functions are tested in which unit tests. Also rewriting parts of bodies of unit tests in libcwtest.c so that printout of test results is a bit cleaner. 2013-10-17 Kamil Ignacak * xcwcp: reviewing, commenting and reformatting code in receiver.cc - the file that uses libcw's receiver functionality. 2013-10-13 Kamil Ignacak * receiver code: more changes and reviewing of receiver code. Unit tests for two functions in recever section: cw_timestamp_validate_internal() and cw_timestamp_compare_internal(). I've reversed order of arguments for the first function, so the order now matches (output, input) pattern of parameters order. 2013-10-10 Kamil Ignacak * testing: some improvements in code testing receiving functions in libcwtest.c: simplifications, comments, refactoring. * refactoring in libcw.c: moving some free variables related to sending and receiving of Morse code into generator and receiver variables, respectively. 2013-10-06 Kamil Ignacak * tone queue: using "len" property to simplify management and monitoring of tone queue and its capacity. Code is now a bit simpler. 2013-10-05 Kamil Ignacak * tone queue: adding "len" property to tone queue. Adding and using it should simplify some aspects of queue management. 2013-10-04 Kamil Ignacak * tone queue: still working with capacity, head and tail. Updated data type for the three variables, it will be uint32_t. * debugging: introduced cw_assert() macro that should improve debugging and assertions. 2013-09-12 Kamil Ignacak * tone queue: more changes related to "capacity", and additionally "head" and "tail" properties of tone queue. No functionality changed yet, just some minor improvements, comments and tests. 2013-09-09 Kamil Ignacak * tone queue: refactoring code handling "capacity" of tone queue; two purposes: improved "testability" and possibility of assigning non-default capacities of tone queues. 2013-07-24 Kamil Ignacak * FreeBSD patches: applying patches for FreeBSD, provided by Diane Bruce (thank you!). Thanks to the patches the ./configure script now should support two new flags: --with-qt-includes= --with-qt-libraries= 2013-03-18 Kamil Ignacak * fixes: Thomas Beierlein has reported that some combinations of '--enable-*'/'--disable-*' flags result in a code that fails to compile. He also provided a patch that fixes it. I've created a tool ('unixcw/tools/test_configure_flags.sh') that tests all combinations of configure flags, which also detected one more faulty configuration, in addition to the ones discovered by Thomas. 2013-01-24 Kamil Ignacak * unit tests: src/libcw/libcwtests.c has been heavily refactored. The work on the file will continue to ensure that public API of libcw is tested as thoroughly as reasonably possible. * new functions: adding new functions: cw_character_is_valid() (replaces cw_check_character()) and cw_string_is_valid() (replaces cw_check_string()). The old functions are still available, but have been marked as deprecated. 2013-01-03 Kamil Ignacak * fixes: fixing a function in libcw that attempts to open audio device. Simple improvement of logic conditions makes it less possible (under certain circumstances) that the function will fail to open a device. * pre-release: new version of unicw will have version number 3.2.0. New version of libcw will have soname version 5.0.0. Implementing necessary changes related to the two pieces of information. Also attempting to build debian packages from unixcw source package, and fixing bugs and upgrading files in the process. Various other updates of files related to pre-release activities. 2012-12-31 Kamil Ignacak * distribution: src/libcw/libcw_debug.h is now marked as file to be installed along with libcw.h * generator: improved behaviour of cw_generator_new() in cases when the function failed to create a generator. Now if the function fails, the generator variable is fully 'de-initialized'. 2012-12-29 Kamil Ignacak * tone queue: improving the method of queuing a space character. This helps me avoid problems in some corner case (when a single space is queued, and tone queue's low watermark is set to 1). This problem has been spotted when testing cwdaemon. * compilation errors: unixcw/src/libcw/libcw.h: Rewriting three cases of usage of "__attribute__ ((deprecated(msg)))", some versions of gcc may not support "deprecated" with string argument (error reported by user). 2012-11-21 Kamil Ignacak * debugging: I have reviewed list of CW_DEBUG_* flags. Some of the names of the flags have changed. If you are using any of these flags, you need to review your code. debug module now provides updated macro printing debug messages. The macro now accepts 'severity level' flag (debug / info / warning / error). There are now three permanent debug objects provided by libcw. Two of them are only for debug purposes, the third one is for general use. Behaviour of the objects may be controlled by the severity flag, as well as by debug flags defining areas of code, for which the debugging is active. 2012-09-17 Kamil Ignacak * libcw: code implementing support for various audio backends has been moved from libcw.c to new files. That way we have less mess in libcw.c, better separation of modules, and cleaner space for improvements in code implementing support for audio backends (e.g. advanced API of PulseAudio). 2012-09-11 Kamil Ignacak * debug: adding src/libcw/libcw_debug.py - a tool that transforms timing information that may be printed by debugged libcw applications. The transformed timing information may be then plotted by plotting programs to gain insight into inner workings and time dependencies of different parts of libcw applications. The tool is rather imperfect and may need manual adjustments for every new situation, but hopefully it will turn out to be useful. * cwcp: refactoring cwcp.c, adding comments, fixing one small annoyance. 2012-08-15 Kamil Ignacak * tone slopes: libcw now can generate tones with slopes shaped as sine function or as raised cosine function. The 'raised cosine' slope is now default. Until now, shape of slopes has been calculated every time a tone has been generated. Now the shape is calculated only when some parameters of generator (volume, sample rate, length of slope) change. This should improve performance of libcw by one or two percents (no benchmarks were performed :/ ). 2012-08-11 Kamil Ignacak * debug: Adding src/libcw/libcw_debug.c module. Currently the module implements only debug events with time stamps, but most probably all debugging facilities will be moved to the module in future. 2012-08-06 Kamil Ignacak * build system configuration: fixing faulty logic checking "--disable-feature" flags in configure.ac. Bug reported by Thomas Beierlein. Thanks Thomas! 2012-07-04 Kamil Ignacak * libcw: fixing problem with interlocks in threaded code; since this is an important fix, I will have to prepare new release of unixcw. 2012-05-27 Kamil Ignacak * libcw: fixing an error with overflowing n_samples. It was spotted during some tests performed when reviewing files before new release. I guess that means that I'm in a phase of final tests of the package. * unixcw: bunch of changes that prepare the package for new release. New release number of unixcw will be 3.1.0, new soname of libcw will be 4:0:0. 2012-05-21 Kamil Ignacak * libcw: Implementing loading of PulseAudio and ALSA libraries at run-time (loading symbols from the two libraries - to be exact), instead of doing it at compile time (load time?). This means that support for PulseAudio and/or ALSA can be enabled during compile time, but user doesn't have to have PulseAudio and/or ALSA installed on target system in order to install libcw. In other words: PulseAudio and ALSA libraries are now recommended, but not required. This does not mean that the two libraries are somehow deprecated by libcw. It just means that installing libcw doesn't have to mean a necessity of installing too much additional packages. OSS - by its nature - doesn't have to be loaded to be used by libcw, it's just open(), write() and close(), so handling OSS is much easier and doesn't involve run time loading (the description is rather simplified, but still OSS is a different story than PulseAudio and ALSA libraries). 2012-05-17 Kamil Ignacak * libcw/pulseaudio: some general improvements in PulseAudio code; there may be a slight improvement in responsiveness, as I've added code that configures buffering attribute passed to pa_simple_new(). The configuration is very simple (read: code copied from the net), I suspect that it can be improved. * libcw/Null: Adding support for Null output. Null output is an empty device that doesn't produce a sound. It doesn't use any physical or logical device to output a sound. Its only function is to provide a timing information: it acts as if it accepts and plays x microseconds of sound, and it returns after the x microseconds. I'm not sure if this will turn out useful, maybe yes, maybe no. Currently it is implemented using single call to usleep(), without any checks of return value. This should be improved. Code from cwutils has been changed to use/provide functionality related to the new output. 2012-05-14 Kamil Ignacak * libcw/tone queue: cw_generator_write_sine_wave_internal(): using cw_signal_wait_internal() (together with pthread_kill() in enqueue function) instead of usleep() - this should decrease CPU usage in idle state; still needs to be tested and verified, but - overall - good idea; I think that this solution existed in libcw before I've started slaughtering the code, so consider this just rediscovering of a clever code; 2012-05-10 Kamil Ignacak * libcw/console: Console output now works correctly, playing nicely with queue()/dequeue() and the generator. 2012-05-06 Kamil Ignacak * cwcp/user interface: fixing small bug in code: till now modifying practice time didn't work correctly, any attempts to do so resulted in resetting the time to zero. Now this is fixed. 2012-05-01 Kamil Ignacak * libcw/experimental code: removed code that was disabled when CW_DEV_EXPERIMENTAL_WRITE was set to 1. 2012-05-01 Kamil Ignacak * libcw/audio: more changes in libcw; I'm decreasing dependency on timers, and increasing dependency on tone queue. I could put it that way: there are less places where time periods are dictated by itimers, and more places where time periods are measured by audio systems' 'write' functions. This way I am sure that when I'm sending X samples to audio sink, it - given sample rate Y - results in Z microseconds of sound. This seems like a good concept, it works well so far. I think that it will me impossible (and impractical) to get rid of all timers, but for generating audio - it works well. There may be a problem with console buzzer, as there is no audio sink that would accept X samples, but already I have an idea how to solve this. I've been using CW_DEV_EXPERIMENTAL_WRITE definition to enable new, experimental code, and disable old code. Since the new, experimental code works so well, I will completely get rid of the old code soon. 2012-04-25 Kamil Ignacak * libcw/ALSA: the problem described below has been solved, but at a cost of significant changes in how a Morse code sound is generated (timers are no longer used). This will almost certainly affect other parts of libcw (e.g. Morse keys handling), but it has advantages: timing of ALSA sound is (should be - to be tested) perfect, and I can easily add support for PulseAudio. (this change has been made and committed earlier, somewhere between 21.04 and 24.04, I'm just describing it now). * libcw/PulseAudio: Adding support for PulseAudio. PulseAudio only works with "experimental write" enabled, so there is no way that I can have both PulseAudio and old algorithm for generating audio. Either I will have to refactor/refresh/rework rest of libcw.c (and then have PulseAudio), or I will leave old way of generating audio (not breaking things (yet) in applications using libcw, but also not having PulseAudio). 2012-04-21 Kamil Ignacak * libcw/ALSA: I've noticed that there is a problem with waveform produced with ALSA. Dits and dashes start at correct time, but they are ended incorrectly - either too soon, or too late. This results in dots and dashes of incorrect length. The length differences are small, but audible. I have ruled out problems with itimer. itimer doesn't produce perfect time intervals, but error introduced by itimer is way to small to result in described problem. The problem is probably related to delay introduced by snd_pcm_writei(). I'm thinking about following solution: start generating tones (be it inter-symbol silence, or the symbols (dits and dashes) themselves) on signal generated by itimer, but stop generating dit/dash/silence after writing specific number of samples to audio device. In other words: start generating on timer events, stop generating on sample counter overflow. The problem doesn't seem to appear when using OSS output, perhaps because sound fragment size is much smaller than ALSA period size, e.g. 128 vs. 940. 2012-04-15 Kamil Ignacak * build system: Most important change: adding code that handles --disable-xxx options passed to the script: --disable-console --disable-oss --disable-alsa --disable-cwcp --disable-xcwcp The options allow disabling certain functionalities of unixcw, so that the package can be built on machines that don't provide some functions or properties. Example: unixcw 3.0.1 can't be compiled on hurd-i386 because of this error from configure script: "configure: error: Cannot find either sys/kd.h, sys/vtkd.h, or sys/kbio.h" At lease one of these headers is needed for console buzzer support. With new code in configure, compilation of code working with console buzzer can be disabled. Similarly, user can now disable compilation of OSS-related code on platforms that no longer provide OSS. Functionality can be disabled explicitly (with command line option), or implicitly - if any of tests performed by configure script fails. Changes described above required some modifications of libcw.c file. Also added --enable-dev. Build system now uses Makefile.am and Makefile.in files used/generated by Automake. Adding two calls to configure: AM_INIT_AUTOMAKE (build system now uses Automake to handle Makefile files), and AC_PROG_LIBTOOL (libtool is now used to create libraries). Build system now supports "make distcheck" target. Build system now depends on libtool. unixcw-3.6.0/src/0000755000175000017500000000000014001105773010623 500000000000000unixcw-3.6.0/src/libcw/0000755000175000017500000000000014001105773011723 500000000000000unixcw-3.6.0/src/libcw/libcw_tq.h0000644000175000017500000002420614000344554013625 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef H_LIBCW_TQ #define H_LIBCW_TQ #include "config.h" #include /* pthread_mutex_t */ #include /* bool */ #include /* uint32_t */ #include "libcw2.h" #if defined(__cplusplus) extern "C" { #endif /* Right now there is no function that would calculate number of tones representing given character or string, so there is no easy way to present exact relationship between capacity of tone queue and number of characters that it can hold. TODO: perhaps we could write utility functions to do that calculation? */ /* TODO: create tests that validate correctness of handling of tone queue capacity. See if we really handle the capacity correctly. */ enum { /* Default and maximum values of two basic parameters of tone queue: capacity and high water mark. The parameters can be modified using suitable function. */ /* Tone queue will accept at most "capacity" tones. */ CW_TONE_QUEUE_CAPACITY_MAX = 3000, /* ~= 5 minutes at 12 WPM */ /* Tone queue will refuse to accept new tones (TODO: tones or characters?) if number of tones in queue (queue length) is already equal or larger than queue's high water mark. */ CW_TONE_QUEUE_HIGH_WATER_MARK_MAX = 2900 }; /* Tone queue states (with totally random non-zero values). We need CW_TQ_EMPTY and CW_TQ_JUST_EMPTIED to detect a moment when generator has completed generating the last tone. When generator ends playing some tone, it goes to dequeueing next tone. If queue returns CW_TQ_JUST_EMPTIED, there is a chance that in the meantime some tone will be enqueued in the background. But if the queue returns CW_TQ_EMPTY, it means that the tone just generated by generator was truly the last tone. This is important for cw_tq_wait_for_end_of_current_tone_internal() that must be able to wait for end of any tone, including the end of last tone. With two-value cw_queue_state_t type (empty/non-empty), the function can't detect transition of generator from "I'm generating the last tone that was on queue" state to "I'm done with generating the last tone that was on queue". In both states cases the tq will be in "I'm empty" state, and cw_tq_wait_for_end_of_current_tone_internal() will return in the middle of generator's "I'm generating the last tone that was on queue". */ typedef enum { /* Initial state of queue, and state to which empty queue transitions from CW_TQ_JUST_EMPTIED, when asked again to dequeue a tone. */ CW_TQ_EMPTY = 45, /* Transitional state, when last element has been just removed. Next call to dequeue function will make the queue transition from this state to CW_TQ_EMPTY. */ CW_TQ_JUST_EMPTIED = 51, /* There is at least one element on the queue. */ CW_TQ_NONEMPTY = 74 } cw_queue_state_t; /* If there are any slopes in a tone, there can be only rising slope (without falling slope), falling slope (without rising slope), or both slopes (i.e. standard slopes). These values don't tell anything about shape of slopes (unless you consider 'no slopes' a shape). */ typedef enum cw_tone_slope_mode_t { CW_SLOPE_MODE_STANDARD_SLOPES, CW_SLOPE_MODE_NO_SLOPES, CW_SLOPE_MODE_RISING_SLOPE, CW_SLOPE_MODE_FALLING_SLOPE } cw_tone_slope_mode_t; /* TODO: come up with thought-out, consistent type system for samples count and tone duration. The type system should take into consideration very long duration of tones in QRSS. */ typedef int64_t cw_sample_iter_t; typedef struct { /* Frequency of a tone, in Hz. */ int frequency; /* Duration of a tone, in microseconds. */ int duration; /* Is this "forever" tone? See libcw_tq.c for more info about "forever" tones. */ bool is_forever; /* Is this the first tone of a character? Used to remove full character (all tones constituting a character) from the queue. */ bool is_first; /* Type/mode of slope(s) in a tone. */ cw_tone_slope_mode_t slope_mode; /* Duration of a tone, in samples. This is a derived value, a function of duration and sample rate. */ cw_sample_iter_t n_samples; /* Counter of samples from the tone that have been calculated and put into generator's buffer. If a tone has more samples than size of generator's buffer, then we will have to pass the same tone to generator's function few times, until whole length of tone will be re-calculated into samples and put into generator's buffer. This iterator will be advanced by generator's buffer size each time we pass it to the function. */ cw_sample_iter_t sample_iterator; /* a tone can start and/or end abruptly (which may result in audible clicks), or its beginning and/or end can have form of slopes (ramps), where amplitude increases/decreases less abruptly than if there were no slopes; using slopes reduces audible clicks at the beginning/end of tone, and can be used to shape spectrum of a tone; AFAIK most desired shape of a slope looks like sine wave; most simple one is just a linear slope; slope area should be integral part of a tone, i.e. it shouldn't make the tone longer than duration/n_samples; a tone with rising and falling slope should have this length (in samples): rising_slope_n_samples + (n_samples - 2 * slope_n_samples) + falling_slope_n_samples libcw allows following slope area scenarios (modes): 1. no slopes: tone shouldn't have any slope areas (i.e. tone with constant amplitude); 1.a. a special case of this mode is silent tone - amplitude of a tone is zero for whole duration of the tone; 2. tone has nothing more than a single slope area (rising or falling); there is no area with constant amplitude; 3. a regular tone, with area of rising slope, then area with constant amplitude, and then falling slope; currently, if a tone has both slopes (rising and falling), both slope areas have to have the same length; */ int rising_slope_n_samples; /* Number of samples on rising slope. */ int falling_slope_n_samples; /* Number of samples on falling slope. */ /* Useful for marking individual tones during debugging. */ char debug_id; } cw_tone_t; /* Set values of tone's fields. Some field are set with values given as arguments to the macro. Other are initialized with default values. The macro should be used like this: cw_tone_t my_tone; CW_TONE_INIT(&tone, 200, 5000, CW_SLOPE_MODE_STANDARD_SLOPES); */ #define CW_TONE_INIT(m_tone, m_frequency, m_duration, m_slope_mode) { \ (m_tone)->frequency = m_frequency; \ (m_tone)->duration = m_duration; \ (m_tone)->slope_mode = m_slope_mode; \ (m_tone)->is_forever = false; \ (m_tone)->is_first = false; \ (m_tone)->n_samples = 0; \ (m_tone)->sample_iterator = 0; \ (m_tone)->rising_slope_n_samples = 0; \ (m_tone)->falling_slope_n_samples = 0; \ (m_tone)->debug_id = 0; \ } /* Copy values of all fields from one variable of cw_tone_t type to the other. The macro accepts pointers to cw_tone_t variables as arguments. */ #define CW_TONE_COPY(m_dest, m_source) { \ (m_dest)->frequency = (m_source)->frequency; \ (m_dest)->duration = (m_source)->duration; \ (m_dest)->slope_mode = (m_source)->slope_mode; \ (m_dest)->is_forever = (m_source)->is_forever; \ (m_dest)->is_first = (m_source)->is_first; \ (m_dest)->n_samples = (m_source)->n_samples; \ (m_dest)->sample_iterator = (m_source)->sample_iterator; \ (m_dest)->rising_slope_n_samples = (m_source)->rising_slope_n_samples; \ (m_dest)->falling_slope_n_samples = (m_source)->falling_slope_n_samples; \ (m_dest)->debug_id = (m_source)->debug_id; \ }; struct cw_gen_struct; typedef struct { volatile cw_tone_t queue[CW_TONE_QUEUE_CAPACITY_MAX]; /* Tail index of tone queue. Index of last (newest) inserted tone, index of tone to be dequeued from the list as a last one. The index is incremented *after* adding a tone to queue. */ volatile size_t tail; /* Head index of tone queue. Index of first (oldest) tone inserted to the queue. Index of the tone to be dequeued from the queue as a first one. */ volatile size_t head; cw_queue_state_t state; size_t capacity; size_t high_water_mark; size_t len; /* It's useful to have the tone queue dequeue function call a client-supplied callback routine when the amount of data in the queue drops below a defined low water mark. This routine can then refill the buffer, as required. */ volatile size_t low_water_mark; void (* low_water_callback)(void *); void * low_water_callback_arg; /* Inter-thread communication. Used to broadcast queue events to waiting functions. */ pthread_cond_t wait_var; pthread_mutex_t wait_mutex; /* Generator associated with a tone queue. */ struct cw_gen_struct * gen; char label[LIBCW_OBJECT_INSTANCE_LABEL_SIZE]; } cw_tone_queue_t; cw_tone_queue_t * cw_tq_new_internal(void); void cw_tq_delete_internal(cw_tone_queue_t ** tq); void cw_tq_flush_internal(cw_tone_queue_t * tq); size_t cw_tq_capacity_internal(const cw_tone_queue_t * tq); size_t cw_tq_length_internal(cw_tone_queue_t * tq); cw_ret_t cw_tq_enqueue_internal(cw_tone_queue_t * tq, const cw_tone_t * tone); cw_queue_state_t cw_tq_dequeue_internal(cw_tone_queue_t * tq, cw_tone_t * tone); cw_ret_t cw_tq_wait_for_level_internal(cw_tone_queue_t * tq, size_t level); cw_ret_t cw_tq_register_low_level_callback_internal(cw_tone_queue_t * tq, cw_queue_low_callback_t callback_func, void * callback_arg, size_t level); bool cw_tq_is_nonempty_internal(const cw_tone_queue_t * tq); cw_ret_t cw_tq_wait_for_end_of_current_tone_internal(cw_tone_queue_t * tq); void cw_tq_reset_internal(cw_tone_queue_t * tq); bool cw_tq_is_full_internal(const cw_tone_queue_t * tq); cw_ret_t cw_tq_remove_last_character_internal(cw_tone_queue_t * tq); #if defined(__cplusplus) } #endif #endif /* #ifndef H_LIBCW_TQ */ unixcw-3.6.0/src/libcw/libcw_oss.c0000644000175000017500000005160714000344554014005 00000000000000/* Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** @file libcw_oss.c @brief OSS sound system. */ #include #include "config.h" #include "libcw_debug.h" #include "libcw_oss.h" #define MSG_PREFIX "libcw/oss: " extern cw_debug_t cw_debug_object; extern cw_debug_t cw_debug_object_ev; extern cw_debug_t cw_debug_object_dev; #ifdef LIBCW_WITH_OSS #include #include #include /* dlopen() and related symbols */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_SYS_SOUNDCARD_H) # include #elif defined(HAVE_SOUNDCARD_H) # include #else # #endif #include "libcw_gen.h" extern const unsigned int cw_supported_sample_rates[]; /* Conditional compilation flags. */ #define CW_OSS_SET_FRAGMENT 1 /* ioctl(fd, SNDCTL_DSP_SETFRAGMENT, ¶m) */ #define CW_OSS_SET_POLICY 0 /* ioctl(fd, SNDCTL_DSP_POLICY, ¶m) */ /* Constants specific to OSS sound system configuration. */ static const unsigned int CW_OSS_SETFRAGMENT = 7U; /* Sound fragment size, 2^7 samples. */ static const int CW_OSS_SAMPLE_FORMAT = AFMT_S16_NE; /* Sound format AFMT_S16_NE = signed 16 bit, native endianess; LE = Little endianess. */ static cw_ret_t cw_oss_open_device_ioctls_internal(int fd, unsigned int * sample_rate); static cw_ret_t cw_oss_get_version_internal(int fd, cw_oss_version_t * version); static cw_ret_t cw_oss_write_buffer_to_sound_device_internal(cw_gen_t * gen); static cw_ret_t cw_oss_open_and_configure_sound_device_internal(cw_gen_t * gen, const cw_gen_config_t * gen_conf); static void cw_oss_close_sound_device_internal(cw_gen_t * gen); /** @brief Check if it is possible to open OSS output with given device name Function does a test opening and test configuration of OSS output, but it closes it before returning. @reviewed 2020-07-19 @param[in] device_name name of OSS device to be used; if NULL or empty then the function will use library-default device name. @return true if opening OSS output succeeded @return false if opening OSS output failed */ bool cw_is_oss_possible(const char * device_name) { char picked_device_name[LIBCW_SOUND_DEVICE_NAME_SIZE] = { 0 }; cw_gen_pick_device_name_internal(device_name, CW_AUDIO_OSS, picked_device_name, sizeof (picked_device_name)); /* Open the given soundcard device file, for write only. */ int soundcard = open(picked_device_name, O_WRONLY); if (soundcard == -1) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_INFO, MSG_PREFIX "is possible: open(%s): '%s'", picked_device_name, strerror(errno)); return false; } { cw_oss_version_t version = { 0 }; cw_ret_t cw_ret = cw_oss_get_version_internal(soundcard, &version); if (cw_ret == CW_FAILURE) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "is possible: can't get OSS version '%s'", strerror(errno)); close(soundcard); return false; } else { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_INFO, MSG_PREFIX "is possible: OSS version %u.%u.%u", version.x, version.y, version.z); } } /* http://manuals.opensound.com/developer/OSS_GETVERSION.html: about OSS_GETVERSION ioctl: "This ioctl call returns the version number OSS API used in the current system. Applications can use this information to find out if the OSS version is new enough to support the features required by the application. However this methods should be used with great care. Usually it's recommended that applications check availability of each ioctl() by calling it and by checking if the call returned errno=EINVAL." So, we call all necessary ioctls to be 100% sure that all needed features are available. cw_oss_open_device_ioctls_internal() doesn't specifically look for EINVAL, it only checks return values from ioctl() and returns CW_FAILURE if one of ioctls() returns -1. */ unsigned int dummy = 0; cw_ret_t cw_ret = cw_oss_open_device_ioctls_internal(soundcard, &dummy); close(soundcard); if (cw_ret != CW_SUCCESS) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "is possible: one or more OSS ioctl() calls failed"); return false; } else { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_INFO, MSG_PREFIX "is possible: OSS is possible"); return true; } } /** @brief Configure given @p gen variable to work with OSS sound system This function only initializes @p gen by setting some of its members. It doesn't interact with sound system (doesn't try to open or configure it). @reviewed 2020-11-12 @param[in,out] gen generator structure to initialize @return CW_SUCCESS */ cw_ret_t cw_oss_init_gen_internal(cw_gen_t * gen) { assert (gen); gen->sound_system = CW_AUDIO_OSS; gen->open_and_configure_sound_device = cw_oss_open_and_configure_sound_device_internal; gen->close_sound_device = cw_oss_close_sound_device_internal; gen->write_buffer_to_sound_device = cw_oss_write_buffer_to_sound_device_internal; return CW_SUCCESS; } /** @brief Write generated samples to OSS sound system device configured and opened for generator @reviewed 2020-07-19 @param[in] gen generator that will write to sound device @return CW_SUCCESS on success @return CW_FAILURE otherwise */ cw_ret_t cw_oss_write_buffer_to_sound_device_internal(cw_gen_t * gen) { assert (gen); assert (gen->sound_system == CW_AUDIO_OSS); size_t n_bytes = sizeof (gen->buffer[0]) * gen->buffer_n_samples; ssize_t rv = write(gen->oss_data.sound_sink_fd, gen->buffer, n_bytes); if (rv != (ssize_t) n_bytes) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "write: %s", strerror(errno)); return CW_FAILURE; } // cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_INFO, MSG_PREFIX "written %d samples", gen->buffer_n_samples); return CW_SUCCESS; } /** @brief Open and configure OSS handle stored in given generator @reviewed 2020-11-14 @param[in] gen generator for which to open and configure sound system handle @param[in] gen_conf @return CW_FAILURE on errors @return CW_SUCCESS on success */ cw_ret_t cw_oss_open_and_configure_sound_device_internal(cw_gen_t * gen, const cw_gen_config_t * gen_conf) { if (gen->sound_device_is_open) { /* Ignore the call if the device is already open. */ return CW_SUCCESS; } cw_gen_pick_device_name_internal(gen_conf->sound_device, gen->sound_system, gen->picked_device_name, sizeof (gen->picked_device_name)); /* TODO: there seems to be some redundancy between cw_oss_open_and_configure_sound_device_internal() and is_possible() function. */ /* Open the given soundcard device file, for write only. */ gen->oss_data.sound_sink_fd = open(gen->picked_device_name, O_WRONLY); if (-1 == gen->oss_data.sound_sink_fd) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "open: open(%s): '%s'", gen->picked_device_name, strerror(errno)); return CW_FAILURE; } cw_ret_t cw_ret = cw_oss_open_device_ioctls_internal(gen->oss_data.sound_sink_fd, &gen->sample_rate); if (cw_ret != CW_SUCCESS) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "open: one or more OSS ioctl() calls failed"); close(gen->oss_data.sound_sink_fd); return CW_FAILURE; } int size = 0; /* Get fragment size in bytes, may be different than requested with ioctl(..., SNDCTL_DSP_SETFRAGMENT), and, in particular, can be different than 2^N. */ /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ int rv = ioctl(gen->oss_data.sound_sink_fd, SNDCTL_DSP_GETBLKSIZE, &size); if (-1 == rv) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "open: ioctl(SNDCTL_DSP_GETBLKSIZE): '%s'", strerror(errno)); close(gen->oss_data.sound_sink_fd); return CW_FAILURE; } if ((size & 0x0000ffff) != (1U << CW_OSS_SETFRAGMENT)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "open: OSS fragment size not set, %d", size); close(gen->oss_data.sound_sink_fd); /* FIXME */ return CW_FAILURE; } else { /* TODO: are we reporting size correctly? Shouldn't it be "(size & 0x0000ffff)" or maybe even "2^(size & 0x0000ffff)"? */ cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_INFO, MSG_PREFIX "open: OSS fragment size = %d", size); } gen->buffer_n_samples = size; cw_oss_get_version_internal(gen->oss_data.sound_sink_fd, &gen->oss_data.version); /* Mark sound sink as now open for business. */ gen->sound_device_is_open = true; #if CW_DEV_RAW_SINK gen->dev_raw_sink = open("/tmp/cw_file.oss.raw", O_WRONLY | O_TRUNC | O_NONBLOCK); #endif return CW_SUCCESS; } /** @brief Perform all necessary ioctl calls on opened OSS file descriptor Wrapper function for ioctl calls that need to be done when configuring file descriptor @p fd for OSS playback. @reviewed 2020-07-19 @param[in] fd file descriptor of open OSS file; @param[out] sample_rate sample rate configured by ioctl calls @return CW_FAILURE on errors @return CW_SUCCESS on success */ cw_ret_t cw_oss_open_device_ioctls_internal(int fd, unsigned int * sample_rate) { int parameter = 0; /* Ignored. */ /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ if (-1 == ioctl(fd, SNDCTL_DSP_SYNC, ¶meter)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_DSP_SYNC): '%s'", strerror(errno)); return CW_FAILURE; } #if 0 /* This ioctl call failed on FreeBSD 10, which resulted in libcw failing to open OSS device. A bit of digging on the web revealed this: OSS4: http://manuals.opensound.com/developer/SNDCTL_DSP_POST.html: "This ioctl call is provided for compatibility with older applications. It has no practical purpose and should in no case be used in new applications." */ parameter = 0; /* Ignored. */ if (-1 == ioctl(fd, SNDCTL_DSP_POST, ¶meter)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_DSP_POST): '%s'", strerror(errno)); return CW_FAILURE; } #endif /* Set the sample format. */ parameter = CW_OSS_SAMPLE_FORMAT; /* Don't cast second argument of ioctl() to int, because you will get this warning in dmesg (found on FreeBSD 12.1): "ioctl sign-extension ioctl ffffffffc0045005" */ /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ if (-1 == ioctl(fd, SNDCTL_DSP_SETFMT, ¶meter)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_DSP_SETFMT): '%s'", strerror(errno)); return CW_FAILURE; } if (parameter != CW_OSS_SAMPLE_FORMAT) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: sample format not supported"); /* TODO: can't we try some other sample format? */ return CW_FAILURE; } /* Set up mono/stereo mode. */ parameter = CW_AUDIO_CHANNELS; /* Don't cast second argument of ioctl() to int, because you will get this warning in dmesg (found on FreeBSD 12.1): "ioctl sign-extension ioctl ffffffffc0045006" */ /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ if (-1 == ioctl(fd, SNDCTL_DSP_CHANNELS, ¶meter)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_DSP_CHANNELS): '%s'", strerror(errno)); return CW_FAILURE; } if (parameter != CW_AUDIO_CHANNELS) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: number of channels not supported"); return CW_FAILURE; } /* Set up a standard sampling rate based on the notional correct value, and retain the one we actually get. */ unsigned int rate = 0; bool success = false; for (int i = 0; cw_supported_sample_rates[i]; i++) { rate = cw_supported_sample_rates[i]; /* Don't cast second argument of ioctl() to int, because you will get this warning in dmesg (found on FreeBSD 12.1): "ioctl sign-extension ioctl ffffffffc0045002" */ /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ if (0 == ioctl(fd, SNDCTL_DSP_SPEED, &rate)) { if (rate != cw_supported_sample_rates[i]) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_WARNING, MSG_PREFIX "ioctls: imprecise sample rate:"); cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_WARNING, MSG_PREFIX "ioctls: asked for: %u", cw_supported_sample_rates[i]); cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_WARNING, MSG_PREFIX "ioctls: got: %u", rate); } success = true; break; } } if (!success) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_DSP_SPEED): '%s'", strerror(errno)); return CW_FAILURE; } else { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_INFO, MSG_PREFIX "OSS sample rate = %u\n", rate); *sample_rate = rate; } audio_buf_info buff; /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ if (-1 == ioctl(fd, SNDCTL_DSP_GETOSPACE, &buff)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_DSP_GETOSPACE): '%s'", strerror(errno)); return CW_FAILURE; } else { /* fprintf(stderr, "before:\n"); fprintf(stderr, "buff.fragments = %d\n", buff.fragments); fprintf(stderr, "buff.fragsize = %d\n", buff.fragsize); fprintf(stderr, "buff.bytes = %d\n", buff.bytes); fprintf(stderr, "buff.fragstotal = %d\n", buff.fragstotal); */ } #if CW_OSS_SET_FRAGMENT /* * Live a little dangerously, by trying to set the fragment size of the * card. We'll try for a relatively short fragment of 128 bytes. This * gives us a little better granularity over the amounts of audio data * we write periodically to the soundcard output buffer. We may not get * the requested fragment size, and may be stuck with the default. The * argument has the format 0xMMMMSSSS - fragment size is 2^SSSS, and * setting 0x7fff for MMMM allows as many fragments as the driver can * support. */ /* parameter = 0x7fff << 16 | CW_OSS_SETFRAGMENT; */ parameter = 0x0032U << 16U | CW_OSS_SETFRAGMENT; /* Don't cast second argument of ioctl() to int, because you will get this warning in dmesg (found on FreeBSD 12.1): "ioctl sign-extension ioctl ffffffffc004500a" */ /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ if (-1 == ioctl(fd, SNDCTL_DSP_SETFRAGMENT, ¶meter)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_DSP_SETFRAGMENT): '%s'", strerror(errno)); return CW_FAILURE; } cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_INFO, MSG_PREFIX "ioctls: fragment size is 2^%d = %d", parameter & 0x0000ffff, 2 << ((parameter & 0x0000ffffU) - 1)); /* Query fragment size just to get the driver buffers set. */ /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ if (-1 == ioctl(fd, SNDCTL_DSP_GETBLKSIZE, ¶meter)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_DSP_GETBLKSIZE): '%s'", strerror(errno)); return CW_FAILURE; } if (parameter != (1U << CW_OSS_SETFRAGMENT)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: OSS fragment size not set, %d", parameter); } #endif #if CW_OSS_SET_POLICY parameter = 5; /* TODO: what does this value mean? */ if (-1 == ioctl(fd, SNDCTL_DSP_POLICY, ¶meter)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_DSP_POLICY): '%s'", strerror(errno)); return CW_FAILURE; } #endif /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ if (-1 == ioctl(fd, SNDCTL_DSP_GETOSPACE, &buff)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "ioctls: ioctl(SNDCTL_GETOSPACE): '%s'", strerror(errno)); return CW_FAILURE; } else { /* fprintf(stderr, "after:\n"); fprintf(stderr, "buff.fragments = %d\n", buff.fragments); fprintf(stderr, "buff.fragsize = %d\n", buff.fragsize); fprintf(stderr, "buff.bytes = %d\n", buff.bytes); fprintf(stderr, "buff.fragstotal = %d\n", buff;3R.fragstotal); */ } return CW_SUCCESS; } /** @brief Close OSS device stored in given generator @reviewed 2020-07-19 @param[in] gen generator for which to close its sound device */ void cw_oss_close_sound_device_internal(cw_gen_t * gen) { close(gen->oss_data.sound_sink_fd); gen->oss_data.sound_sink_fd = -1; gen->sound_device_is_open = false; #if CW_DEV_RAW_SINK if (gen->dev_raw_sink != -1) { close(gen->dev_raw_sink); gen->dev_raw_sink = -1; } #endif return; } /** @brief Get version number of OSS API @reviewed 2020-09-14 @param[in] fd opened file descriptor for OSS device @param[out] version structure with version digits @return CW_SUCCESS on success @return CW_FAILURE otherwise */ cw_ret_t cw_oss_get_version_internal(int fd, cw_oss_version_t * version) { assert (fd != -1); int parameter = 0; /* Don't let clang-tidy report warning about signed. To fix the warning we would have to introduce casting, and that would introduce runtime warnings in dmesg on FreeBSD. */ /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ if (-1 == ioctl(fd, OSS_GETVERSION, ¶meter)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "get version: ioctl OSS_GETVERSION"); return CW_FAILURE; } else { const unsigned int u_parameter = (unsigned int) parameter; version->x = (u_parameter & 0xFF0000U) >> 16U; version->y = (u_parameter & 0x00FF00U) >> 8U; version->z = (u_parameter & 0x0000FFU) >> 0U; return CW_SUCCESS; } } #else /* #ifdef LIBCW_WITH_OSS */ bool cw_is_oss_possible(__attribute__((unused)) const char * device) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_INFO, MSG_PREFIX "This sound system has been disabled during compilation"); return false; } cw_ret_t cw_oss_init_gen_internal(__attribute__((unused)) cw_gen_t * gen) { cw_debug_msg (&cw_debug_object, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_INFO, MSG_PREFIX "This sound system has been disabled during compilation"); return CW_FAILURE; } #endif /* #ifdef LIBCW_WITH_OSS */ unixcw-3.6.0/src/libcw/libcw_oss.h0000644000175000017500000000076114000344554014005 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef H_LIBCW_OSS #define H_LIBCW_OSS typedef struct cw_oss_version { unsigned int x; unsigned int y; unsigned int z; } cw_oss_version_t; typedef struct cw_oss_data_struct { cw_oss_version_t version; int sound_sink_fd; } cw_oss_data_t; #include "libcw_gen.h" cw_ret_t cw_oss_init_gen_internal(cw_gen_t * gen); #endif /* #ifndef H_LIBCW_OSS */ unixcw-3.6.0/src/libcw/libsigs.awk0000644000175000017500000000236114000344554014006 00000000000000#!/bin/awk -f # # Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) # Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # # AWK script to produce function signatures from processed C source. # Pass output of libdoc.awk script to input of this script. # Initialize tags BEGIN { FUNCTION_TAG = "F" } # Handle lines containing function specification $1 == FUNCTION_TAG { sub(FUNCTION_TAG, "") sub(/^ */, "") gsub(/\t/, " ") printf(".BI \"%s\"\n.br\n", $0) next } # Tidy up on end of file END { print(".fi\n") } unixcw-3.6.0/src/libcw/libcw_rec.h0000644000175000017500000002153214000344554013751 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef H_LIBCW_REC #define H_LIBCW_REC #include #include /* struct timeval */ #include "libcw.h" #include "libcw2.h" #include "libcw_debug.h" /* Dot duration magic number. From PARIS calibration, 1 Dot duration [us] = 1200000 / speed [wpm]. This variable is used in generator code as well. */ enum { CW_DOT_CALIBRATION = 1200000 }; /* State of receiver state machine. "RS" stands for "Receiver State" */ typedef enum { RS_IDLE, /* Representation buffer is empty and ready to accept data. */ RS_MARK, /* Between begin and end of Mark (Dot or Dash). */ RS_INTER_MARK_SPACE, /* Space between Marks within one character. */ RS_EOC_GAP, /* Gap after a character, without error (EOC = end-of-character). */ RS_EOW_GAP, /* Gap after a word, without error (EOW = end-of-word). */ RS_EOC_GAP_ERR, /* Gap after a character, with error. */ RS_EOW_GAP_ERR /* Gap after a word, with error. */ } cw_rec_state_t; /* Does receiver initially adapt to varying speed of input data? */ enum { CW_REC_ADAPTIVE_MODE_INITIAL = false }; /* TODO: it would be interesting to track (in debug mode) relationship between "speed threshold" and "noise threshold" parameters. */ enum { CW_REC_SPEED_THRESHOLD_INITIAL = (CW_DOT_CALIBRATION / CW_SPEED_INITIAL) * 2 }; /* Initial adaptive speed threshold. [us] */ enum { CW_REC_NOISE_THRESHOLD_INITIAL = (CW_DOT_CALIBRATION / CW_SPEED_MAX) / 2 }; /* Initial noise filter threshold. */ /* Receiver contains a fixed-length buffer for representation of received data. Capacity of the buffer is vastly longer than any practical representation. Don't know why, a legacy thing. Representation can be presented as a char string. This is the maximal length of the string. This value does not include string's ending NULL. */ enum { CW_REC_REPRESENTATION_CAPACITY = 256 }; /* TODO: what is the relationship between this constant and CW_REC_REPRESENTATION_CAPACITY? Both have value of 256. Coincidence? I don't think so. */ enum { CW_REC_DURATION_STATS_CAPACITY = 256 }; /* Length of array used to calculate average duration of a mark. Average duration of a mark is used in adaptive receiving mode to track speed of incoming Morse data. */ enum { CW_REC_AVERAGING_DURATIONS_COUNT = 4 }; /* Types of receiver's timing statistics. CW_REC_STAT_NONE must be zero so that the statistics buffer is initially empty. */ typedef enum { CW_REC_STAT_NONE = 0, CW_REC_STAT_DOT, /* Dot mark. */ CW_REC_STAT_DASH, /* Dash mark. */ CW_REC_STAT_INTER_MARK_SPACE, /* Space between Dots and Dashes within one character. */ CW_REC_STAT_INTER_CHARACTER_SPACE /* Space between characters within one word. */ } stat_type_t; typedef struct { stat_type_t type; /* Record type */ int duration_delta; /* Difference between actual and ideal duration of mark or space. [us] */ } cw_rec_duration_stats_point_t; /* A moving averages structure - circular buffer. Used for calculating averaged duration ([us]) of dots and dashes. */ typedef struct { int buffer[CW_REC_AVERAGING_DURATIONS_COUNT]; /* Buffered mark durations. */ int cursor; /* Circular buffer cursor. */ int sum; /* Running sum of durations of marks. [us] */ int average; /* Averaged duration of a mark. [us] */ } cw_rec_averaging_t; struct cw_rec_struct { cw_rec_state_t state; /* Essential parameters. */ /* Changing values of speed, tolerance, gap or is_adaptive_receive_mode will trigger a recalculation of low level timing parameters. */ /* 'speed' is float instead of being 'int' on purpose. It makes adaptation to varying speed of incoming data more smooth. This is especially important at low speeds, where change/adaptation from (int) 5wpm to (int) 4wpm would mean a sharp decrease by 20%. With 'float' data type the adjustment of receive speeds is more gradual. */ float speed; /* [wpm] */ int tolerance; int gap; /* Inter-character-gap, similar as in generator. */ bool is_adaptive_receive_mode; int noise_spike_threshold; /* Library variable which is automatically adjusted based on incoming Morse data stream, rather than being settable by the user. Not exactly a *speed* threshold, but for a lack of a better name... When the library changes internally value of this variable, it recalculates low level timing parameters too. */ int adaptive_speed_threshold; /* [microseconds]/[us] */ /* Retained timestamps of mark's begin and end. */ struct timeval mark_start; struct timeval mark_end; /* Buffer for received representation (dots/dashes). This is a fixed-length buffer, filled in as tone on/off timings are taken. The buffer is vastly longer than any practical representation. Along with it we maintain a cursor indicating the current write position. */ char representation[CW_REC_REPRESENTATION_CAPACITY + 1]; int representation_ind; /* Receiver's low-level timing parameters */ /* These are basic timing parameters which should be recalculated each time client code demands changing some higher-level parameter of receiver. How these values are calculated depends on receiving mode (fixed/adaptive). */ int dot_duration_ideal; /* Duration of an ideal dot. [microseconds]/[us] */ int dot_duration_min; /* Minimal duration of mark that will be identified as dot. [us] */ int dot_duration_max; /* Maximal duration of mark that will be identified as dot. [us] */ int dash_duration_ideal; /* Duration of an ideal dash. [us] */ int dash_duration_min; /* Minimal duration of mark that will be identified as dash. [us] */ int dash_duration_max; /* Maximal duration of mark that will be identified as dash. [us] */ int ims_duration_ideal; /* Ideal inter-mark-space, for stats. [us] */ int ims_duration_min; /* Shortest inter-mark-space allowable. [us] */ int ims_duration_max; /* Longest inter-mark-space allowable. [us] */ int ics_duration_ideal; /* Ideal inter-character-space, for stats. [us] */ int ics_duration_min; /* Shortest inter-character-space allowable. [us] */ int ics_duration_max; /* Longest inter-character-space allowable. [us] */ /* These two fields have the same function as in cw_gen_t. They are needed in function re-synchronizing parameters. */ int additional_delay; /* More delay at the end of a char. [us] */ int adjustment_delay; /* More delay at the end of a word. [us] */ /* Are receiver's parameters in sync? After changing receiver's essential parameters, its low-level timing parameters need to be re-calculated. This is a flag that shows when this needs to be done. */ bool parameters_in_sync; /* Receiver statistics. A circular buffer of entries indicating the difference between the actual and the ideal duration of received mark or space, tagged with the type of statistic held, and a circular buffer pointer. */ cw_rec_duration_stats_point_t duration_stats[CW_REC_DURATION_STATS_CAPACITY]; int duration_stats_idx; /* Data structures for calculating averaged duration of dots and dashes. The averaged durations are used for adaptive tracking of receiver's speed (tracking of speed of incoming data). */ cw_rec_averaging_t dot_averaging; cw_rec_averaging_t dash_averaging; #define REC_HAS_PENDING_INTER_WORD_SPACE_FLAG 0 #if REC_HAS_PENDING_INTER_WORD_SPACE_FLAG /* Flag indicating if receive polling has received a character, and may need to augment it with a word space on a later poll. */ bool is_pending_inter_word_space; #endif char label[LIBCW_OBJECT_INSTANCE_LABEL_SIZE]; }; /* Other helper functions. */ void cw_rec_reset_parameters_internal(cw_rec_t * rec); void cw_rec_sync_parameters_internal(cw_rec_t * rec); void cw_rec_get_parameters_internal(cw_rec_t * rec, int * dot_duration_ideal, int * dash_duration_ideal, int * dot_duration_min, int * dot_duration_max, int * dash_duration_min, int * dash_duration_max, int * ims_duration_min, int * ims_duration_max, int * ims_duration_ideal, int * ics_duration_min, int * ics_duration_max, int * ics_duration_ideal, int * adaptive_threshold); void cw_rec_get_statistics_internal(const cw_rec_t * rec, float * dot_sd, float * dash_sd, float * inter_mark_space_sd, float * inter_character_space_sd); int cw_rec_get_buffer_length_internal(const cw_rec_t * rec); int cw_rec_get_receive_buffer_capacity_internal(void); void cw_rec_set_state_internal(cw_rec_t * rec, cw_rec_state_t new_state); #endif /* #ifndef H_LIBCW_REC */ unixcw-3.6.0/src/libcw/libcw_data.c0000644000175000017500000012702714000344554014112 00000000000000/* Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** @file libcw_data.c @brief Characters, representations, lookup and validation functions. The only hard data stored by libcw library seems to be: @li characters and their representations @li procedural signals @li phonetics These three groups of data, collected in three separate tables, are defined in this file, together with lookup functions and other related utility functions. Unit test functions for this code are at the end of the file. */ #include #include #include /* UCHAR_MAX */ #include #include #include #include "libcw.h" #include "libcw_data.h" #include "libcw_debug.h" #define MSG_PREFIX "libcw/data: " extern cw_debug_t cw_debug_object; extern cw_debug_t cw_debug_object_ev; extern cw_debug_t cw_debug_object_dev; static __attribute__((constructor)) void cw_data_constructor_internal(void); /* Morse code characters table. This table allows lookup of the Morse representation of a given alphanumeric character. Representations are held as a string, with "-" representing Dash, and "." representing Dot. The table ends with a NULL entry. Notice that ASCII characters are stored as uppercase characters. */ static size_t g_main_table_maximum_representation_length; static int g_main_table_characters_count; static const cw_entry_t * g_main_table_fast_lookup[UCHAR_MAX]; /* Fast lookup table */ const cw_entry_t CW_TABLE[] = { /* TODO: make it accessible through function only, and add static keyword. */ /* ASCII 7bit letters */ {'A', ".-" }, {'B', "-..."}, {'C', "-.-."}, {'D', "-.." }, {'E', "." }, {'F', "..-."}, {'G', "--." }, {'H', "...."}, {'I', ".." }, {'J', ".---"}, {'K', "-.-" }, {'L', ".-.."}, {'M', "--" }, {'N', "-." }, {'O', "---" }, {'P', ".--."}, {'Q', "--.-"}, {'R', ".-." }, {'S', "..." }, {'T', "-" }, {'U', "..-" }, {'V', "...-"}, {'W', ".--" }, {'X', "-..-"}, {'Y', "-.--"}, {'Z', "--.."}, /* Numerals */ {'0', "-----"}, {'1', ".----"}, {'2', "..---"}, {'3', "...--"}, {'4', "....-"}, {'5', "....."}, {'6', "-...."}, {'7', "--..."}, {'8', "---.."}, {'9', "----."}, /* Punctuation */ {'"', ".-..-."}, {'\'', ".----."}, {'$', "...-..-"}, {'(', "-.--." }, {')', "-.--.-"}, {'+', ".-.-." }, {',', "--..--"}, {'-', "-....-"}, {'.', ".-.-.-" }, {'/', "-..-." }, {':', "---..."}, {';', "-.-.-." }, {'=', "-...-" }, {'?', "..--.."}, {'_', "..--.-" }, {'@', ".--.-."}, /* ISO 8859-1 accented characters */ {'\334', "..--" }, /* U with diaeresis */ {'\304', ".-.-" }, /* A with diaeresis */ {'\307', "-.-.."}, /* C with cedilla */ {'\326', "---." }, /* O with diaeresis */ {'\311', "..-.."}, /* E with acute */ {'\310', ".-..-"}, /* E with grave */ {'\300', ".--.-"}, /* A with grave */ {'\321', "--.--"}, /* N with tilde */ /* ISO 8859-2 accented characters */ {'\252', "----" }, /* S with cedilla */ {'\256', "--..-"}, /* Z with dot above */ /* Non-standard procedural signal extensions to standard CW characters. */ {'<', "...-.-" }, /* VA/SK, end of work */ {'>', "-...-.-"}, /* BK, break */ {'!', "...-." }, /* SN, understood */ {'&', ".-..." }, /* AS, wait */ {'^', "-.-.-" }, /* KA, starting signal */ {'~', ".-.-.." }, /* AL, paragraph */ {0, NULL} /* Guard. */ }; /** @brief Return the number of characters present in main character lookup table Return the number of characters that are known to libcw. The number includes: @li ASCII 7bit letters, @li numerals, @li punctuation, @li ISO 8859-1 accented characters, @li ISO 8859-2 accented characters, @li non-standard procedural signal extensions to standard CW characters. @internal @reviewed 2020-07-25 @endinternal @return number of characters known to libcw */ int cw_get_character_count(void) { #if 0 /* Disabled on 2020-07-25. Moved to library constructor. */ static int g_main_table_characters_count = 0; if (0 == g_main_table_characters_count) { for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { g_main_table_characters_count++; } } #endif return g_main_table_characters_count; } /** @brief Get list of characters present in character lookup table Function provides a string containing all of the characters represented in library's lookup table. The list includes: @li ASCII 7bit letters, @li numerals, @li punctuation, @li ISO 8859-1 accented characters, @li ISO 8859-2 accented characters, @li non-standard procedural signal extensions to standard CW characters. @p list should be allocated and managed by caller. The length of @p list must be at least one greater than the number of characters represented in the character lookup table, returned by cw_get_character_count(). The string placed in @p list will be NUL-terminated. @internal @reviewed 2020-07-25 @endinternal @param[out] list buffer for string with all characters */ void cw_list_characters(char * list) { cw_assert (list, MSG_PREFIX "output pointer is NULL"); /* Append each table character to the output string. */ int i = 0; for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { list[i++] = cw_entry->character; } list[i] = '\0'; return; } /** @brief Get length of the longest representation of characters Function returns the string length of the longest representation in the main character lookup table. It is the count of dots and dashes in the longest representation of characters known to libcw (not including terminating NUL). @internal @reviewed 2020-07-25 @endinternal @return length of the longest representation */ int cw_get_maximum_representation_length(void) { #if 0 /* Disabled on 2020-07-25. Code moved to library constructor. */ static size_t g_main_table_maximum_representation_length = 0; if (0 == g_main_table_maximum_representation_length) { /* Traverse the main lookup table, finding the longest representation. */ for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { size_t length = strlen(cw_entry->representation); if (length > g_main_table_maximum_representation_length) { g_main_table_maximum_representation_length = length; } } } #endif return (int) g_main_table_maximum_representation_length; } /** @brief Return representation of given character Look up the given character @p character, and return the representation of that character. Return NULL if there is no representation for the given character. Otherwise return pointer to static string with representation of character. The returned pointer is owned and managed by library. @internal @reviewed 2020-07-25 @endinternal @param[in] character character to look up @return pointer to string with representation of character on success @return NULL on failure (when @p character has no representation) */ const char * cw_character_to_representation_internal(int character) { #if 0 /* Disabled on 2020-07-25. Moved to library constructor. */ static const cw_entry_t * g_main_table_fast_lookup[UCHAR_MAX]; /* Fast lookup table */ static bool is_initialized = false; /* If this is the first call, set up the fast lookup table to give direct access to the CW table for a given character. */ if (!is_initialized) { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_INFO, MSG_PREFIX "initializing fast lookup table"); for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { g_main_table_fast_lookup[(unsigned char) cw_entry->character] = cw_entry; } is_initialized = true; } #endif /* There is no differentiation in the lookup and representation table between upper and lower case characters; everything is held as uppercase. So before we do the lookup, we convert to ensure that both cases work. */ character = toupper(character); /* Now use the table to lookup the table entry. Unknown characters return NULL, courtesy of the fact that explicitly uninitialized static variables are initialized to zero, so lookup[x] is NULL if it's not assigned to in the above loop. */ const cw_entry_t * cw_entry = g_main_table_fast_lookup[(unsigned char) character]; /* Lookups code may be called frequently, so first do a rough test with cw_debug_has_flag(). */ if (cw_debug_has_flag(&cw_debug_object, CW_DEBUG_LOOKUPS)) { if (cw_entry) { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_DEBUG, MSG_PREFIX "char to representation: '%c' -> '%c'/'%s'\n", character, cw_entry->character, cw_entry->representation); } else if (isprint(character)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_DEBUG, MSG_PREFIX "char to representation: '%c' -> NOTHING\n", character); } else { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_DEBUG, MSG_PREFIX "char to representation: '0x%02x' -> NOTHING\n", (unsigned int) character); } } return cw_entry ? cw_entry->representation : NULL; } /** @brief Get representation of a given character The function is depreciated, use cw_character_to_representation() instead. Return the string representation of a given character @p character. The routine returns CW_SUCCESS on success, and fills in the string pointer (@p representation) passed in. On failure, it returns CW_FAILURE and sets errno to ENOENT, indicating that the character @p character could not be found. The length of @p representation buffer must be at least one greater than the length of longest representation held in the character lookup table. The largest value of length is returned by cw_get_maximum_representation_length(). @internal @reviewed 2020-07-25 @endinternal @param[in] character character to look up @param[out] representation pointer to space for representation of character @return CW_SUCCESS on success @return CW_FAILURE on failure */ int cw_lookup_character(char character, char * representation) { /* Lookup the character, and if found, return the string. */ const char * looked_up = cw_character_to_representation_internal(character); if (looked_up) { if (representation) { strncpy(representation, looked_up, g_main_table_maximum_representation_length); representation[g_main_table_maximum_representation_length] = '\0'; } return CW_SUCCESS; } /* Failed to find the requested character. */ errno = ENOENT; return CW_FAILURE; } /** @brief Get representation of a given character On success return representation of a given character. Returned pointer is owned by caller of the function. On failure function returns NULL and sets errno. @exception ENOENT the character could not be found. @exception ENOMEM character has been found, but function failed to strdup() representation. @internal @reviewed 2020-07-25 @endinternal @param[in] character character to look up @return pointer to freshly allocated representation on success @return NULL on failure */ char * cw_character_to_representation(int character) { /* Lookup representation of the character, and if found, return copy of the representation. */ const char * looked_up = cw_character_to_representation_internal(character); if (NULL == looked_up) { errno = ENOENT; return NULL; } char * r = strdup(looked_up); if (NULL == r) { errno = ENOMEM; return NULL; } return r; } /** @brief Return a hash value of a character representation Return a hash value, in the range CW_DATA_MIN_REPRESENTATION_HASH-CW_DATA_MAX_REPRESENTATION_HASH, for a character's @p representation. The routine returns 0 if no valid hash could be made from the @p representation string. This hash algorithm is designed ONLY for valid CW representations; that is, strings composed of only "." and "-". The CW representations can be no longer than seven characters. TODO: create unit test that verifies that the longest representation recognized by libcw is in fact no longer than 7. TODO: consider creating an implementation that has the limit larger than 7. Then perhaps make the type of returned value to be uint16_t. The algorithm simply turns the representation string into a number, a "bitmask", based on pattern of "." and "-" in @p representation. The first bit set in the mask indicates the start of data (hence the 7-character limit) - it is not the data itself. This mask is viewable as an integer in the range CW_DATA_MIN_REPRESENTATION_HASH (".") to CW_DATA_MAX_REPRESENTATION_HASH ("-------"), and can be used as an index into a fast lookup array. @internal @reviewed 2020-07-26 @endinternal @param[in] representation string representing a character @return non-zero value of hash of valid representation (in range CW_DATA_MIN_REPRESENTATION_HASH-CW_DATA_MAX_REPRESENTATION_HASH) @return zero for invalid representation */ unsigned int cw_representation_to_hash_internal(const char * representation) { /* Our algorithm can handle only 7 characters of representation. And we insist on there being at least one character, too. */ const size_t length = strlen(representation); if (length > CW_DATA_MAX_REPRESENTATION_LENGTH || length < 1) { return 0; } /* Build up the hash based on the dots and dashes; start at 1, the sentinel * (start) bit. TODO: why do we need the sentinel? To distinguish between hashes of similar representations that have dots at the beginning? representation hash, no sentinel hash, with sentinel .._ 0000 0001 0000 1001 ..._ 0000 0001 0001 0001 */ unsigned int hash = 1; /* TODO: shouldn't this be uint8_t? */ for (size_t i = 0; i < length; i++) { /* Left-shift everything so far. */ hash <<= 1U; if (representation[i] == CW_DASH_REPRESENTATION) { /* Dash is represented by '1' in hash. */ hash |= 1U; } else if (representation[i] == CW_DOT_REPRESENTATION) { /* Dot is represented by '0' in hash. We don't have to do anything at this point, the zero bit is already at the rightmost position after left-shifting hash. */ ; } else { /* Invalid element in representation string. */ return 0; } } return hash; } /** @brief Return character corresponding to given representation Look up the given @p representation, and return the character that it represents. @internal @reviewed 2020-07-26 @endinternal @param[in] representation representation of a character to look up FIXME: function should be able to return zero as non-error value (?). @return zero if there is no character for given representation @return non-zero character corresponding to given representation otherwise */ int cw_representation_to_character_internal(const char * representation) { static const cw_entry_t * lookup[UCHAR_MAX]; /* Fast lookup table */ static bool is_complete = true; /* Set to false if there are any lookup table entries not in the fast lookup table */ static bool is_initialized = false; /* If this is the first call, set up the fast lookup table to give direct access to the CW table for a hashed representation. */ if (!is_initialized) { /* TODO: move the initialization to cw_data_constructor_internal(). */ cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_INFO, MSG_PREFIX "initialize hash lookup table"); is_complete = CW_SUCCESS == cw_data_init_r2c_hash_table_internal(lookup); is_initialized = true; } /* Hash the representation to get an index for the fast lookup. */ /* TODO: shouldn't this be uint8_t? */ unsigned int hash = cw_representation_to_hash_internal(representation); const cw_entry_t * cw_entry = NULL; /* If the hashed lookup table is complete, we can simply believe any hash value that came back. That is, we just use what is at the index "hash", since this is either the entry we want, or NULL. */ if (is_complete) { cw_entry = lookup[hash]; } else { /* Impossible, since test_cw_representation_to_hash_internal() passes without problems for all valid representations. Debug message is already displayed in cw_data_init_r2c_hash_table_internal(). */ /* The lookup table is incomplete, but it doesn't have to be that we are missing entry for this particular hash. Try to find the entry in lookup table anyway, maybe it exists. TODO: create tests to find situation where lookup table is incomplete. */ if (hash && lookup[hash] && lookup[hash]->representation && strcmp(lookup[hash]->representation, representation) == 0) { /* Found it in an incomplete table. */ cw_entry = lookup[hash]; } else { /* We have no choice but to search the table entry by entry, sequentially, from top to bottom. */ for (cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { if (0 == strcmp(cw_entry->representation, representation)) { break; } } /* If we got to the end of the table, prepare to return zero. */ cw_entry = cw_entry->character ? cw_entry : NULL; } } /* Lookups code may be called frequently, so first do a rough test with cw_debug_has_flag(). */ if (cw_debug_has_flag(&cw_debug_object, CW_DEBUG_LOOKUPS)) { if (cw_entry) { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_DEBUG, MSG_PREFIX "lookup [0x%02x]'%s' returned <'%c':\"%s\">\n", hash, representation, cw_entry->character, cw_entry->representation); } else { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_DEBUG, MSG_PREFIX "lookup [0x%02x]'%s' found nothing\n", hash, representation); } } return cw_entry ? cw_entry->character : 0; } /** @brief Return character corresponding to given representation Look up the given @p representation, and return the character that it represents. In contrast to cw_representation_to_character_internal(), this function doesn't use fast lookup table. It directly traverses the main character/representation table and searches for a character. The function shouldn't be used in production code. It has been created for libcw tests, for verification purposes. Its first purpose is to verify correctness of cw_representation_to_character_internal() (since this direct method is simpler and, well, direct) in a unit test function. The second purpose is to compare time of execution of the two functions: direct and with lookup table, and see what are the speed advantages of using function with lookup table. @internal @reviewed 2020-07-26 @endinternal @param[in] representation representation of a character to look up FIXME: function should be able to return zero as non-error value (?). @return zero if there is no character for given representation @return non-zero character corresponding to given representation otherwise */ int cw_representation_to_character_direct_internal(const char * representation) { /* Search the table entry by entry, sequentially, from top to bottom. */ for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { if (0 == strcmp(cw_entry->representation, representation)) { return cw_entry->character; } } return 0; } /** @brief Initialize representation-to-character lookup table Initialize @p table with values from CW_TABLE (of type cw_entry_t). The table is indexed with hashed representations of cw_entry_t->representation strings. @p table must be large enough to store all entries, caller must make sure that the condition is met. On failure function returns CW_FAILURE. On success the function returns CW_SUCCESS. Successful execution of the function is when all representations from CW_TABLE have valid hashes, and all entries from CW_TABLE have been put into @p table. First condition of function's success, mentioned above, should be always true because the CW_TABLE has been created once and it doesn't change, and all representations in the table are valid. Second condition of function's success, mentioned above, should be also always true because first condition is always true. The initialization may fail under one condition: if the lookup functions should operate on non-standard character table, other than CW_TABLE. For now it's impossible, because there is no way for client code to provide its own table of CW characters. @internal @reviewed 2020-07-26 @endinternal @param[in] table lookup table to be initialized @return CW_SUCCESS on success @return CW_FAILURE otherwise */ cw_ret_t cw_data_init_r2c_hash_table_internal(const cw_entry_t * table[]) { /* For each main table entry, create a hash entry. If the hashing of any entry fails, note that the table is not complete and ignore that entry for now (for the current main table (CW_TABLE) this should not happen). The hashed table speeds up representation-to-character lookups by a factor of 5-10. NOTICE: Notice that the lookup table will be marked as incomplete only if one or more representations in CW_TABLE aren't valid (i.e. they are made of anything more than '.' or '-'). This wouldn't be a logic error, this would be an error with invalid input. Such invalid input shouldn't happen in properly built characters table. So perhaps returning "false" tells us more about input CW_TABLE than about lookup table. Other possibility to consider is that "is_complete = false" when length of representation is longer than CW_DATA_MAX_REPRESENTATION_LENGTH Dots/Dashes. There is an assumption that no representation in input CW_TABLE is longer than CW_DATA_MAX_REPRESENTATION_LENGTH dots/dashes. */ bool is_complete = true; for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { /* TODO: uint8_t instead of unsigned int. */ unsigned int hash = cw_representation_to_hash_internal(cw_entry->representation); if (hash) { table[hash] = cw_entry; } else { is_complete = false; } } if (!is_complete) { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_WARNING, MSG_PREFIX "hash lookup table incomplete"); } return is_complete ? CW_SUCCESS : CW_FAILURE; } /** @brief Check if representation of a character is valid This function is depreciated, use cw_representation_is_valid() instead. Check that the given string is a valid Morse representation. A valid string is one composed of only "." and "-" characters. This means that the function checks only if representation is error-free, and not whether the representation represents existing/defined character. If representation is invalid, function returns CW_FAILURE and sets errno to EINVAL. @internal @reviewed 2020-07-26 @endinternal @param[in] representation representation of a character to check @return CW_SUCCESS on success @return CW_FAILURE on failure */ int cw_check_representation(const char * representation) { const bool valid = cw_representation_is_valid(representation); return valid ? CW_SUCCESS : CW_FAILURE; } /** @brief Check if representation of a character is valid Check that the given string is a valid Morse representation. A valid string is one composed of only "." and "-" characters. This means that the function checks only if representation is error-free, and not whether the representation represents existing/defined character. @internal @reviewed 2020-07-26 @endinternal @exception EINVAL representation is invalid @param[in] representation representation of a character to check @return true if representation is valid @return false if representation is invalid */ bool cw_representation_is_valid(const char * representation) { /* Check the characters in representation. */ for (int i = 0; representation[i]; i++) { if (representation[i] != CW_DOT_REPRESENTATION && representation[i] != CW_DASH_REPRESENTATION) { errno = EINVAL; return false; } } return true; } /** @brief Get the character represented by a given Morse representation This function is depreciated, use cw_representation_to_character() instead. Function checks @p representation, and if it is valid and represents a known character, function returns CW_SUCCESS. Additionally, if @p character is non-NULL, function puts the looked up character in @p character. @p character should be allocated by caller. Function assumes that @p character being NULL pointer is a valid situation, and can return CW_SUCCESS in such situation. On error, function returns CW_FAILURE. errno is set to EINVAL if any character of the representation is invalid, or ENOENT to indicate that the character represented by @p representation could not be found. @internal @reviewed 2020-07-26 @endinternal @param[in] representation representation of a character to look up @param[out] character location where to put looked up character @return CW_SUCCESS on success @return CW_FAILURE on failure */ int cw_lookup_representation(const char * representation, char * character) { /* Check the characters in representation. */ if (!cw_representation_is_valid(representation)) { errno = EINVAL; return CW_FAILURE; } /* Lookup the representation, and if found, return the character. */ const int looked_up = cw_representation_to_character_internal(representation); if (0 != looked_up) { if (character) { *character = looked_up; } return CW_SUCCESS; } /* Failed to find the requested representation. */ errno = ENOENT; return CW_FAILURE; } /** @brief Return the character represented by a given Morse representation Function checks @p representation, and if it is valid and represents a known character, function returns the character (a non-zero value). On error, function returns zero (character represented by @p representation was not found). @internal @reviewed 2020-07-26 @endinternal @exception EINVAL @p representation contains invalid symbol (other than Dots and Dashes) @exception ENOENT a character represented by @p representation could not be found @param[in] representation representation of a character to look up @return non-zero character on success @return zero on failure */ int cw_representation_to_character(const char * representation) { /* Check the characters in representation. */ if (!cw_representation_is_valid(representation)) { errno = EINVAL; return 0; } /* Lookup the representation, and if found, return the character. */ int character = cw_representation_to_character_internal(representation); if (0 != character) { return character; } else { /* Failed to find the requested representation. */ errno = ENOENT; return 0; } } /* ******************************************************************** */ /* Section:Extended Morse code data and lookup (procedural signals) */ /* ******************************************************************** */ /* Ancillary procedural signals table. This table maps procedural signal characters in the main table to their expansions, along with a flag noting if the character is usually expanded for display. */ typedef struct { const char character; /* Character represented */ const bool is_usually_expanded; /* If expanded display is usual */ const char * const expansion; /* Procedural expansion of the character */ } cw_prosign_entry_t; static int g_prosign_table_characters_count; static size_t g_prosign_table_maximum_expansion_length; static const cw_prosign_entry_t g_prosign_table[] = { /* Standard procedural signals */ { '"', false, "AF" }, { '\'', false, "WG" }, { '$', false, "SX" }, { '(', false, "KN" }, { ')', false, "KK" }, { '+', false, "AR" }, { ',', false, "MIM" }, { '-', false, "DU" }, { '.', false, "AAA" }, { '/', false, "DN" }, { ':', false, "OS" }, { ';', false, "KR" }, { '=', false, "BT" }, { '?', false, "IMI" }, { '_', false, "IQ" }, { '@', false, "AC" }, /* Non-standard procedural signal extensions to standard CW characters. */ { '<', true, "VA" }, /* VA/SK, end of work */ { '>', true, "BK" }, /* BK, break */ { '!', true, "SN" }, /* SN, understood */ { '&', true, "AS" }, /* AS, wait */ { '^', true, "KA" }, /* KA, starting signal */ { '~', true, "AL" }, /* AL, paragraph */ { 0, false, NULL } /* Guard. */ }; static const cw_prosign_entry_t * g_prosign_table_fast_lookup[UCHAR_MAX]; /* Fast lookup table. */ /** @brief Get number of procedural signals @internal @reviewed 2020-07-26 @endinternal @return the number of characters stored in the procedural signal expansion lookup table */ int cw_get_procedural_character_count(void) { #if 0 /* Disabled on 2020-07-25. Moved to library constructor. */ static int g_prosign_table_characters_count = 0; if (0 == g_prosign_table_characters_count) { for (const cw_prosign_entry_t *e = g_prosign_table; e->character; e++) { g_prosign_table_characters_count++; } } #endif return g_prosign_table_characters_count; } /** @brief Get list of characters for which procedural expansion is available Function copies into preallocated buffer @p list a string containing all of the Morse characters for which procedural expansion is available. The length of @p list must be at least by one greater than the number of characters represented in the procedural signal expansion lookup table, returned by cw_get_procedural_character_count(). @p list buffer is allocated and managed by caller. @internal @reviewed 2020-07-26 @endinternal @param[out] list buffer for returned string */ void cw_list_procedural_characters(char * list) { /* Append each table character to the output string. */ int i = 0; for (const cw_prosign_entry_t *e = g_prosign_table; e->character; e++) { list[i++] = e->character; } list[i] = '\0'; return; } /** @brief Get length of the longest procedural expansion Function returns the string length of the longest expansion in the procedural signal expansion table. @internal @reviewed 2020-07-26 @endinternal @return the string length of the longest expansion of procedural signals */ int cw_get_maximum_procedural_expansion_length(void) { #if 0 /* Disabled on 2020-07-25. Moved to library constructor. */ static size_t g_prosign_table_maximum_expansion_length = 0; if (0 == g_prosign_table_maximum_expansion_length) { /* Traverse the prosign table, finding the longest expansion. */ for (const cw_prosign_entry_t *e = g_prosign_table; e->character; e++) { size_t length = strlen(e->expansion); if (length > g_prosign_table_maximum_expansion_length) { g_prosign_table_maximum_expansion_length = length; } } } #endif return (int) g_prosign_table_maximum_expansion_length; } /** @brief Get the string expansion of a given Morse code procedural signal character Function looks up the given procedural character @p character, and returns the expansion of that procedural character, with a display hint in @p is_usually_expanded. Pointer returned by the function is owned and managed by library. @p is_usually_expanded pointer is owned by client code. @internal @reviewed 2020-07-26 @endinternal @param[in] character character to look up @param[out] is_usually_expanded display hint @return expansion of input character on success @return NULL if there is no table entry for the given character */ const char * cw_lookup_procedural_character_internal(int character, bool * is_usually_expanded) { #if 0 /* Disabled on 2020-07-25. Code moved to library constructor. */ /* If this is the first call, set up the fast lookup table to give direct access to the procedural expansions table for a given character. */ static bool is_initialized = false; if (!is_initialized) { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_INFO, MSG_PREFIX "initialize prosign fast lookup table"); for (const cw_prosign_entry_t *e = g_prosign_table; e->character; e++) { g_prosign_table_fast_lookup[(unsigned char) e->character] = e; } is_initialized = true; } #endif /* Lookup the procedural signal table entry. Unknown characters return NULL. All procedural signals are non-alphabetical, so no need to use any uppercase coercion here. */ const cw_prosign_entry_t * cw_prosign = g_prosign_table_fast_lookup[(unsigned char) character]; /* Lookups code may be called frequently, so first do a rough test with cw_debug_has_flag(). */ if (cw_debug_has_flag((&cw_debug_object), CW_DEBUG_LOOKUPS)) { if (cw_prosign) { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_DEBUG, MSG_PREFIX "prosign lookup '%c' -> '%c'/'%s'/%d\n", character, cw_prosign->character, cw_prosign->expansion, cw_prosign->is_usually_expanded); } else if (isprint(character)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_DEBUG, MSG_PREFIX "prosign lookup '%c' -> NOTHING\n", character); } else { cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_DEBUG, MSG_PREFIX "prosign lookup '0x%02x' -> NOTHING\n", (unsigned int) character); } } /* If found, return any display hint and the expansion; otherwise, NULL. */ if (cw_prosign) { *is_usually_expanded = cw_prosign->is_usually_expanded; return cw_prosign->expansion; } else { return NULL; } } /** @brief Get the string expansion of a given Morse code procedural signal character On success the function @li fills @p expansion with the string expansion of a given Morse code procedural signal character @p character; @li sets @p is_usually_expanded to appropriate value as a display hint for the caller; @li returns CW_SUCCESS. Both @p expansion and @p is_usually_expanded must be allocated and managed by caller. They can be NULL, then the function won't attempt to use them. The length of @p expansion must be at least by one greater than the longest expansion held in the procedural signal character lookup table, as returned by cw_get_maximum_procedural_expansion_length(). @internal @reviewed 2020-07-26 @endinternal @exception ENOENT procedural signal character @p character cannot be found @param[in] character character to look up @param[out] expansion buffer to fill with expansion of the character @param[out] is_usually_expanded visual hint @return CW_FAILURE on failure (character cannot be found) @return CW_SUCCESS on success */ int cw_lookup_procedural_character(char character, char *expansion, int * is_usually_expanded) { bool is_expanded = false; /* Lookup, and if found, return the string and display hint. */ const char * retval = cw_lookup_procedural_character_internal(character, &is_expanded); if (retval) { if (expansion) { strncpy(expansion, retval, g_prosign_table_maximum_expansion_length); expansion[g_prosign_table_maximum_expansion_length] = '\0'; } if (is_usually_expanded) { *is_usually_expanded = is_expanded; } return CW_SUCCESS; } /* Failed to find the requested procedural signal character. */ errno = ENOENT; return CW_FAILURE; } /* ******************************************************************** */ /* Section:Phonetic alphabet */ /* ******************************************************************** */ /* Phonetics table. Not really CW, but it might be handy to have. The table contains ITU/NATO phonetics. */ static size_t g_phonetics_table_maximum_phonetic_length; static const char * const g_phonetics_table[] = { "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliett", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu", NULL /* guard */ }; /** @brief Get length of a longest phonetic @internal @reviewed 2020-07-26 @endinternal @return the string length of the longest phonetic in the phonetics lookup table */ int cw_get_maximum_phonetic_length(void) { #if 0 /* Disabled on 2020-07-25. Moved to library constructor. */ static size_t g_phonetics_table_maximum_phonetic_length = 0; if (0 == g_phonetics_table_maximum_phonetic_length) { /* Traverse the phonetics table, finding the longest phonetic string. */ for (int phonetic = 0; g_phonetics_table[phonetic]; phonetic++) { size_t length = strlen(g_phonetics_table[phonetic]); if (length > g_phonetics_table_maximum_phonetic_length) { g_phonetics_table_maximum_phonetic_length = length; } } } #endif return (int) g_phonetics_table_maximum_phonetic_length; } /** @brief Look up the phonetic of a given character On success the routine copies a phonetic corresponding to @p character into @p phonetic. @p phonetic is managed and owned by caller. It is NOT considered an error if @p phonetic is NULL. In such case the function will just verify if @p character can be represented by a phonetic, i.e. if @p character is a letter. If non-NULL, the size of @p phonetic must be greater by at least 1 than the longest phonetic held in the phonetic lookup table, as returned by cw_get_maximum_phonetic_length(). @internal @reviewed 2020-07-25 @endinternal @exception ENOENT phonetic for given character cannot be found @param[in] character character to look up @param[out] phonetic buffer for phonetic of a character (may be NULL) @return CW_SUCCESS on success (phonetic has been found and - if @p phonetic is non-NULL) has been copied to the buffer @return CW_FAILURE on failure (phonetic for given character cannot be found) */ int cw_lookup_phonetic(char character, char * phonetic) { /* Coerce to uppercase, and verify the input argument. */ character = toupper(character); if (character >= 'A' && character <= 'Z') { if (NULL != phonetic) { strncpy(phonetic, g_phonetics_table[character - 'A'], g_phonetics_table_maximum_phonetic_length); phonetic[g_phonetics_table_maximum_phonetic_length] = '\0'; } else { /* Simply ignore the fact that caller didn't provide output buffer. */ } return CW_SUCCESS; } /* No such phonetic. */ errno = ENOENT; return CW_FAILURE; } /** @brief Check if given character is valid Check that a given character is valid, i.e. it is present in library's character-to-representation lookup table and can be represented with Dots and Dashes. Space character (' ') is also considered to be a valid character. @exception ENOENT on failure (character is invalid). Notice the difference in errno set by cw_character_is_valid() (ENOENT) and cw_string_is_valid() (EINVAL). This different behaviour goes way back to unixcw-2.3 and is preserved in new versions for backwards compatibility reasons. @internal @reviewed 2020-08-24 @endinternal @param[in] character character to check @return true if character is valid @return false otherwise */ bool cw_character_is_valid(char character) { /* If the character is the Space special-case, or it is in the lookup table, return success. */ if (character == ' ' || NULL != cw_character_to_representation_internal(character)) { return true; } else { errno = ENOENT; return false; } } /** @internal @reviewed 2020-07-25 @endinternal */ int cw_check_character(char character) { return (int) cw_character_is_valid(character); } /** @brief Check if all characters in given string are valid Check that each character in the given string is valid and can be converted by libcw to Morse code symbols sent as a Morse character. Space character (' ') is also considered to be a valid character. @exception EINVAL on failure (when an invalid character has been found in @p string). Notice the difference in errno set by cw_character_is_valid() (ENOENT) and cw_string_is_valid() (EINVAL). This different behaviour goes way back to unixcw-2.3 and is preserved in new versions for backwards compatibility reasons. @internal @reviewed 2020-07-25 @endinternal @param[in] string string to check @return true if all characters in string are valid @return false otherwise */ bool cw_string_is_valid(const char * string) { /* Check that each character satisfies validity as specified by cw_character_is_valid(). */ for (int i = 0; string[i] != '\0'; i++) { if (!cw_character_is_valid(string[i])) { errno = EINVAL; return CW_FAILURE; } } return CW_SUCCESS; } /** @internal @reviewed 2020-07-25 @endinternal */ int cw_check_string(const char * string) { return cw_string_is_valid(string); } // @cond LIBCW_INTERNAL /** @brief Constructor for 'data' module of libcw Since we may have multiple gen/rec/key objects per application, it's better to initialize library's data structures before the multiple objects start calling library functions that have internal static 'is_initialized' flags. @internal @reviewed 2020-07-25 @endinternal */ void cw_data_constructor_internal(void) { cw_debug_msg (&cw_debug_object, CW_DEBUG_INTERNAL, CW_DEBUG_INFO, MSG_PREFIX "constructor for 'data' module started"); cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_INFO, MSG_PREFIX "initializing main fast lookup table"); for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { /* Initialize lookup table for main characters. */ g_main_table_fast_lookup[(unsigned char) cw_entry->character] = cw_entry; /* Calculate the length of the longest representation in main lookup table. */ const size_t length = strlen(cw_entry->representation); if (length > g_main_table_maximum_representation_length) { g_main_table_maximum_representation_length = length; } /* Calculate count of characters in main lookup table. */ g_main_table_characters_count++; } cw_debug_msg (&cw_debug_object, CW_DEBUG_LOOKUPS, CW_DEBUG_INFO, MSG_PREFIX "initialize prosign fast lookup table"); for (const cw_prosign_entry_t * entry = g_prosign_table; entry->character; entry++) { /* Initialize lookup table for procedural characters. */ g_prosign_table_fast_lookup[(unsigned char) entry->character] = entry; /* Calculate the length of the longest expansion of prosign. */ const size_t length = strlen(entry->expansion); if (length > g_prosign_table_maximum_expansion_length) { g_prosign_table_maximum_expansion_length = length; } /* Calculate the count of procedural characters. */ g_prosign_table_characters_count++; } /* Calculate the length of the longest phonetic. */ for (int phonetic = 0; g_phonetics_table[phonetic]; phonetic++) { const size_t length = strlen(g_phonetics_table[phonetic]); if (length > g_phonetics_table_maximum_phonetic_length) { g_phonetics_table_maximum_phonetic_length = length; } } cw_debug_msg (&cw_debug_object, CW_DEBUG_INTERNAL, CW_DEBUG_INFO, MSG_PREFIX "constructor for 'data' module completed"); } //@endcond unixcw-3.6.0/src/libcw/Makefile.in0000644000175000017500000020770014001105754013715 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) # Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/libcw ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(libcw_include_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/src/config.h CONFIG_CLEAN_FILES = libcw.pc CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)" \ "$(DESTDIR)$(man7dir)" "$(DESTDIR)$(pkgconfigdir)" \ "$(DESTDIR)$(libcw_includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) am__DEPENDENCIES_1 = libcw_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am__objects_1 = libcw_la-libcw.lo libcw_la-libcw_gen.lo \ libcw_la-libcw_rec.lo libcw_la-libcw_tq.lo \ libcw_la-libcw_data.lo libcw_la-libcw_key.lo \ libcw_la-libcw_utils.lo libcw_la-libcw_signal.lo \ libcw_la-libcw_null.lo libcw_la-libcw_console.lo \ libcw_la-libcw_oss.lo libcw_la-libcw_alsa.lo \ libcw_la-libcw_pa.lo libcw_la-libcw_debug.lo am_libcw_la_OBJECTS = $(am__objects_1) libcw_la_OBJECTS = $(am_libcw_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libcw_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libcw_la_CFLAGS) \ $(CFLAGS) $(libcw_la_LDFLAGS) $(LDFLAGS) -o $@ libcw_test_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) am__objects_2 = libcw_test_la-libcw.lo libcw_test_la-libcw_gen.lo \ libcw_test_la-libcw_rec.lo libcw_test_la-libcw_tq.lo \ libcw_test_la-libcw_data.lo libcw_test_la-libcw_key.lo \ libcw_test_la-libcw_utils.lo libcw_test_la-libcw_signal.lo \ libcw_test_la-libcw_null.lo libcw_test_la-libcw_console.lo \ libcw_test_la-libcw_oss.lo libcw_test_la-libcw_alsa.lo \ libcw_test_la-libcw_pa.lo libcw_test_la-libcw_debug.lo am_libcw_test_la_OBJECTS = $(am__objects_2) libcw_test_la_OBJECTS = $(am_libcw_test_la_OBJECTS) libcw_test_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libcw_test_la_CFLAGS) \ $(CFLAGS) $(libcw_test_la_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libcw_la_SOURCES) $(libcw_test_la_SOURCES) DIST_SOURCES = $(libcw_la_SOURCES) $(libcw_test_la_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac man3dir = $(mandir)/man3 man7dir = $(mandir)/man7 NROFF = nroff MANS = $(man_MANS) DATA = $(pkgconfig_DATA) HEADERS = $(libcw_include_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/libcw.pc.in \ $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CC_LINKS_SO = @CC_LINKS_SO@ CFLAGS = @CFLAGS@ CFLAG_PIC = @CFLAG_PIC@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DL_LIB = @DL_LIB@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ GZIP = @GZIP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTL_LIB = @INTL_LIB@ LD = @LD@ LDCONFIG = @LDCONFIG@ LDFLAGS = @LDFLAGS@ LD_LINKS_SO = @LD_LINKS_SO@ LIBCW_NDEBUG = @LIBCW_NDEBUG@ LIBCW_VERSION = @LIBCW_VERSION@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MOC = @MOC@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OSS_LIB = @OSS_LIB@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QT5_CFLAGS = @QT5_CFLAGS@ QT5_LIBS = @QT5_LIBS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SRC_SUBDIRS = @SRC_SUBDIRS@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = tests # Targets to be built in this directory. Only libcw.X should be # installed. Test library should not be installed. lib_LTLIBRARIES = libcw.la noinst_LTLIBRARIES = libcw_test.la man_MANS = libcw.3 cw.7 pkgconfig_DATA = libcw.pc EXTRA_DIST = include.awk libdoc.awk libfuncs.awk libpc.awk libsigs.awk \ libcw.3.m4 \ libcw.pc.in \ cw.7 \ libcw_gen.h libcw_rec.h \ libcw_tq.h libcw_data.h libcw_key.h libcw_utils.h libcw_signal.h \ libcw_null.h libcw_console.h libcw_oss.h libcw_alsa.h libcw_pa.h \ libcw_gen_internal.h libcw_rec_internal.h libcw_tq_internal.h \ Doxyfile clang_tidy.sh # These files are used to build two different targets - list them only # once. I can't compile these files into an utility library because # the two targets are compiled with different CPPFLAGS. LIBCW_BASE_C_FILES = \ libcw.c \ libcw_gen.c libcw_rec.c \ libcw_tq.c libcw_data.c libcw_key.c libcw_utils.c libcw_signal.c \ libcw_null.c libcw_console.c libcw_oss.c libcw_alsa.c libcw_pa.c \ libcw_debug.c # target: shared library # source code files used to build libcw shared library libcw_la_SOURCES = $(LIBCW_BASE_C_FILES) # target-specific linker flags (objects to link) libcw_la_LIBADD = -lm -lpthread $(DL_LIB) $(OSS_LIB) # target-specific linker flags (additional flags) # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html # LIBCW_VERSION is defined in configure.ac libcw_la_LDFLAGS = -version-number $(LIBCW_VERSION) # target-specific compiler flags libcw_la_CFLAGS = -rdynamic # target-specific preprocessor flags (#defs and include dirs) # # $(LIBCW_NDEBUG) activates asserts in base libcw for dev builds, and # deactivates asserts in base libcw for regular builds. libcw_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCW_NDEBUG) libcw_includedir = $(includedir) libcw_include_HEADERS = libcw.h libcw2.h libcw_debug.h # target: shared library for tests # source code files used to build libcw shared library libcw_test_la_SOURCES = $(LIBCW_BASE_C_FILES) # target-specific linker flags (objects to link) libcw_test_la_LIBADD = -lm -lpthread $(DL_LIB) $(OSS_LIB) # target-specific linker flags (additional flags) # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html # LIBCW_VERSION is defined in configure.ac libcw_test_la_LDFLAGS = -version-number $(LIBCW_VERSION) # target-specific compiler flags libcw_test_la_CFLAGS = -rdynamic # target-specific preprocessor flags (#defs and include dirs) # # $(LIBCW_NDEBUG) activates asserts in base libcw for dev builds, and # deactivates asserts in base libcw for regular builds. libcw_test_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCW_NDEBUG) -DLIBCW_UNIT_TESTS # target: libcw.pc pkgconfigdir = $(libdir)/pkgconfig # CLEANFILES extends list of files that need to be removed when # calling "make clean" CLEANFILES = libcw.3 all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/libcw/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/libcw/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): libcw.pc: $(top_builddir)/config.status $(srcdir)/libcw.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) @list='$(noinst_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libcw.la: $(libcw_la_OBJECTS) $(libcw_la_DEPENDENCIES) $(EXTRA_libcw_la_DEPENDENCIES) $(AM_V_CCLD)$(libcw_la_LINK) -rpath $(libdir) $(libcw_la_OBJECTS) $(libcw_la_LIBADD) $(LIBS) libcw_test.la: $(libcw_test_la_OBJECTS) $(libcw_test_la_DEPENDENCIES) $(EXTRA_libcw_test_la_DEPENDENCIES) $(AM_V_CCLD)$(libcw_test_la_LINK) $(libcw_test_la_OBJECTS) $(libcw_test_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_alsa.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_console.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_debug.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_gen.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_key.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_null.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_oss.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_pa.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_rec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_signal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_tq.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_la-libcw_utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_alsa.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_console.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_debug.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_gen.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_key.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_null.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_oss.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_pa.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_rec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_signal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_tq.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_test_la-libcw_utils.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< libcw_la-libcw.lo: libcw.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw.Tpo -c -o libcw_la-libcw.lo `test -f 'libcw.c' || echo '$(srcdir)/'`libcw.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw.Tpo $(DEPDIR)/libcw_la-libcw.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw.c' object='libcw_la-libcw.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw.lo `test -f 'libcw.c' || echo '$(srcdir)/'`libcw.c libcw_la-libcw_gen.lo: libcw_gen.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_gen.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_gen.Tpo -c -o libcw_la-libcw_gen.lo `test -f 'libcw_gen.c' || echo '$(srcdir)/'`libcw_gen.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_gen.Tpo $(DEPDIR)/libcw_la-libcw_gen.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_gen.c' object='libcw_la-libcw_gen.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_gen.lo `test -f 'libcw_gen.c' || echo '$(srcdir)/'`libcw_gen.c libcw_la-libcw_rec.lo: libcw_rec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_rec.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_rec.Tpo -c -o libcw_la-libcw_rec.lo `test -f 'libcw_rec.c' || echo '$(srcdir)/'`libcw_rec.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_rec.Tpo $(DEPDIR)/libcw_la-libcw_rec.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_rec.c' object='libcw_la-libcw_rec.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_rec.lo `test -f 'libcw_rec.c' || echo '$(srcdir)/'`libcw_rec.c libcw_la-libcw_tq.lo: libcw_tq.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_tq.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_tq.Tpo -c -o libcw_la-libcw_tq.lo `test -f 'libcw_tq.c' || echo '$(srcdir)/'`libcw_tq.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_tq.Tpo $(DEPDIR)/libcw_la-libcw_tq.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_tq.c' object='libcw_la-libcw_tq.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_tq.lo `test -f 'libcw_tq.c' || echo '$(srcdir)/'`libcw_tq.c libcw_la-libcw_data.lo: libcw_data.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_data.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_data.Tpo -c -o libcw_la-libcw_data.lo `test -f 'libcw_data.c' || echo '$(srcdir)/'`libcw_data.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_data.Tpo $(DEPDIR)/libcw_la-libcw_data.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_data.c' object='libcw_la-libcw_data.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_data.lo `test -f 'libcw_data.c' || echo '$(srcdir)/'`libcw_data.c libcw_la-libcw_key.lo: libcw_key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_key.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_key.Tpo -c -o libcw_la-libcw_key.lo `test -f 'libcw_key.c' || echo '$(srcdir)/'`libcw_key.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_key.Tpo $(DEPDIR)/libcw_la-libcw_key.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_key.c' object='libcw_la-libcw_key.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_key.lo `test -f 'libcw_key.c' || echo '$(srcdir)/'`libcw_key.c libcw_la-libcw_utils.lo: libcw_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_utils.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_utils.Tpo -c -o libcw_la-libcw_utils.lo `test -f 'libcw_utils.c' || echo '$(srcdir)/'`libcw_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_utils.Tpo $(DEPDIR)/libcw_la-libcw_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_utils.c' object='libcw_la-libcw_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_utils.lo `test -f 'libcw_utils.c' || echo '$(srcdir)/'`libcw_utils.c libcw_la-libcw_signal.lo: libcw_signal.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_signal.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_signal.Tpo -c -o libcw_la-libcw_signal.lo `test -f 'libcw_signal.c' || echo '$(srcdir)/'`libcw_signal.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_signal.Tpo $(DEPDIR)/libcw_la-libcw_signal.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_signal.c' object='libcw_la-libcw_signal.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_signal.lo `test -f 'libcw_signal.c' || echo '$(srcdir)/'`libcw_signal.c libcw_la-libcw_null.lo: libcw_null.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_null.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_null.Tpo -c -o libcw_la-libcw_null.lo `test -f 'libcw_null.c' || echo '$(srcdir)/'`libcw_null.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_null.Tpo $(DEPDIR)/libcw_la-libcw_null.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_null.c' object='libcw_la-libcw_null.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_null.lo `test -f 'libcw_null.c' || echo '$(srcdir)/'`libcw_null.c libcw_la-libcw_console.lo: libcw_console.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_console.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_console.Tpo -c -o libcw_la-libcw_console.lo `test -f 'libcw_console.c' || echo '$(srcdir)/'`libcw_console.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_console.Tpo $(DEPDIR)/libcw_la-libcw_console.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_console.c' object='libcw_la-libcw_console.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_console.lo `test -f 'libcw_console.c' || echo '$(srcdir)/'`libcw_console.c libcw_la-libcw_oss.lo: libcw_oss.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_oss.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_oss.Tpo -c -o libcw_la-libcw_oss.lo `test -f 'libcw_oss.c' || echo '$(srcdir)/'`libcw_oss.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_oss.Tpo $(DEPDIR)/libcw_la-libcw_oss.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_oss.c' object='libcw_la-libcw_oss.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_oss.lo `test -f 'libcw_oss.c' || echo '$(srcdir)/'`libcw_oss.c libcw_la-libcw_alsa.lo: libcw_alsa.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_alsa.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_alsa.Tpo -c -o libcw_la-libcw_alsa.lo `test -f 'libcw_alsa.c' || echo '$(srcdir)/'`libcw_alsa.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_alsa.Tpo $(DEPDIR)/libcw_la-libcw_alsa.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_alsa.c' object='libcw_la-libcw_alsa.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_alsa.lo `test -f 'libcw_alsa.c' || echo '$(srcdir)/'`libcw_alsa.c libcw_la-libcw_pa.lo: libcw_pa.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_pa.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_pa.Tpo -c -o libcw_la-libcw_pa.lo `test -f 'libcw_pa.c' || echo '$(srcdir)/'`libcw_pa.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_pa.Tpo $(DEPDIR)/libcw_la-libcw_pa.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_pa.c' object='libcw_la-libcw_pa.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_pa.lo `test -f 'libcw_pa.c' || echo '$(srcdir)/'`libcw_pa.c libcw_la-libcw_debug.lo: libcw_debug.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -MT libcw_la-libcw_debug.lo -MD -MP -MF $(DEPDIR)/libcw_la-libcw_debug.Tpo -c -o libcw_la-libcw_debug.lo `test -f 'libcw_debug.c' || echo '$(srcdir)/'`libcw_debug.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_la-libcw_debug.Tpo $(DEPDIR)/libcw_la-libcw_debug.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_debug.c' object='libcw_la-libcw_debug.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_la_CPPFLAGS) $(CPPFLAGS) $(libcw_la_CFLAGS) $(CFLAGS) -c -o libcw_la-libcw_debug.lo `test -f 'libcw_debug.c' || echo '$(srcdir)/'`libcw_debug.c libcw_test_la-libcw.lo: libcw.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw.Tpo -c -o libcw_test_la-libcw.lo `test -f 'libcw.c' || echo '$(srcdir)/'`libcw.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw.Tpo $(DEPDIR)/libcw_test_la-libcw.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw.c' object='libcw_test_la-libcw.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw.lo `test -f 'libcw.c' || echo '$(srcdir)/'`libcw.c libcw_test_la-libcw_gen.lo: libcw_gen.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_gen.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_gen.Tpo -c -o libcw_test_la-libcw_gen.lo `test -f 'libcw_gen.c' || echo '$(srcdir)/'`libcw_gen.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_gen.Tpo $(DEPDIR)/libcw_test_la-libcw_gen.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_gen.c' object='libcw_test_la-libcw_gen.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_gen.lo `test -f 'libcw_gen.c' || echo '$(srcdir)/'`libcw_gen.c libcw_test_la-libcw_rec.lo: libcw_rec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_rec.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_rec.Tpo -c -o libcw_test_la-libcw_rec.lo `test -f 'libcw_rec.c' || echo '$(srcdir)/'`libcw_rec.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_rec.Tpo $(DEPDIR)/libcw_test_la-libcw_rec.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_rec.c' object='libcw_test_la-libcw_rec.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_rec.lo `test -f 'libcw_rec.c' || echo '$(srcdir)/'`libcw_rec.c libcw_test_la-libcw_tq.lo: libcw_tq.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_tq.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_tq.Tpo -c -o libcw_test_la-libcw_tq.lo `test -f 'libcw_tq.c' || echo '$(srcdir)/'`libcw_tq.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_tq.Tpo $(DEPDIR)/libcw_test_la-libcw_tq.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_tq.c' object='libcw_test_la-libcw_tq.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_tq.lo `test -f 'libcw_tq.c' || echo '$(srcdir)/'`libcw_tq.c libcw_test_la-libcw_data.lo: libcw_data.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_data.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_data.Tpo -c -o libcw_test_la-libcw_data.lo `test -f 'libcw_data.c' || echo '$(srcdir)/'`libcw_data.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_data.Tpo $(DEPDIR)/libcw_test_la-libcw_data.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_data.c' object='libcw_test_la-libcw_data.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_data.lo `test -f 'libcw_data.c' || echo '$(srcdir)/'`libcw_data.c libcw_test_la-libcw_key.lo: libcw_key.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_key.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_key.Tpo -c -o libcw_test_la-libcw_key.lo `test -f 'libcw_key.c' || echo '$(srcdir)/'`libcw_key.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_key.Tpo $(DEPDIR)/libcw_test_la-libcw_key.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_key.c' object='libcw_test_la-libcw_key.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_key.lo `test -f 'libcw_key.c' || echo '$(srcdir)/'`libcw_key.c libcw_test_la-libcw_utils.lo: libcw_utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_utils.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_utils.Tpo -c -o libcw_test_la-libcw_utils.lo `test -f 'libcw_utils.c' || echo '$(srcdir)/'`libcw_utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_utils.Tpo $(DEPDIR)/libcw_test_la-libcw_utils.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_utils.c' object='libcw_test_la-libcw_utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_utils.lo `test -f 'libcw_utils.c' || echo '$(srcdir)/'`libcw_utils.c libcw_test_la-libcw_signal.lo: libcw_signal.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_signal.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_signal.Tpo -c -o libcw_test_la-libcw_signal.lo `test -f 'libcw_signal.c' || echo '$(srcdir)/'`libcw_signal.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_signal.Tpo $(DEPDIR)/libcw_test_la-libcw_signal.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_signal.c' object='libcw_test_la-libcw_signal.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_signal.lo `test -f 'libcw_signal.c' || echo '$(srcdir)/'`libcw_signal.c libcw_test_la-libcw_null.lo: libcw_null.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_null.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_null.Tpo -c -o libcw_test_la-libcw_null.lo `test -f 'libcw_null.c' || echo '$(srcdir)/'`libcw_null.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_null.Tpo $(DEPDIR)/libcw_test_la-libcw_null.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_null.c' object='libcw_test_la-libcw_null.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_null.lo `test -f 'libcw_null.c' || echo '$(srcdir)/'`libcw_null.c libcw_test_la-libcw_console.lo: libcw_console.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_console.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_console.Tpo -c -o libcw_test_la-libcw_console.lo `test -f 'libcw_console.c' || echo '$(srcdir)/'`libcw_console.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_console.Tpo $(DEPDIR)/libcw_test_la-libcw_console.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_console.c' object='libcw_test_la-libcw_console.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_console.lo `test -f 'libcw_console.c' || echo '$(srcdir)/'`libcw_console.c libcw_test_la-libcw_oss.lo: libcw_oss.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_oss.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_oss.Tpo -c -o libcw_test_la-libcw_oss.lo `test -f 'libcw_oss.c' || echo '$(srcdir)/'`libcw_oss.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_oss.Tpo $(DEPDIR)/libcw_test_la-libcw_oss.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_oss.c' object='libcw_test_la-libcw_oss.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_oss.lo `test -f 'libcw_oss.c' || echo '$(srcdir)/'`libcw_oss.c libcw_test_la-libcw_alsa.lo: libcw_alsa.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_alsa.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_alsa.Tpo -c -o libcw_test_la-libcw_alsa.lo `test -f 'libcw_alsa.c' || echo '$(srcdir)/'`libcw_alsa.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_alsa.Tpo $(DEPDIR)/libcw_test_la-libcw_alsa.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_alsa.c' object='libcw_test_la-libcw_alsa.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_alsa.lo `test -f 'libcw_alsa.c' || echo '$(srcdir)/'`libcw_alsa.c libcw_test_la-libcw_pa.lo: libcw_pa.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_pa.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_pa.Tpo -c -o libcw_test_la-libcw_pa.lo `test -f 'libcw_pa.c' || echo '$(srcdir)/'`libcw_pa.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_pa.Tpo $(DEPDIR)/libcw_test_la-libcw_pa.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_pa.c' object='libcw_test_la-libcw_pa.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_pa.lo `test -f 'libcw_pa.c' || echo '$(srcdir)/'`libcw_pa.c libcw_test_la-libcw_debug.lo: libcw_debug.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -MT libcw_test_la-libcw_debug.lo -MD -MP -MF $(DEPDIR)/libcw_test_la-libcw_debug.Tpo -c -o libcw_test_la-libcw_debug.lo `test -f 'libcw_debug.c' || echo '$(srcdir)/'`libcw_debug.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_test_la-libcw_debug.Tpo $(DEPDIR)/libcw_test_la-libcw_debug.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_debug.c' object='libcw_test_la-libcw_debug.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_test_la_CPPFLAGS) $(CPPFLAGS) $(libcw_test_la_CFLAGS) $(CFLAGS) -c -o libcw_test_la-libcw_debug.lo `test -f 'libcw_debug.c' || echo '$(srcdir)/'`libcw_debug.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-man3: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man3dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.3[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man3dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man3dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man3dir)" || exit $$?; }; \ done; } uninstall-man3: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man3dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.3[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir) install-man7: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man7dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man7dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man7dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.7[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man7dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man7dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man7dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man7dir)" || exit $$?; }; \ done; } uninstall-man7: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man7dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.7[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man7dir)'; $(am__uninstall_files_from_dir) install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) install-libcw_includeHEADERS: $(libcw_include_HEADERS) @$(NORMAL_INSTALL) @list='$(libcw_include_HEADERS)'; test -n "$(libcw_includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(libcw_includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libcw_includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libcw_includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libcw_includedir)" || exit $$?; \ done uninstall-libcw_includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(libcw_include_HEADERS)'; test -n "$(libcw_includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(libcw_includedir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(LTLIBRARIES) $(MANS) $(DATA) $(HEADERS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(man7dir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(libcw_includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ clean-noinstLTLIBRARIES mostlyclean-am distclean: distclean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-libcw_includeHEADERS install-man \ install-pkgconfigDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-man3 install-man7 install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-libLTLIBRARIES uninstall-libcw_includeHEADERS \ uninstall-man uninstall-pkgconfigDATA uninstall-man: uninstall-man3 uninstall-man7 .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libLTLIBRARIES \ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-libLTLIBRARIES \ install-libcw_includeHEADERS install-man install-man3 \ install-man7 install-pdf install-pdf-am install-pkgconfigDATA \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \ uninstall-libcw_includeHEADERS uninstall-man uninstall-man3 \ uninstall-man7 uninstall-pkgconfigDATA .PRECIOUS: Makefile -include $(top_builddir)/Makefile.inc # target: libcw man page libcw.3: libcw.3.m4 cat $(top_srcdir)/src/libcw/*.c | $(AC_AWK) -f $(top_srcdir)/src/libcw/libdoc.awk | $(AC_AWK) -f $(top_srcdir)/src/libcw/libsigs.awk > signatures cat $(top_srcdir)/src/libcw/*.c | $(AC_AWK) -f $(top_srcdir)/src/libcw/libdoc.awk | $(AC_AWK) -f $(top_srcdir)/src/libcw/libfuncs.awk > functions $(AC_AWK) -f $(top_srcdir)/src/libcw/include.awk < $(top_srcdir)/src/libcw/libcw.3.m4 >libcw.3 rm -f signatures functions # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: unixcw-3.6.0/src/libcw/libcw_utils.c0000644000175000017500000004644114000344554014341 00000000000000/* Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** @file libcw_utils.c @brief Utility functions that should be put in a common place. One of the utilities is cw_dlopen_internal() - a function that allowed me to drop compile-time dependency on ALSA libs and PulseAudio libs, and replace it with run-time dependency. You will find calls to dlclose() in libcw_alsa.c and libcw_pa.c. */ #include "config.h" #include /* dlopen() and related symbols */ #include #include /* INT_MAX, for clang. */ #include #include #include /* strtol() */ #include #include #if defined(HAVE_STRING_H) # include #endif #if defined(HAVE_STRINGS_H) # include #endif #if (defined(__unix__) || defined(unix)) && !defined(USG) # include #endif #include "cw_copyright.h" #include "libcw.h" #include "libcw2.h" #include "libcw_debug.h" #include "libcw_gen.h" #include "libcw_signal.h" #include "libcw_utils.h" #define MSG_PREFIX "libcw/utils: " extern cw_debug_t cw_debug_object; extern cw_debug_t cw_debug_object_ev; extern cw_debug_t cw_debug_object_dev; /* Human-readable labels of sound systems. Indexed by values of "enum cw_audio_systems". */ static const char * cw_sound_system_labels[] = { "None", "Null", "Console", "OSS", "ALSA", "PulseAudio", "Soundcard" }; /* Finalization and cleanup. */ static void cw_finalization_clock_internal(void); /** @brief Return version number of libcw library Return the version number of the library. Version numbers (major and minor) are returned as an int, composed of major_version << 16 | minor_version. @reviewed 2020-08-17 @return library's major and minor version number encoded as single int */ int cw_version(void) { char * endptr = NULL; /* LIBCW_VERSION: "current:revision:age", libtool notation. */ const unsigned long int current = strtoul(LIBCW_VERSION, &endptr, 10); const unsigned long int revision = strtoul(endptr + 1, &endptr, 10); __attribute__((unused)) unsigned long int age = strtoul(endptr + 1, &endptr, 10); fprintf(stderr, "current:revision:age: %lu:%lu:%lu\n", current, revision, age); return (int) ((current) << 16U | revision); } /** @brief Return version number of libcw library Return version number of the library, split into @p current, @p revision, @p age. These three properties are described here: http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html @reviewed 2020-08-17 @param[out] current 'current' part of library version number (may be NULL) @param[out] revision 'revision' part of library version number (may be NULL) @param[out] age 'age' part of library version number (may be NULL) @return CW_SUCCESS */ cw_ret_t cw_get_lib_version(int * current, int * revision, int * age) { char * endptr = NULL; /* LIBCW_VERSION: "current:revision:age", libtool notation. */ long int c = strtol(LIBCW_VERSION, &endptr, 10); if (current) { *current = (int) c; } long int r = strtol(endptr + 1, &endptr, 10); if (revision) { *revision = (int) r; } long int a = strtol(endptr + 1, &endptr, 10); if (age) { *age = (int) a; } cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_INTERNAL, CW_DEBUG_INFO, MSG_PREFIX "current:revision:age: %ld:%ld:%ld\n", c, r, a); return CW_SUCCESS; } cw_ret_t cw_get_package_version(__attribute__((unused)) int * major, __attribute__((unused)) int * minor, __attribute__((unused)) int * maintenance) { /* TODO: implement. */ return CW_FAILURE; } /** @brief Print libcw's license text to stdout Function prints to stdout information about libcw version, followed by short text presenting libcw's copyright and license notice. */ void cw_license(void) { int current = 0; int revision = 0; int age = 0; cw_get_lib_version(¤t, &revision, &age); printf("libcw version %d.%d.%d\n", current, revision, age); printf("%s\n", CW_COPYRIGHT); return; } /** @brief Get a readable label of given sound system The function returns one of following strings: None, Null, Console, OSS, ALSA, PulseAudio, Soundcard Returned pointer is owned and managed by the library. @internal TODO: change the declaration to "const char *const cw_get_audio_system_label(...)"? @endinternal @reviewed 2020-08-17 @param[in] sound_system ID of sound system @return sound system's label */ const char * cw_get_audio_system_label(int sound_system) { return cw_sound_system_labels[sound_system]; } /** @brief Convert microseconds to struct timespec Function fills fields of struct timespec @p t (seconds and nanoseconds) based on value of @p usecs. @p usecs should be non-negative. This function is just a simple wrapper for few lines of code. @reviewed 2020-08-17 @param[out] ts pointer to existing struct to be filled with data @param[in] usecs value to convert to timespec */ void cw_usecs_to_timespec_internal(struct timespec * ts, int usecs) { assert (usecs >= 0); assert (NULL != ts); const int sec = usecs / CW_USECS_PER_SEC; const int usec = usecs % CW_USECS_PER_SEC; ts->tv_sec = sec; ts->tv_nsec = usec * 1000; return; } void cw_usleep_internal(int usecs) { struct timespec remaining = { 0 }; cw_usecs_to_timespec_internal(&remaining, usecs); int rv = 0; do { struct timespec req = { .tv_sec = remaining.tv_sec, .tv_nsec = remaining.tv_nsec }; //fprintf(stderr, " -- sleeping for %ld s, %ld ns\n", req.tv_sec, req.tv_nsec); rv = nanosleep(&req, &remaining); if (rv) { //fprintf(stderr, " -- remains %ld s, %ld ns\n", remaining.tv_sec, remaining.tv_nsec); } } while (rv); return; } #if (defined(LIBCW_WITH_ALSA) || defined(LIBCW_WITH_PULSEAUDIO)) /** @brief Try to dynamically open shared library Function tries to open a shared library specified by @p library_name using dlopen() system function. On success, handle to open library is returned via @p handle. Name of the library should contain ".so" suffix, e.g.: "libasound.so.2", or "libpulse-simple.so". @reviewed 2020-08-17 @param[in] library_name name of library to test @param[out] handle handle to opened library @return CW_SUCCESS on success @return CW_FAILURE otherwise */ cw_ret_t cw_dlopen_internal(const char * library_name, void ** handle) { assert (NULL != library_name); dlerror(); void * h = dlopen(library_name, RTLD_LAZY); char * e = dlerror(); if (e) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "dlopen() fails for library %s with error: %s", library_name, e); return CW_FAILURE; } else { *handle = h; cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_STDLIB, CW_DEBUG_DEBUG, MSG_PREFIX "dlopen() succeeds for library %s", library_name); return CW_SUCCESS; } } #endif /** @brief Validate and return timestamp If an input timestamp @p in_timestamp is given (non-NULL pointer), validate it for correctness, and if valid, copy contents of @p in_timestamp into @p out_timestamp and return CW_SUCCESS. If @p in_timestamp is non-NULL and the timestamp is invalid, return CW_FAILURE with errno set to EINVAL. If @p in_timestamp is not given (NULL), get current time (with gettimeofday()), put it in @p out_timestamp and return CW_SUCCESS. If call to gettimeofday() fails, return CW_FAILURE. gettimeofday() sets its own errno. @p out_timestamp cannot be NULL. @reviewed 2020-08-17 @param[out] out_timestamp timestamp to be used by client code after the function call @param[in] in_timestamp timestamp to be validated @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_timestamp_validate_internal(struct timeval * out_timestamp, const struct timeval * in_timestamp) { cw_assert (NULL != out_timestamp, MSG_PREFIX "validate timestamp: pointer to output timestamp is NULL"); if (in_timestamp) { if (in_timestamp->tv_sec < 0 || in_timestamp->tv_usec < 0 || in_timestamp->tv_usec >= CW_USECS_PER_SEC) { errno = EINVAL; return CW_FAILURE; } else { *out_timestamp = *in_timestamp; return CW_SUCCESS; } } else { /* TODO: gettimeofday is susceptible to NTP syncs which can negatively impact measurements of time. */ if (0 != gettimeofday(out_timestamp, NULL)) { if (out_timestamp->tv_usec < 0) { // fprintf(stderr, "Negative usecs in %s\n", __func__); } perror(MSG_PREFIX "validate timestamp: gettimeofday"); return CW_FAILURE; } else { return CW_SUCCESS; } } } /** @brief Compare two timestamps Compare two timestamps and return the difference between them in microseconds, taking care to clamp values which would overflow an int. This routine always returns a positive integer in the range 0 to INT_MAX. @reviewed 2020-08-17 @param[in] earlier earlier (older) timestamp to compare @param[in] later later (newer) timestamp to compare @return difference between timestamps (in microseconds) */ int cw_timestamp_compare_internal(const struct timeval * earlier, const struct timeval * later) { /* Compare the timestamps, taking care on overflows. At 4 WPM, the dash length is 3*(1200000/4)=900,000 usecs, and the word gap is 2,100,000 usecs. With the maximum Farnsworth additional delay, the word gap extends to 20,100,000 usecs. This fits into an int with a lot of room to spare, in fact, an int can represent 2,147,483,647 usecs, or around 33 minutes. This is way, way longer than we'd ever want to differentiate, so if by some chance we see timestamps farther apart than this, and it ought to be very, very unlikely, then we'll clamp the return value to INT_MAX with a clear conscience. Note: passing nonsensical or bogus timevals in may result in unpredictable results. Nonsensical includes timevals with -ve tv_usec, -ve tv_sec, tv_usec >= 1,000,000, etc. To help in this, we check all incoming timestamps for "well-formedness". However, we assume the gettimeofday() call always returns good timevals. All in all, timeval could probably be a better thought-out structure. */ /* Calculate an initial delta, possibly with overflow. */ long delta_usec = (later->tv_sec - earlier->tv_sec) * CW_USECS_PER_SEC + later->tv_usec - earlier->tv_usec; /* Check specifically for overflow, and clamp if it did. */ if ((later->tv_sec - earlier->tv_sec) > (INT_MAX / CW_USECS_PER_SEC) + 1 || delta_usec < 0) { delta_usec = INT_MAX; // fprintf(stderr, "earlier = %10ld : %10ld\n", earlier->tv_sec, earlier->tv_usec); // fprintf(stderr, "later = %10ld : %10ld\n", later->tv_sec, later->tv_usec); } /* TODO: add somewhere a debug message informing that we are returning INT_MAX. */ return (int) delta_usec; /* TODO: remove casting. */ } /* Morse code controls and timing parameters. */ /** @brief Get speed limits Get (through function's arguments) limits on speed of Morse code that are supported by libcw. See CW_SPEED_MIN and CW_SPEED_MAX in libcw.h for values. Any of functions two arguments can be NULL - function won't update value of that argument. @reviewed 2020-08-17 @param[out] min_speed minimal allowed speed @param[out] max_speed maximal allowed speed */ void cw_get_speed_limits(int * min_speed, int * max_speed) { if (min_speed) { *min_speed = CW_SPEED_MIN; } if (max_speed) { *max_speed = CW_SPEED_MAX; } return; } /** @brief Get frequency limits Get (through function's arguments) limits on frequency that are supported by libcw. See CW_FREQUENCY_MIN and CW_FREQUENCY_MAX in libcw.h for values. Any of functions two arguments can be NULL - function won't update value of that argument. @reviewed 2020-08-17 @param[out] min_frequency minimal allowed frequency @param[out] max_frequency maximal allowed frequency */ void cw_get_frequency_limits(int * min_frequency, int * max_frequency) { if (min_frequency) { *min_frequency = CW_FREQUENCY_MIN; } if (max_frequency) { *max_frequency = CW_FREQUENCY_MAX; } return; } /** @brief Get volume limits Get (through function's arguments) limits on volume of sound supported by libcw and generated by generator. See CW_VOLUME_MIN and CW_VOLUME_MAX in libcw.h for values. Any of functions two arguments can be NULL - function won't update value of that argument. @reviewed 2020-08-17 @param[out] min_volume minimal allowed volume @param[out] max_volume maximal allowed volume */ void cw_get_volume_limits(int * min_volume, int * max_volume) { if (min_volume) { *min_volume = CW_VOLUME_MIN; } if (max_volume) { *max_volume = CW_VOLUME_MAX; } return; } /** @brief Get gap limits Get (through function's arguments) limits on gap in cw signal supported by libcw. See CW_GAP_MIN and CW_GAP_MAX in libcw.h for values. Any of functions two arguments can be NULL - function won't update value of that argument. @reviewed 2020-08-17 @param[out] min_gap minimal allowed gap @param[out] max_gap maximal allowed gap */ void cw_get_gap_limits(int * min_gap, int * max_gap) { if (min_gap) { *min_gap = CW_GAP_MIN; } if (max_gap) { *max_gap = CW_GAP_MAX; } return; } /** @brief Get tolerance limits Get (through function's arguments) limits on "tolerance" parameter supported by libcw. See CW_TOLERANCE_MIN and CW_TOLERANCE_MAX in libcw.h for values. Any of functions two arguments can be NULL - function won't update value of that argument. @reviewed 2020-08-17 @param[out] min_tolerance minimal allowed tolerance @param[out] max_tolerance maximal allowed tolerance */ void cw_get_tolerance_limits(int * min_tolerance, int * max_tolerance) { if (min_tolerance) { *min_tolerance = CW_TOLERANCE_MIN; } if (max_tolerance) { *max_tolerance = CW_TOLERANCE_MAX; } return; } /** @brief Get weighting limits Get (through function's arguments) limits on "weighting" parameter supported by libcw. See CW_WEIGHTING_MIN and CW_WEIGHTING_MAX in libcw.h for values. Any of functions two arguments can be NULL - function won't update value of that argument. @reviewed 2020-08-17 @param[out] min_weighting minimal allowed weighting @param[out] max_weighting maximal allowed weighting */ void cw_get_weighting_limits(int * min_weighting, int * max_weighting) { if (min_weighting) { *min_weighting = CW_WEIGHTING_MIN; } if (max_weighting) { *max_weighting = CW_WEIGHTING_MAX; } return; } /* Finalization and cleanup. */ /* We prefer to close the soundcard after a period of library inactivity, so that other applications can use it. Ten seconds seems about right. We do it in one-second timeouts so that any leaked pending timeouts from other facilities don't cause premature finalization. */ static const int CW_AUDIO_FINALIZATION_DELAY = 10000000; /* Counter counting down the number of clock calls before we finalize. */ static volatile bool cw_is_finalization_pending = false; static volatile int cw_finalization_countdown = 0; /* Use a mutex to suppress delayed finalizations on complete resets. */ static volatile bool cw_is_finalization_locked_out = false; /** @brief Tick a finalization clock If finalization is pending, decrement the countdown, and if this reaches zero, we've waited long enough to release sound and timeouts. */ void cw_finalization_clock_internal(void) { if (cw_is_finalization_pending) { /* Decrement the timeout countdown, and finalize if we reach zero. */ cw_finalization_countdown--; if (cw_finalization_countdown <= 0) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_FINALIZATION, CW_DEBUG_INFO, MSG_PREFIX "finalization timeout, closing down"); cw_sigalrm_restore_internal(); // cw_gen_release_internal(&cw_generator); cw_is_finalization_pending = false; cw_finalization_countdown = 0; } else { cw_debug_msg ((&cw_debug_object), CW_DEBUG_FINALIZATION, CW_DEBUG_INFO, MSG_PREFIX "finalization countdown %d", cw_finalization_countdown); /* Request another timeout. This results in a call to our cw_finalization_cancel_internal below; to ensure that it doesn't really cancel finalization, unset the pending flag, then set it back again after reqesting the timeout. */ cw_is_finalization_pending = false; cw_timer_run_with_handler_internal(CW_USECS_PER_SEC, NULL); cw_is_finalization_pending = true; } } return; } /** Set the finalization pending flag, and request a timeout to call the finalization function after a delay of a few seconds. */ void cw_finalization_schedule_internal(void) { if (!cw_is_finalization_locked_out && !cw_is_finalization_pending) { cw_timer_run_with_handler_internal(CW_USECS_PER_SEC, cw_finalization_clock_internal); /* Set the flag and countdown last; calling cw_timer_run_with_handler() * above results in a call to our cw_finalization_cancel_internal(), which clears the flag and countdown if we set them early. */ cw_is_finalization_pending = true; cw_finalization_countdown = CW_AUDIO_FINALIZATION_DELAY / CW_USECS_PER_SEC; cw_debug_msg ((&cw_debug_object), CW_DEBUG_FINALIZATION, CW_DEBUG_INFO, MSG_PREFIX "finalization scheduled"); } return; } /** Cancel any pending finalization on noting other library activity, indicated by a call from the timeout request function telling us that it is setting a timeout. */ void cw_finalization_cancel_internal(void) { if (cw_is_finalization_pending) { /* Cancel pending finalization and return to doing nothing. */ cw_is_finalization_pending = false; cw_finalization_countdown = 0; cw_debug_msg ((&cw_debug_object), CW_DEBUG_FINALIZATION, CW_DEBUG_INFO, MSG_PREFIX "finalization canceled"); } return; } /** @brief Reset all library features to their default states Clears the tone queue, receive buffers and retained state information, any current keyer activity, and any straight key activity, returns to silence, and closes soundcard and console devices. This function is suitable for calling from an application exit handler. */ void cw_complete_reset(void) { /* If the finalizer thinks it's pending, stop it, then temporarily lock out finalizations. */ cw_finalization_cancel_internal(); cw_is_finalization_locked_out = true; cw_generator_stop(); /* Call the reset functions for each subsystem. */ cw_reset_tone_queue(); cw_reset_receive(); cw_reset_keyer(); cw_reset_straight_key(); cw_generator_delete_internal(); cw_sigalrm_restore_internal(); /* Now we can re-enable delayed finalizations. */ cw_is_finalization_locked_out = false; return; } unixcw-3.6.0/src/libcw/libcw_data.h0000644000175000017500000000317514000344554014114 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef H_LIBCW_DATA #define H_LIBCW_DATA #include #include #include "libcw2.h" /* TODO: write a test that iterates over table of representations and verifies that all representations' lengths are within these bounds. */ #define CW_DATA_MIN_REPRESENTATION_LENGTH 1 /* Smallest representation contains single Dot or Dash. This value does not include space for terminating NUL. */ #define CW_DATA_MAX_REPRESENTATION_LENGTH 7 /* CHAR_BIT - 1. Does not include space for terminating NUL (TODO: check and explain why). */ #define CW_DATA_MIN_REPRESENTATION_HASH 2 #define CW_DATA_MAX_REPRESENTATION_HASH 255 typedef struct cw_entry_struct { const char character; /* Character represented. */ const char *const representation; /* Dot-dash pattern of the character. */ } cw_entry_t; /* Functions handling representation of a character. Representation looks like this: ".-" for "a", "--.." for "z", etc. */ cw_ret_t cw_data_init_r2c_hash_table_internal(const cw_entry_t * table[]); int cw_representation_to_character_internal(const char * representation); int cw_representation_to_character_direct_internal(const char * representation); unsigned int cw_representation_to_hash_internal(const char * representation); /* TODO: uint8_t return value (or maybe uint16_t?). */ const char * cw_character_to_representation_internal(int character); const char * cw_lookup_procedural_character_internal(int character, bool * is_usually_expanded); #endif /* #ifndef H_LIBCW_DATA */ unixcw-3.6.0/src/libcw/libcw_tq.c0000644000175000017500000007343614000344554013631 00000000000000/* Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** @file libcw_tq.c @brief Queue of tones to be converted by generator to pcm data and sent to sound sink. Tone queue: a circular list of tone durations and frequencies pending, with a pair of indexes: tail (enqueue) and head (dequeue). The indexes are used to manage addition and removal of tones from queue. The tone queue (the circular list) is implemented using constant size table. Explanation of "forever" tone: If a "forever" flag is set in a tone that is a last one on a tone queue, the tone should be constantly returned by dequeue function, without removing the tone - as long as it is a last tone on queue. Adding new, "non-forever" tone to the queue results in permanent dequeuing "forever" tone and proceeding to newly added tone. Adding the new "non-forever" tone ends generation of "forever" tone. The "forever" tone is useful for generating tones of duration unknown in advance. dequeue() function recognizes the "forever" tone and acts as described above; there is no visible difference between dequeuing N separate "non-forever" tones of duration D [us] each, and dequeuing a "forever" tone of duration D [us] N times in a row. Because of some corner cases related to "forever" tones it is very strongly advised to set "low water mark" level to no less than 2 tones. Tone queue data type is not visible to user of library's API. Tone queue is an integral part of a generator. Generator data type is visible to user of library's API. */ #include #include /* "PRIu32" */ #include #include #include "libcw2.h" #include "libcw.h" #include "libcw_debug.h" #include "libcw_gen.h" #include "libcw_signal.h" #include "libcw_tq.h" #include "libcw_tq_internal.h" #if defined(HAVE_STRING_H) # include #endif #if defined(HAVE_STRINGS_H) # include #endif #define MSG_PREFIX "libcw/tq: " /* The CW tone queue functions implement the following state graph: [start] | | v CW_TQ_EMPTY <---------------------------------+ v | [call enqueue()] | | | | [call dequeue() on empty tone queue] | | v [call dequeue(), last tone] ^ +-------------> CW_TQ_NONEMPTY >-------------------------CW_TQ_JUST_EMPTIED | v ^ v | | | | [call enqueue()] +----------------+ +------------------------------------+ [call dequeue(), not last tone] [call enqueue()] Future libcw API should (completely) hide tone queue from client code. The client code should only operate on a generator: enqueue tones to generator, flush a generator, register low water callback with generator etc. There is very little (or even no) need to explicitly reveal to client code this implementation detail called "tone queue". */ extern cw_debug_t cw_debug_object; extern cw_debug_t cw_debug_object_ev; extern cw_debug_t cw_debug_object_dev; /* Not used anymore. 2015.02.22. */ #if 0 /* Remember that tail and head are of unsigned type. Make sure that order of calculations is correct when tail < head. */ #define CW_TONE_QUEUE_LENGTH(m_tq) \ ( m_tq->tail >= m_tq->head \ ? m_tq->tail - m_tq->head \ : m_tq->capacity - m_tq->head + m_tq->tail) \ #endif /** @brief Create new tone queue Allocate and initialize new tone queue structure. @internal @reviewed 2020-10-17 @endinternal @return pointer to new tone queue on success @return NULL pointer on failure */ cw_tone_queue_t * cw_tq_new_internal(void) { /* TODO: do we really need to allocate the tone queue? If the queue is never a stand-alone object in user's code but only a member in generator, then maybe we don't have to malloc it. That would be one error source less. */ cw_tone_queue_t * tq = (cw_tone_queue_t *) calloc(1, sizeof (cw_tone_queue_t)); if (NULL == tq) { cw_debug_msg (&cw_debug_object, CW_DEBUG_TONE_QUEUE, CW_DEBUG_ERROR, MSG_PREFIX "new: failed to calloc() tone queue"); return (cw_tone_queue_t *) NULL; } pthread_mutex_init(&tq->wait_mutex, NULL); pthread_mutex_lock(&tq->wait_mutex); pthread_cond_init(&tq->wait_var, NULL); tq->head = 0; tq->tail = 0; tq->len = 0; tq->state = CW_TQ_EMPTY; tq->low_water_mark = 0; tq->low_water_callback = NULL; tq->low_water_callback_arg = NULL; tq->gen = (cw_gen_t *) NULL; /* This field will be set by generator code. */ cw_ret_t cwret = cw_tq_set_capacity_internal(tq, CW_TONE_QUEUE_CAPACITY_MAX, CW_TONE_QUEUE_HIGH_WATER_MARK_MAX); cw_assert (CW_SUCCESS == cwret, MSG_PREFIX "new: failed to set initial capacity of tq"); pthread_mutex_unlock(&tq->wait_mutex); return tq; } /** @brief Delete tone queue Function deallocates all resources held by @p tq, deallocates the @p tq itself, and sets the pointer to NULL. @internal @reviewed 2020-10-17 @endinternal @param[in] tq tone queue to delete */ void cw_tq_delete_internal(cw_tone_queue_t ** tq) { cw_assert (NULL != tq, MSG_PREFIX "delete: pointer to tq is NULL"); if (NULL == tq || NULL == *tq) { return; } /* Don't call pthread_cond_destroy(). When pthread_cond_wait() is waiting for signal, and a SIGINT signal arrives, the _wait() function will be interrupted, application's signal handler will call cw_gen_delete(), which will call cw_tq_delete_internal(), which will call pthread_cond_destroy(). pthread_cond_destroy() called from (effectively) signal handler will signal all waiters to release condition variable before destroying conditional variable, but since our _wait() is interrupted by signal, it won't release the condition variable. So we have a deadlock: _destroy() telling _wait() to stop waiting, but _wait() being interrupted by signal, handled by function called _destroy(). So don't call pthread_cond_destroy(). */ //pthread_cond_destroy(&(*tq)->wait_var); pthread_mutex_destroy(&(*tq)->wait_mutex); free(*tq); *tq = (cw_tone_queue_t *) NULL; return; } /** @brief Reset state of given tone queue This makes the @p tq empty, but without calling low water mark callback. @internal @reviewed 2020-10-17 @endinternal */ void cw_tq_make_empty_internal(cw_tone_queue_t * tq) { pthread_mutex_lock(&tq->wait_mutex); bool broadcast = false; if (tq->len > 0 || tq->state != CW_TQ_EMPTY) { broadcast = true; } tq->head = 0; tq->tail = 0; tq->len = 0; tq->state = CW_TQ_EMPTY; if (broadcast) { //fprintf(stderr, "[II] " MSG_PREFIX "%s:%d broadcast on 'make empty'\n", __func__, __LINE__); pthread_cond_broadcast(&tq->wait_var); } pthread_mutex_unlock(&tq->wait_mutex); return; } /** @brief Set capacity and high water mark for queue Set two parameters of queue: total capacity of the queue, and high water mark. When calling the function, client code must provide valid values of both parameters. The two parameters refer to tones, not to characters. Calling the function *by a client code* for a queue is optional, as a queue has these parameters always set to default values (CW_TONE_QUEUE_CAPACITY_MAX and CW_TONE_QUEUE_HIGH_WATER_MARK_MAX) by internal call to cw_tq_new_internal(). @p capacity must be no larger than CW_TONE_QUEUE_CAPACITY_MAX. @p high_water_mark must be no larger than CW_TONE_QUEUE_HIGH_WATER_MARK_MAX. Both values must be larger than zero (this condition is subject to changes in future revisions of the library). @p high_water_mark must be no larger than @p capacity. @exception EINVAL any of the two parameters (@p capacity or @p high_water_mark) is invalid. @internal @reviewed 2020-07-28 @endinternal @param[in] tq tone queue to configure @param[in] capacity new capacity of queue @param[in] high_water_mark high water mark for the queue @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_tq_set_capacity_internal(cw_tone_queue_t * tq, size_t capacity, size_t high_water_mark) { cw_assert (NULL != tq, MSG_PREFIX "set capacity: tq is NULL"); if (NULL == tq) { return CW_FAILURE; } if (0 == high_water_mark || high_water_mark > CW_TONE_QUEUE_HIGH_WATER_MARK_MAX) { /* If we allowed high water mark to be zero, the queue would not accept any new tones: it would constantly be full. Any attempt to enqueue any tone would result in "sorry, new tones would reach above high_water_mark of the queue". */ errno = EINVAL; return CW_FAILURE; } if (0 == capacity || capacity > CW_TONE_QUEUE_CAPACITY_MAX) { /* Tone queue of capacity zero doesn't make much sense, so capacity == 0 is not allowed. */ errno = EINVAL; return CW_FAILURE; } if (high_water_mark > capacity) { errno = EINVAL; return CW_FAILURE; } tq->capacity = capacity; tq->high_water_mark = high_water_mark; return CW_SUCCESS; } /** @brief Return capacity of a queue Return number of tones that the queue can hold. @internal @reviewed 2020-07-29 @endinternal @param[in] tq tone queue, for which you want to get capacity @return capacity of tone queue */ size_t cw_tq_capacity_internal(const cw_tone_queue_t * tq) { cw_assert (NULL != tq, MSG_PREFIX "get capacity: tone queue is NULL"); return tq->capacity; } /** @brief Return high water mark of a queue @reviewed 2017-01-30 @internal @reviewed 2020-07-28 @endinternal @param[in] tq tone queue from which to get high water mark @return high water mark of tone queue */ size_t cw_tq_get_high_water_mark_internal(const cw_tone_queue_t * tq) { cw_assert (NULL != tq, MSG_PREFIX "get high water mark: tone queue is NULL"); return tq->high_water_mark; } /** @brief Return current number of items (tones) in tone queue @internal @reviewed 2020-10-17 @endinternal @param[in] tq tone queue @return the count of tones currently held in the tone queue */ size_t cw_tq_length_internal(cw_tone_queue_t * tq) { pthread_mutex_lock(&tq->wait_mutex); const size_t len = tq->len; pthread_mutex_unlock(&tq->wait_mutex); return len; } /** @brief Get previous index to queue Calculate index of previous slot in queue, relative to given @p ind. The function calculates the index taking circular wrapping into consideration. This function doesn't care if the slots are occupied or not. @internal @reviewed 2020-07-29 @endinternal @param[in] tq tone queue for which to calculate previous index @param[in] ind index in relation to which to calculate index of previous slot in queue @return index of previous slot in queue */ size_t cw_tq_prev_index_internal(const cw_tone_queue_t * tq, size_t ind) { return ind == 0 ? tq->capacity - 1 : ind - 1; } /** @brief Get next index to queue Calculate index of next slot in queue, relative to given @p ind. The function calculates the index taking circular wrapping into consideration. This function doesn't care if the slots are occupied or not. @internal @reviewed 2020-07-29 @endinternal @param[in] tq tone queue for which to calculate next index @param[in] ind index in relation to which to calculate index of next slot in queue @return index of next slot in queue */ size_t cw_tq_next_index_internal(const cw_tone_queue_t * tq, size_t ind) { return ind == tq->capacity - 1 ? 0 : ind + 1; } /** @brief Dequeue a tone from tone queue If there are any tones in queue (i.e. queue's state is CW_TQ_NONEMPTY), function copies tone from @p tq queue into @p tone supplied by caller, removes the tone from @p tq queue (with exception for "forever" tone). If there are no tones in @p tq queue (i.e. queue's state at the moment of function call is CW_TQ_EMPTY), function does nothing with @p tone. dequeue() is not a totally dumb function. It understands how "forever" tone works and how it should be handled. If the last tone in queue has "forever" flag set, the function won't permanently dequeue it. Instead, it will keep returning (through @p tone) the tone on every call, until a new tone is added to the queue after the "forever" tone. @p tq must be a valid queue. @p tone must be allocated by caller. If queue @p tq has registered low water callback function, and condition to call the function is met after dequeue has occurred, the function calls the callback. @internal @reviewed 2020-10-17 @endinternal @param[in] tq tone queue to dequeue tone from @param[out] tone dequeued tone @return current state of tone queue (state after dequeueing current tone) */ cw_queue_state_t cw_tq_dequeue_internal(cw_tone_queue_t * tq, cw_tone_t * tone) { pthread_mutex_lock(&tq->wait_mutex); bool call_callback = false; const size_t len_before = tq->len; const cw_queue_state_t state_before = tq->state; switch (tq->state) { case CW_TQ_EMPTY: /* Ignore calls if queue is empty. */ break; case CW_TQ_JUST_EMPTIED: /* There are no more tones to dequeue, but we still need to update the state. */ tq->state = CW_TQ_EMPTY; break; case CW_TQ_NONEMPTY: cw_assert (tq->len > 0, MSG_PREFIX "dequeue: tone queue is CW_TQ_NONEMPTY, but tq->len = %zu\n", tq->len); call_callback = cw_tq_dequeue_sub_internal(tq, tone); if (0 == tq->len) { tq->state = CW_TQ_JUST_EMPTIED; } break; default: cw_debug_msg (&cw_debug_object, CW_DEBUG_TONE_QUEUE, CW_DEBUG_ERROR, MSG_PREFIX "unexpected queue state %d", tq->state); break; } const cw_queue_state_t queue_state = tq->state; #if 0 /* Verbose debug. */ cw_debug_msg (&cw_debug_object, CW_DEBUG_TONE_QUEUE, CW_DEBUG_DEBUG MSG_PREFIX_ "queue state = %d, dequeued tone %dHz %dus\n", queue_state, tone->frequency, tone->duration); #endif if (len_before != tq->len || state_before != tq->state) { //fprintf(stderr, "[II] " MSG_PREFIX "%s:%d broadcast on 'dequeue'\n", __func__, __LINE__); pthread_cond_broadcast(&tq->wait_var); } pthread_mutex_unlock(&tq->wait_mutex); /* Since client's callback can use libcw functions that call pthread_mutex_lock(&tq->...), we should call the callback *after* we unlock queue's mutexes in this function. */ if (call_callback) { (*(tq->low_water_callback))(tq->low_water_callback_arg); } return queue_state; } /** @brief Handle dequeueing of tone from non-empty tone queue Function gets a tone from head of the queue. If this was a last tone in queue, and it was a "forever" tone, the tone is not removed from the queue (the philosophy of "forever" tone), and "low watermark" condition is not checked. Otherwise remove the tone from tone queue, check "low watermark" condition, and return value of the check (true/false). In any case, dequeued tone is returned through @p tone. @p tone must be a valid pointer provided by caller. TODO: add unit tests @internal @reviewed 2020-10-17 @endinternal @param[in] tq tone queue to dequeue from @param[out] tone dequeued tone @return true if a condition for calling "low watermark" callback is true @return false otherwise */ bool cw_tq_dequeue_sub_internal(cw_tone_queue_t * tq, cw_tone_t * tone) { CW_TONE_COPY(tone, &(tq->queue[tq->head])); if (tone->is_forever && tq->len == 1) { /* Don't permanently remove the last tone that is "forever" tone in queue. Keep it in tq until client code adds next tone (this means possibly waiting forever). Queue's head should not be iterated. "forever" tone should be played by caller code, this is why we return the tone through function's argument. */ /* Don't call "low watermark" callback for "forever" tone. As the function's top-level comment has stated: avoid endlessly calling the callback if the only queued tone is "forever" tone.*/ return false; } /* Used to check if we passed tq's low level watermark. */ const size_t tq_len_before = tq->len; /* Dequeue. We already have the tone, now update tq's state. */ tq->head = cw_tq_next_index_internal(tq, tq->head); tq->len--; if (tq->len == 0) { /* Verify basic property of empty tq. */ cw_assert (tq->head == tq->tail, MSG_PREFIX "dequeue sub: head: %zu, tail: %zu", tq->head, tq->tail); } #if 0 /* Disabled because these debug messages produce lots of output to console. Enable only when necessary. */ cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_TONE_QUEUE, CW_DEBUG_DEBUG, MSG_PREFIX "dequeue sub: dequeue tone %d us, %d Hz", tone->duration, tone->frequency); cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_TONE_QUEUE, CW_DEBUG_DEBUG, MSG_PREFIX "dequeue sub: head = %zu, tail = %zu, length = %zu -> %zu", tq->head, tq->tail, tq_len_before, tq->len); #endif /* You can remove this assert in future. It is only temporary, to check that some changes introduced on 2015.03.01 didn't break one assumption. */ cw_assert (!(tone->is_forever && tq_len_before == 1), MSG_PREFIX "dequeue sub: 'forever' tone appears!"); bool call_callback = false; if (tq->low_water_callback) { /* It may seem that the double condition in 'if ()' is redundant, but for some reason it is necessary. Be very, very careful when modifying this. */ if (tq_len_before > tq->low_water_mark && tq->len <= tq->low_water_mark) { call_callback = true; } } return call_callback; } /** @brief Add tone to tone queue This routine adds the new tone to the queue, and - if necessary - sends a signal to generator, so that the generator can dequeue the tone. The function does not accept tones with frequency outside of CW_FREQUENCY_MIN-CW_FREQUENCY_MAX range. If duration of a tone (tone->duration) is zero, the function does not add it to tone queue and returns CW_SUCCESS. The function does not accept tones with negative values of duration. @internal @reviewed 2020-10-17 @endinternal @exception EINVAL invalid values of @p tone @exception EAGAIN tone not enqueued because tone queue is full @param[in] tq tone queue to enqueue to @param[in] tone tone to enqueue @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_tq_enqueue_internal(cw_tone_queue_t * tq, const cw_tone_t * tone) { cw_assert (tq, MSG_PREFIX "enqueue: tone queue is null"); cw_assert (tone, MSG_PREFIX "enqueue: tone is null"); /* Check the arguments given for realistic values. */ if (tone->frequency < CW_FREQUENCY_MIN || tone->frequency > CW_FREQUENCY_MAX) { errno = EINVAL; return CW_FAILURE; } if (tone->duration < 0) { errno = EINVAL; return CW_FAILURE; } if (tone->duration == 0) { /* Drop empty tone. It won't be played anyway, and for now there are no other good reasons to enqueue it. While it may happen in higher-level code to create such tone, but there is no need to spend time on it here. */ cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_TONE_QUEUE, CW_DEBUG_INFO, MSG_PREFIX "enqueue: ignoring tone with duration == 0"); return CW_SUCCESS; } pthread_mutex_lock(&tq->wait_mutex); if (tq->len == tq->capacity) { /* Tone queue is full. */ errno = EAGAIN; cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_TONE_QUEUE, CW_DEBUG_ERROR, MSG_PREFIX "enqueue: can't enqueue tone, tq is full"); pthread_mutex_unlock(&tq->wait_mutex); return CW_FAILURE; } // cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_TONE_QUEUE, CW_DEBUG_DEBUG, MSG_PREFIX "enqueue: enqueue tone %d us, %d Hz", tone->duration, tone->frequency); /* Enqueue the new tone. Notice that tail is incremented after adding a tone. This means that for empty tq new tone is inserted at index tail == head (which should be kind of obvious). */ tq->queue[tq->tail] = *tone; tq->tail = cw_tq_next_index_internal(tq, tq->tail); tq->len++; tq->state = CW_TQ_NONEMPTY; /* tq->len and perhaps tq->state have changed. Signal this fact to listeners. A loop in cw_gen_dequeue_and_generate_internal() function may await for the queue to be filled with new tones to dequeue and play. It waits for a notification from tq that there are some new tones in tone queue. We will need to use pthread_cond_broadcast() to make sure the notification reaches all listeners. */ // fprintf(stderr, "[II] " MSG_PREFIX "%s:%d broadcast on 'enqueue'\n", __func__, __LINE__); pthread_cond_broadcast(&tq->wait_var); pthread_mutex_unlock(&tq->wait_mutex); return CW_SUCCESS; } /** @brief Register callback for low queue state Register a function to be called automatically by the dequeue routine whenever the count of tones in tone queue falls to a given @p level. To be more precise: the callback is called by queue's dequeue function if, after dequeueing a tone, the function notices that tone queue length has become equal or less than @p level. @p level can't be equal to or larger than tone queue capacity. If @p level is zero, the behaviour of the mechanism is not guaranteed to work correctly. If @p callback_func is NULL then the mechanism becomes disabled. @p callback_arg will be passed to @p callback_func. @exception EINVAL @p level is invalid @internal @reviewed 2020-08-31 @endinternal @param[in] tq tone queue in which to register a callback @param[in] callback_func callback function to be registered @param[in] callback_arg argument for callback_func to pass return value @param[in] level low level of queue triggering call of the callback @return CW_SUCCESS on successful registration @return CW_FAILURE on failure */ cw_ret_t cw_tq_register_low_level_callback_internal(cw_tone_queue_t * tq, cw_queue_low_callback_t callback_func, void * callback_arg, size_t level) { if (level >= tq->capacity) { errno = EINVAL; return CW_FAILURE; } tq->low_water_mark = level; tq->low_water_callback = callback_func; tq->low_water_callback_arg = callback_arg; return CW_SUCCESS; } /** @brief Wait for the current tone to complete The routine always returns CW_SUCCESS. TODO: add unit test for this function. TODO: clarify behaviour when current tone is 'forever' tone. TODO: clarify what happens if there is no tone in progress. @internal @reviewed 2020-07-29 @endinternal @param[in] tq tone queue to wait on @return CW_SUCCESS */ cw_ret_t cw_tq_wait_for_end_of_current_tone_internal(cw_tone_queue_t * tq) { pthread_mutex_lock(&tq->wait_mutex); /* According to man page, spurious wakeups of pthread_cond_wait() may occur. Call the function in loop with two conditions to work around these wakeups. First of the two conditions checks this: are we still in the same position in queue as at the beginning of function call (i.e. has anything been dequeued)? Second of the two conditions checks if there is any tone generation in progress. As long as state of queue is different than CW_TQ_EMPTY, it means that generator is still in the middle of generating something: either some tone from the middle of the queue (then queue state is CW_TQ_NONEMPTY), or the last tone from tone queue (then queue state is CW_TQ_JUST_EMPTIED). TODO: perhaps we shouldn't be checking three-values state of queue, but a property (state) of generator. Maybe the generator should be telling us if it is still generating something. Spurious wakeups noticed on: Intel Celeron 430, Ubuntu 18.04.5 x86_64, kernel 5.4.0-47 TODO: check that our usage of wait_mutex in this function and in other tq functions allows us to safely get tq->head. */ /* Wait for the queue index to change or the dequeue to go completely empty. */ const size_t check_tq_head = tq->head; while (tq->head == check_tq_head && tq->state != CW_TQ_EMPTY) { pthread_cond_wait(&tq->wait_var, &tq->wait_mutex); } pthread_mutex_unlock(&tq->wait_mutex); #if 0 /* Original implementation using signals. */ /* This code has been disabled some time before 2017-01-30. */ /* Wait for the head index to change or the dequeue to go idle. */ size_t check_tq_head = tq->head; while (tq->head == check_tq_head && tq->state != CW_TQ_EMPTY) { cw_signal_wait_internal(); } #endif return CW_SUCCESS; } /** @brief Wait for the tone queue to drain until only as many tones as given in @p level remain queued This function is for use by programs that want to optimize themselves to avoid the cleanup that happens when the tone queue drains completely; such programs have a short time in which to add more tones to the queue. The function returns when queue's level is equal or lower than @p level. If at the time of function call the level of queue is already equal or lower than @p level, function returns immediately. Notice that generator must be running (started with cw_gen_start()) when this function is called, otherwise it will be waiting forever for a change of tone queue's level that will never happen. TODO: perhaps add checking if generator is running. @internal @reviewed 2020-07-29 @endinternal @param[in] tq tone queue to wait on @param[in] level low level in queue, at which to return @return CW_SUCCESS */ cw_ret_t cw_tq_wait_for_level_internal(cw_tone_queue_t * tq, size_t level) { /* Wait until the queue length is at or below given level. */ pthread_mutex_lock(&tq->wait_mutex); while (tq->len > level) { pthread_cond_wait(&tq->wait_var, &tq->wait_mutex); } pthread_mutex_unlock(&tq->wait_mutex); #if 0 /* Original implementation using signals. */ /* This code has been disabled some time before 2017-01-30. */ /* Wait until the queue length is at or below critical level. */ while (cw_tq_length_internal(tq) > level) { cw_signal_wait_internal(); } #endif return CW_SUCCESS; } /** @brief See if the tone queue is full This is a helper subroutine created so that I can pass a test tone queue in unit tests. The 'cw_is_tone_queue_full() works only on library's default/global tone queue object. @internal @reviewed 2020-07-29 @endinternal @param[in] tq tone queue to check @return true if tone queue is full @return false if tone queue is not full */ bool cw_tq_is_full_internal(const cw_tone_queue_t * tq) { /* TODO: shouldn't we lock tq when making the comparison? */ return tq->len == tq->capacity; } /** @brief Force emptying tone queue. Wait until it's really empty. Notice that because this function uses cw_tq_wait_for_level_internal(), generator must be running (started with cw_gen_start()) when this function is called, otherwise it will be waiting forever for a change of tone queue's level that will never happen. @internal @reviewed 2020-10-17 @endinternal @param[in] tq tone queue to empty */ void cw_tq_flush_internal(cw_tone_queue_t * tq) { /* Force zero length state. */ cw_tq_make_empty_internal(tq); /* TODO: is this necessary? We have already reset queue state. */ /* This has been disabled on 2020-10-17. */ //cw_tq_wait_for_level_internal(tq, 0); #if 0 /* Original implementation using signals. */ /* This code has been disabled some time before 2017-01-30. */ /* If we can, wait until the dequeue goes idle. */ if (!cw_sigalrm_is_blocked_internal()) { cw_tq_wait_for_level_internal(tq, 0); } #endif return; } /** @brief Check if tone queue is non-empty @internal @reviewed 2020-07-29 @endinternal @param[in] tq tone queue to check @return true if queue is non-empty @return false otherwise */ bool cw_tq_is_nonempty_internal(const cw_tone_queue_t * tq) { /* TODO: shouldn't we lock tq when making the comparison? */ return CW_TQ_NONEMPTY == tq->state; } /** @brief Attempt to remove all tones constituting full, single character Try to remove all tones until and including first tone with ->is_first tone flag set. The function removes character's tones only if all the tones, including the first tone in the character, are still in tone queue. TODO: write tests for this function @internal @reviewed 2020-10-17 @endinternal @param[in] tq tone queue from which to remove tones @return CW_SUCCESS if a character has been removed successfully @return CW_FAILURE otherwise */ cw_ret_t cw_tq_remove_last_character_internal(cw_tone_queue_t * tq) { cw_ret_t cwret = CW_FAILURE; pthread_mutex_lock(&tq->wait_mutex); size_t len = tq->len; size_t idx = tq->tail; bool is_found = false; while (len > 0) { --len; idx = cw_tq_prev_index_internal(tq, idx); if (tq->queue[idx].is_first) { is_found = true; break; } } if (is_found) { tq->len = len; tq->tail = idx; cwret = CW_SUCCESS; if (0 == tq->len) { tq->state = CW_TQ_JUST_EMPTIED; } } pthread_mutex_unlock(&tq->wait_mutex); return cwret; } unixcw-3.6.0/src/libcw/libcw_gen.c0000644000175000017500000031455014000647135013752 00000000000000/* Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** @file libcw_gen.c @brief Generate pcm samples according to tones from tone queue, and send them to sound sink. Functions operating on one of core elements of libcw: a generator. Generator is an object that has access to sound sink (soundcard, console buzzer, null sound device) and that can generate dots and dashes using the sound sink. You can request generator to produce sound by using *_enqeue_*() functions. The inner workings of the generator seem to be quite simple: 1. dequeue tone from tone queue 2. recalculate tone duration in microseconds into tone length in samples 3. for every sample in tone, calculate sine wave sample and put it in generator's constant size buffer 4. if buffer is full of sine wave samples, push it to sound sink 5. since buffer is shorter than (almost) any tone, you will recalculate contents of the buffer and push it to sound sink multiple times per tone 6. if you iterated over all samples in tone, but you still didn't fill up that last buffer, go to step #1 7. if there are no more tones in queue, pad the buffer with silence, and push the buffer to sound sink. Looks simple, right? But it's the little details that ruin it all. One of the details is tone's slopes. TODO: we need additional function that returns only after all tones have been sent to sound sink AND played. Currently client program can call cw_gen_wait_for_queue_level(gen, 0), but the function returns when the last tone is still being played. That's too early in some situations, we need a function that returns after the last tone has been played and generator returned to "empty,idle" state. */ #include "config.h" #include #include /* uint32_t */ #include #include #include #include #include #if defined(HAVE_STRING_H) # include #endif #if defined(HAVE_STRINGS_H) # include #endif #if defined(__linux__) #include /* prctl() */ #elif defined(__FreeBSD__) #include /* pthread_set_name_np() */ #endif #include "libcw2.h" #include "libcw_alsa.h" #include "libcw_console.h" #include "libcw_data.h" #include "libcw_debug.h" #include "libcw_gen.h" #include "libcw_gen_internal.h" #include "libcw_null.h" #include "libcw_oss.h" #include "libcw_rec.h" #include "libcw_signal.h" #include "libcw_utils.h" #define MSG_PREFIX "libcw/gen: " /* Measuring how long some thread operations take. */ #define LIBCW_GEN_DEBUG_THREAD_TIMING 1 /* Our own definition, to have it as a float. */ static const float CW_PI = 3.14159265358979323846F; /* From libcw_debug.c. */ extern cw_debug_t cw_debug_object; extern cw_debug_t cw_debug_object_ev; extern cw_debug_t cw_debug_object_dev; /* Most of sound systems (excluding console) should be configured to have specific sample rate. Some sound systems (with connection with given hardware) can support several different sample rates. Values of supported sample rates are standardized. Here is a list of them to be used by this library. When the library configures given sound system, it tries if the system will accept a sample rate from the table, starting from the first one. If a sample rate is accepted, rest of sample rates is not tested anymore. */ const unsigned int cw_supported_sample_rates[] = { 44100, 48000, 32000, 22050, 16000, 11025, /* This is the lowest value, dictated by value of CW_FREQUENCY_MAX, but in practice I found that with this value the generation of sound rarely works at all. */ 8000, 0 /* guard */ }; static cw_ret_t cw_gen_value_tracking_internal(cw_gen_t * gen, const cw_tone_t * tone, cw_queue_state_t queue_state); static void cw_gen_value_tracking_set_value_internal(cw_gen_t * gen, volatile cw_key_t * key, cw_key_value_t value); static void cw_gen_empty_tone_calculate_samples_size_internal(const cw_gen_t * gen, cw_tone_t * tone); static void cw_gen_silencing_tone_calculate_samples_size_internal(const cw_gen_t * gen, cw_tone_t * tone); static void cw_gen_tone_calculate_samples_size_internal(const cw_gen_t * gen, cw_tone_t * tone); /* Every sound system opens an sound device: a default device, or some other device. Default devices have their default names, and here is a list of them. It is indexed by values of "enum cw_audio_systems". */ static const char * default_sound_devices[] = { (char *) NULL, /* CW_AUDIO_NONE */ CW_DEFAULT_NULL_DEVICE, /* CW_AUDIO_NULL */ CW_DEFAULT_CONSOLE_DEVICE, CW_DEFAULT_OSS_DEVICE, CW_DEFAULT_ALSA_DEVICE, CW_DEFAULT_PA_DEVICE, (char *) NULL }; /* just in case someone decided to index the table with CW_AUDIO_SOUNDCARD */ /* Generic constants - common for all sound systems (or not used in some of systems). */ static const int CW_AUDIO_VOLUME_RANGE = (1U << 15U); /* 2^15 = 32768 */ /* Shortest duration of time (in microseconds) that is used by libcw for idle waiting and idle loops. If a libcw function needs to wait for something, or make an idle loop, it should call usleep(N * gen->quantum_duration) This is also duration of a single "forever" tone. Don't make the quantum duration too short. Short quantum duration will have two negative results: 1. you can't create a nice slope with just 4 samples - you will hear a click. 2. you will get very frequent dequeues of 'forever' tone that has quantum duration. n_samples = gen->sample_rate * duration / (usecs per sec); (code calculating n_samples uses slightly modified formula) sample rate | duration | n || duration | n ------------------------------------------------ 48000 | 100 | 4 || 500 | 24 44100 | 100 | 4 || 500 | 22 32000 | 100 | 3 || 500 | 16 22050 | 100 | 2 || 500 | 11 16000 | 100 | 1 || 500 | 8 11025 | 100 | 1 || 500 | 5 8000 | 100 | 0 || 500 | 4 */ static const int CW_AUDIO_QUANTUM_DURATION_INITIAL = 500; /* [us] */ /** @brief Get a readable label of current sound system Get a human-readable string describing sound system associated currently with given @p gen. The function returns through @p buffer one of following strings: "None", "Null", "Console", "OSS", "ALSA", "PulseAudio", "Soundcard". @internal @reviewed 2020-08-04 @endinternal @param[in] gen generator for which to check sound system label @param[out] buffer output buffer where the label will be saved @param[in] size total size of the buffer (including space for terminating NUL) @return @p buffer */ char * cw_gen_get_sound_system_label_internal(const cw_gen_t * gen, char * buffer, size_t size) { if (buffer) { snprintf(buffer, size, "%s", cw_get_audio_system_label(gen->sound_system)); } return buffer; } /* TODO: the function must detect already running generator, and return failure when it is detected. */ cw_ret_t cw_gen_start(cw_gen_t * gen) { gen->phase_offset = 0.0F; #ifdef GENERATOR_CLIENT_THREAD /* This generator exists in client's application thread. Generator's 'dequeue and generate' function will be a separate thread. */ gen->library_client.thread_id = pthread_self(); #endif if (gen->sound_system != CW_AUDIO_NULL && gen->sound_system != CW_AUDIO_CONSOLE && gen->sound_system != CW_AUDIO_OSS && gen->sound_system != CW_AUDIO_ALSA && gen->sound_system != CW_AUDIO_PA) { gen->do_dequeue_and_generate = false; cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "unsupported sound system %d", gen->sound_system); return CW_FAILURE; } /* This should be set to true before launching cw_gen_dequeue_and_generate_internal(), because loop in the function run only when the flag is set. */ gen->do_dequeue_and_generate = true; #if LIBCW_GEN_DEBUG_THREAD_TIMING /* Debug code to measure how long it takes to create thread. */ struct timeval before; struct timeval after; gettimeofday(&before, NULL); #endif /* cw_gen_dequeue_and_generate_internal() is THE function that does the main job of generating tones. */ int rv = pthread_create(&gen->thread.id, &gen->thread.attr, cw_gen_dequeue_and_generate_internal, (void *) gen); if (rv != 0) { gen->do_dequeue_and_generate = false; cw_debug_msg (&cw_debug_object, CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "failed to create %s generator thread", cw_get_audio_system_label(gen->sound_system)); return CW_FAILURE; } else { /* TODO: shouldn't we be doing it in generator's thread function? */ gen->thread.running = true; #if LIBCW_GEN_DEBUG_THREAD_TIMING /* Debug code to measure how long it takes to create thread. My main laptop: ALSA: 32 - 79 us PulseAudio: 20 - 65 us */ gettimeofday(&after, NULL); const int delta = cw_timestamp_compare_internal(&before, &after); cw_debug_msg (&cw_debug_object, CW_DEBUG_GENERATOR, CW_DEBUG_INFO, MSG_PREFIX "generator thread timing: creating thread took %d us", delta); #endif /* FIXME: For some yet unknown reason we have to put usleep() here, otherwise a generator may work incorrectly */ usleep(100000); #ifdef LIBCW_WITH_DEV cw_dev_debug_print_generator_setup_internal(gen); #endif return CW_SUCCESS; } } /** @brief Silence the generator Force a sound sink currently used by generator @p gen to go silent. The function does not clear/flush tone queue, nor does it stop the generator. It just makes sure that sound sink (console / OSS / ALSA / PulseAudio) does not produce a sound of any frequency and any volume. You probably want to call cw_tq_flush_internal(gen->tq) before calling this function. TODO: shouldn't cw_tq_flush_internal() be called inside of this function? @internal @reviewed 2020-10-12 @endinternal @param[in] gen generator using a sound sink that should be silenced @return CW_SUCCESS on success @return CW_FAILURE on failure to silence a sound sink */ cw_ret_t cw_gen_silence_internal(cw_gen_t * gen) { if (NULL == gen) { /* This may happen because the process of finalizing usage of libcw is rather complicated. This should be somehow resolved. */ cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_WARNING, MSG_PREFIX "called the function for NULL generator"); return CW_SUCCESS; } if (!gen->thread.running) { /* Silencing a generator means enqueueing and generating a tone with zero frequency. We shouldn't do this when a "dequeue-and-generate-a-tone" function is not running (anymore). This is not an error situation, so return CW_SUCCESS. */ return CW_SUCCESS; } #if 1 /* Tell 'dequeue and generate' thread function to go silent. TODO: What if the last tone on queue is a Very Long Tone, and silencing the generator will take a long time? This is a real problem that can be observed with large GAP parameter (e.g. 60): a program that is using long gaps is waiting for a long time before exiting on Ctrl-C. */ gen->silencing_initialized = true; cw_gen_wait_for_queue_level(gen, 0); /* Somewhere there may be a key in "down" state and we need to make it go "up", regardless of sound sink (even for CW_AUDIO_NULL, because that sound system can also be used with a key). Otherwise the key may stay in "down" state forever and the sound will be played after "silencing" of the generator. TODO: this code is not being called in some of libcw tests, e.g. in this one: ./src/libcw/tests/libcw_tests -S c -d /dev/console -N legacy_api_test_send_character_and_string */ cw_tone_t tone; CW_TONE_INIT(&tone, 0, gen->quantum_duration, CW_SLOPE_MODE_NO_SLOPES); tone.debug_id = 'd'; cw_ret_t cwret = cw_tq_enqueue_internal(gen->tq, &tone); cw_gen_wait_for_queue_level(gen, 0); cw_gen_wait_for_end_of_current_tone(gen); if (gen->sound_system == CW_AUDIO_CONSOLE) { /* Just in case... TODO: is it still necessary after adding the quantum of silence above? */ cw_console_silence_internal(gen); } return cwret; #else /* TODO: Tone queue may have e.g. 10 tones enqueued at this moment. To have a "gentle" silencing of generator, we shouldn't interrupt in the middle of current tone. What we should do is: - remove all tones ahead of current tone (perhaps starting from the end). - see how long the current tone is. If it's relatively short, let it play to the end, but if it's long tone, interrupt it. - see if the current tone is "forever" tone. If it is, end the "forever" tone using method suitable for such tone. - only after all this you can enqueue a tone silent that will ensure that a key is not in "down" state. What if the last tone on queue is a Very Long Tone, and silencing the generator will take a long time? */ /* Somewhere there may be a key in "down" state and we need to make it go "up", regardless of sound sink (even for CW_AUDIO_NULL, because that sound system can also be used with a key). Otherwise the key may stay in "down" state forever and the sound will be played after "silencing" of the generator. */ cw_tone_t tone; CW_TONE_INIT(&tone, 0, gen->quantum_duration, CW_SLOPE_MODE_NO_SLOPES); cw_ret_t status = cw_tq_enqueue_internal(gen->tq, &tone); if (gen->sound_system == CW_AUDIO_NULL || gen->sound_system == CW_AUDIO_OSS || gen->sound_system == CW_AUDIO_ALSA || gen->sound_system == CW_AUDIO_PA) { /* Allow some time for playing the last tone. */ usleep(2 * tone.duration); } else if (gen->sound_system == CW_AUDIO_CONSOLE) { /* Sine wave generation should have been stopped by a code generating dots/dashes, but just in case... TODO: is it still necessary after adding the quantum of silence above? */ cw_console_silence_internal(gen); } else { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_ERROR, MSG_PREFIX "called silence() function for generator without sound system specified"); } if (gen->sound_system == CW_AUDIO_ALSA) { /* "Stop a PCM dropping pending frames. " */ cw_debug_msg (&cw_debug_object, CW_DEBUG_GENERATOR, CW_DEBUG_INFO, MSG_PREFIX "asking ALSA to drop frames on silence()"); cw_alsa_drop_internal(gen); } /* TODO: we just want to silence the generator, right? So we don't stop it. This line of code has been disabled some time before 2017-01-26. */ //gen->do_dequeue_and_generate = false; return status; #endif } cw_gen_t * cw_gen_new(const cw_gen_config_t * gen_conf) { #ifdef LIBCW_WITH_DEV fprintf(stderr, "libcw build %s %s\n", __DATE__, __TIME__); #endif cw_assert (gen_conf->sound_system != CW_AUDIO_NONE, MSG_PREFIX "can't create generator with sound system '%s'", cw_get_audio_system_label(gen_conf->sound_system)); cw_gen_t * gen = (cw_gen_t *) calloc(1, sizeof (cw_gen_t)); if (NULL == gen) { cw_debug_msg (&cw_debug_object, CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "calloc()"); return (cw_gen_t *) NULL; } /* Tone queue. */ { gen->tq = cw_tq_new_internal(); if (NULL == gen->tq) { cw_gen_delete(&gen); return (cw_gen_t *) NULL; } else { /* Sometimes tq needs to access a key associated with generator. */ gen->tq->gen = gen; } } /* Parameters. */ { /* Generator's basic parameters. */ gen->send_speed = CW_SPEED_INITIAL; gen->frequency = CW_FREQUENCY_INITIAL; gen->volume_percent = CW_VOLUME_INITIAL; gen->volume_abs = (gen->volume_percent * CW_AUDIO_VOLUME_RANGE) / 100; gen->gap = CW_GAP_INITIAL; gen->weighting = CW_WEIGHTING_INITIAL; /* Generator's timing parameters. */ gen->dot_duration = 0; gen->dash_duration = 0; gen->ims_duration = 0; gen->ics_duration = 0; gen->iws_duration = 0; gen->additional_space_duration = 0; gen->adjustment_space_duration = 0; /* Generator's misc parameters. */ gen->quantum_duration = CW_AUDIO_QUANTUM_DURATION_INITIAL; gen->parameters_in_sync = false; } /* Misc fields. */ { /* Sound buffer and related items. */ gen->buffer = NULL; gen->buffer_n_samples = -1; gen->buffer_sub_start = 0; gen->buffer_sub_stop = 0; gen->sample_rate = 0; gen->phase_offset = -1; /* Tone parameters. */ gen->tone_slope.duration = CW_AUDIO_SLOPE_DURATION; gen->tone_slope.shape = CW_TONE_SLOPE_SHAPE_RAISED_COSINE; gen->tone_slope.amplitudes = NULL; gen->tone_slope.n_amplitudes = 0; /* Library's client. */ #ifdef GENERATOR_CLIENT_THREAD gen->library_client.thread_id = -1; #endif gen->library_client.name = (char *) NULL; /* CW key associated with this generator. */ gen->key = (cw_key_t *) NULL; } /* pthread */ { gen->thread.id = (pthread_t) -1; /* FIXME: thread id type is opaque. Don't assign -1. */ pthread_attr_init(&gen->thread.attr); /* Thread must be joinable in order to make a safe call to pthread_kill(thread_id, 0). pthreads are joinable by default, but I take this explicit call as a good opportunity to make this comment. */ pthread_attr_setdetachstate(&gen->thread.attr, PTHREAD_CREATE_JOINABLE); gen->thread.running = false; /* TODO: doesn't this duplicate gen->thread.running flag? */ gen->do_dequeue_and_generate = false; } /* Sound system. */ { /* gen->sound_system = sound_system; */ /* We handle this field below. */ gen->dev_raw_sink = -1; /* Sound system - console. */ gen->console.sound_sink_fd = -1; gen->console.cw_value = CW_KEY_VALUE_OPEN; /* Sound system - OSS. */ #ifdef LIBCW_WITH_OSS gen->oss_data.sound_sink_fd = -1; gen->oss_data.version.x = 0; gen->oss_data.version.y = 0; gen->oss_data.version.z = 0; #endif /* Sound system - ALSA. */ #ifdef LIBCW_WITH_ALSA gen->alsa_data.pcm_handle = NULL; #endif /* Sound system - PulseAudio. */ #ifdef LIBCW_WITH_PULSEAUDIO gen->pa_data.simple = NULL; #endif cw_ret_t cwret = cw_gen_new_open_internal(gen, gen_conf); if (cwret == CW_FAILURE) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_SOUND_SYSTEM, CW_DEBUG_ERROR, MSG_PREFIX "failed to open sound sink for sound system '%s' and device '%s'", cw_get_audio_system_label(gen_conf->sound_system), gen_conf->sound_device); cw_gen_delete(&gen); return (cw_gen_t *) NULL; } if (gen_conf->sound_system == CW_AUDIO_NULL || gen_conf->sound_system == CW_AUDIO_CONSOLE) { ; /* The two types of sound output don't require audio buffer. */ } else { gen->buffer = (cw_sample_t *) calloc(gen->buffer_n_samples, sizeof (cw_sample_t)); if (!gen->buffer) { cw_debug_msg (&cw_debug_object, CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "calloc()"); cw_gen_delete(&gen); return (cw_gen_t *) NULL; } } /* Set slope that late, because it uses value of sample rate. The sample rate value is set in cw_gen_new_open_internal(). */ cwret = cw_gen_set_tone_slope(gen, CW_TONE_SLOPE_SHAPE_RAISED_COSINE, CW_AUDIO_SLOPE_DURATION); if (cwret == CW_FAILURE) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_ERROR, MSG_PREFIX "failed to set slope"); cw_gen_delete(&gen); return (cw_gen_t *) NULL; } } /* Tracking of generator's value. */ { gen->value_tracking.value = CW_KEY_VALUE_OPEN; gen->value_tracking.value_tracking_callback_func = NULL; gen->value_tracking.value_tracking_callback_arg = NULL; } #if 0 /* Part of old inter-thread comm. Disabled on 2020-09-01. */ cw_sigalrm_install_top_level_handler_internal(); #endif return gen; } void cw_gen_delete(cw_gen_t **gen) { cw_assert (NULL != gen, MSG_PREFIX "generator is NULL"); if (NULL == *gen) { return; } if ((*gen)->do_dequeue_and_generate) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_DEBUG, MSG_PREFIX "you forgot to call cw_gen_stop()"); cw_gen_stop(*gen); } /* Wait for "write" thread to end accessing output file descriptor. I have come up with value 500 after doing some experiments. FIXME: magic number. I think that we can come up with algorithm for calculating the value. */ usleep(500); free((*gen)->buffer); (*gen)->buffer = NULL; if ((*gen)->close_sound_device) { (*gen)->close_sound_device(*gen); } else { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_WARNING, MSG_PREFIX "WARNING: 'close' function pointer is NULL, something went wrong"); } pthread_attr_destroy(&(*gen)->thread.attr); free((*gen)->library_client.name); (*gen)->library_client.name = NULL; free((*gen)->tone_slope.amplitudes); (*gen)->tone_slope.amplitudes = NULL; cw_tq_delete_internal(&(*gen)->tq); (*gen)->sound_system = CW_AUDIO_NONE; free(*gen); *gen = NULL; return; } cw_ret_t cw_gen_stop(cw_gen_t * gen) { if (NULL == gen) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_WARNING, MSG_PREFIX "called the function for NULL generator"); /* Not really a runtime error, so return CW_SUCCESS. */ return CW_SUCCESS; } /* FIXME: Something goes wrong when cw_gen_stop() is called from signal handler. pthread_cond_destroy() hangs because there is an interrupted pthread_cond_wait() in frame #8. Signalling it won't help because even if a condition variable is signalled, the function won't be able to continue. Stopping of generator, especially in emergency situations, needs to be re-thought. This is probably fixed by not calling pthread_cond_destroy() in cw_tq_delete_internal(). */ /* #0 __pthread_cond_destroy (cond=0x1b130f0) at pthread_cond_destroy.c:77 #1 0x00007f15b393179d in cw_tq_delete_internal (tq=0x1b13118) at libcw_tq.c:219 #2 0x00007f15b392e2ca in cw_gen_delete (gen=0x1b13118, gen@entry=0x6069e0 ) at libcw_gen.c:608 #3 0x000000000040207f in cw_atexit () at cw.c:668 #4 0x00007f15b35b6bc9 in __run_exit_handlers (status=status@entry=0, listp=0x7f15b39225a8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #5 0x00007f15b35b6c15 in __GI_exit (status=status@entry=0) at exit.c:104 #6 0x00000000004020d7 in signal_handler (signal_number=2) at cw.c:686 #7 #8 pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185 #9 0x00007f15b3931f3b in cw_tq_wait_for_level_internal (tq=0x1af5be0, level=level@entry=1) at libcw_tq.c:964 #10 0x00007f15b392f938 in cw_gen_wait_for_queue_level (gen=, level=level@entry=1) at libcw_gen.c:2701 #11 0x000000000040241e in send_cw_character (c=c@entry=102, is_partial=is_partial@entry=0) at cw.c:501 #12 0x0000000000401d3d in parse_stream (stream=0x7f15b39234e0 <_IO_2_1_stdin_>) at cw.c:538 #13 main (argc=, argv=) at cw.c:652 */ cw_tq_flush_internal(gen->tq); if (CW_SUCCESS != cw_gen_silence_internal(gen)) { return CW_FAILURE; } cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_INFO, MSG_PREFIX "setting gen->do_dequeue_and_generate to false"); gen->do_dequeue_and_generate = false; if (!gen->thread.running) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_INFO, MSG_PREFIX "EXIT: seems that thread function was not started at all"); /* Don't call pthread_kill() on non-initialized thread.id. The generator wasn't even started, so let's return CW_SUCCESS. */ /* TODO: what about code that doesn't use signals? Should we return here? */ return CW_SUCCESS; } /* "while (gen->do_dequeue_and_generate)" loop in thread function may be in a state where dequeue() function returned IDLE state, and the loop is waiting for new tone. This is to force the loop to start new cycle, make the loop notice that gen->do_dequeue_and_generate is false, and to get the thread function to return (and thus to end the thread). */ pthread_mutex_lock(&gen->tq->wait_mutex); pthread_cond_broadcast(&gen->tq->wait_var); pthread_mutex_unlock(&gen->tq->wait_mutex); #if 0 /* Original implementation using signals. */ /* This was disabled some time before 2017-01-19. */ pthread_kill(gen->thread.id, SIGALRM); #endif /* TODO: there is something wrong with the keyer machine state for iambic keyer if we have to reset it here (I'm resetting straight key too, just for completeness). Replication scenario: 1. comment "#define LIBCW_KEY_TESTS_WORKAROUND" in libcw_legacy_api_tests.c 2. comment these two function calls 3. run './src/libcw/tests/libcw_tests -S a -X plughw -A k' 4. see that 'legacy_api_test_iambic_key_dash' test hangs */ if (gen->key) { cw_key_ik_reset_state_internal(gen->key); cw_key_sk_reset_state_internal(gen->key); } return cw_gen_join_thread_internal(gen); } /** @brief Wrapper for pthread_join() and debug code @internal @reviewed 2020-08-04 @endinternal @param[in] gen generator for which to join its thread @return CW_SUCCESS if joining succeeded @return CW_FAILURE otherwise */ cw_ret_t cw_gen_join_thread_internal(cw_gen_t * gen) { /* TODO: this comment may no longer be true and necessary. Sleep a bit to postpone closing a device. This way we can avoid a situation when "do_dequeue_and_generate" is set to false and device is being closed while a new buffer is being prepared, and while write() tries to write this new buffer to already closed device. Without this sleep(), writei() from ALSA library may return "File descriptor in bad state" error - this happened when writei() tried to write to closed ALSA handle. The delay also allows the generator function thread to stop generating tone (or for tone queue to get out of CW_TQ_EMPTY state (TODO: verify this comment, does it describe correct tq state?) and exit before we resort to killing generator function thread. */ cw_usleep_internal(1 * CW_USECS_PER_SEC); #if LIBCW_GEN_DEBUG_THREAD_TIMING /* Debug code to measure how long it takes to join threads. */ struct timeval before; struct timeval after; gettimeofday(&before, NULL); #endif int rv = pthread_join(gen->thread.id, NULL); #if LIBCW_GEN_DEBUG_THREAD_TIMING /* Debug code to measure how long it takes to join threads. */ gettimeofday(&after, NULL); const int delta = cw_timestamp_compare_internal(&before, &after); cw_debug_msg (&cw_debug_object, CW_DEBUG_GENERATOR, CW_DEBUG_INFO, MSG_PREFIX "generator thread timing: joining thread took %d us", delta); #endif if (rv == 0) { gen->thread.running = false; return CW_SUCCESS; } else { cw_debug_msg (&cw_debug_object, CW_DEBUG_GENERATOR, CW_DEBUG_ERROR, MSG_PREFIX "failed to join threads: '%s'", strerror(rv)); return CW_FAILURE; } } /** @brief Open sound system A wrapper for code trying to open sound device specified by @p sound_system. Open sound system will be assigned to given generator. Caller can also specify sound device to use instead of a default one. If @p device_name is NULL or empty string, library-default device will be used. @internal @reviewed 2020-11-14 @endinternal @param[in] gen generator for which to open a sound system @param[in] gen_conf @return CW_SUCCESS on success @return CW_FAILURE otherwise */ cw_ret_t cw_gen_new_open_internal(cw_gen_t * gen, const cw_gen_config_t * gen_conf) { /* FIXME: this functionality is partially duplicated in src/cwutils/cw_common.c/cw_gen_new_from_config() */ /* This function deliberately checks all possible values of sound system name in separate 'if' clauses before it gives up and returns CW_FAILURE. PA/OSS/ALSA are combined with SOUNDCARD, so I have to check all three of them (because @p sound_system may be set to SOUNDCARD). And since I check the three in separate 'if' clauses, I can check all other values of sound system as well. */ if (gen_conf->sound_system == CW_AUDIO_NULL) { if (cw_is_null_possible(gen_conf->sound_device)) { cw_null_init_gen_internal(gen); return gen->open_and_configure_sound_device(gen, gen_conf); } } if (gen_conf->sound_system == CW_AUDIO_PA || gen_conf->sound_system == CW_AUDIO_SOUNDCARD) { if (cw_is_pa_possible(gen_conf->sound_device)) { cw_pa_init_gen_internal(gen); return gen->open_and_configure_sound_device(gen, gen_conf); } } if (gen_conf->sound_system == CW_AUDIO_OSS || gen_conf->sound_system == CW_AUDIO_SOUNDCARD) { if (cw_is_oss_possible(gen_conf->sound_device)) { cw_oss_init_gen_internal(gen); return gen->open_and_configure_sound_device(gen, gen_conf); } } if (gen_conf->sound_system == CW_AUDIO_ALSA || gen_conf->sound_system == CW_AUDIO_SOUNDCARD) { if (cw_is_alsa_possible(gen_conf->sound_device)) { cw_alsa_init_gen_internal(gen); return gen->open_and_configure_sound_device(gen, gen_conf); } } if (gen_conf->sound_system == CW_AUDIO_CONSOLE) { if (cw_is_console_possible(gen_conf->sound_device)) { cw_console_init_gen_internal(gen); return gen->open_and_configure_sound_device(gen, gen_conf); } } /* There is no next sound system type to try. */ return CW_FAILURE; } /** @brief Dequeue tones and push them to sound output This is a thread function. Function dequeues tones from tone queue associated with generator and then sends them to preconfigured sound output (soundcard, NULL or console). Function dequeues tones (or waits for new tones in queue) and pushes them to sound output as long as generator->do_dequeue_and_generate is true. The generator must be fully configured before creating thread with this function. @internal @reviewed 2020-10-17 @endinternal @param[in] arg generator (cast to (void *)) to be used for generating tones @return NULL pointer */ void * cw_gen_dequeue_and_generate_internal(void * arg) { cw_gen_t * gen = (cw_gen_t *) arg; const char name_prefix[] = "deq "; char name[sizeof (name_prefix) + sizeof (gen->label)] = { 0 }; snprintf(name, sizeof (name), "%s%s\n", name_prefix, gen->label); name[15] = '\0'; #if defined(__linux__) /* Choosing prctl() over pthread_setname_np() for Linux because prctl doesn't require explicit "#define _GNU_SOURCE". */ prctl(PR_SET_NAME, name, 0, 0, 0); #elif defined(__FreeBSD__) pthread_set_name_np(pthread_self(), name); #endif /* Tone dequeued in previous call to cw_tq_dequeue_internal(). */ cw_tone_t prev_tone = { 0 }; /* Tone dequeued in current call to cw_tq_dequeue_internal(). */ cw_tone_t tone; CW_TONE_INIT(&tone, 0, 0, CW_SLOPE_MODE_STANDARD_SLOPES); while (gen->do_dequeue_and_generate) { const cw_queue_state_t queue_state = cw_tq_dequeue_internal(gen->tq, &tone); if (CW_TQ_EMPTY == queue_state) { cw_debug_msg (&cw_debug_object, CW_DEBUG_GENERATOR, CW_DEBUG_INFO, MSG_PREFIX "Detected empty queue"); cw_gen_value_tracking_internal(gen, &tone, queue_state); #if 1 if (gen->on_empty_queue) { if (CW_SUCCESS != gen->on_empty_queue(gen)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_TONE_QUEUE, CW_DEBUG_ERROR, MSG_PREFIX "handling of empty queue by generator has failed"); } } #endif /* We won't get here while there are some accumulated tones in queue, because cw_tq_dequeue_internal() will be handling them just fine without any need for synchronization or wait(). Only after the queue has been completely drained, we will be forced to wait() here. It's much better to wait only sometimes after cw_tq_dequeue_internal() than wait always before cw_tq_dequeue_internal(). We are waiting for kick from enqueue() function informing that a new tone appeared in tone queue. The kick may also come from cw_gen_stop() that gently asks this function to stop idling and nicely return. */ /* The 'while' loop handles spurious wakeups of pthread_cond_wait() and also ensures that the wait() function is called only when a wait is necessary. TODO: make sure that getting gen->tq->state doesn't require locking a tq mutex. */ pthread_mutex_lock(&(gen->tq->wait_mutex)); while (CW_TQ_EMPTY == gen->tq->state && gen->do_dequeue_and_generate) { pthread_cond_wait(&gen->tq->wait_var, &gen->tq->wait_mutex); } pthread_mutex_unlock(&(gen->tq->wait_mutex)); #if 0 /* Original implementation using signals. */ /* This code has been disabled some time before 2017-01-19. */ /* TODO: can we / should we specify on which signal exactly we are waiting for? */ cw_signal_wait_internal(); #endif continue; } const bool is_empty_tone = CW_TQ_EMPTY == queue_state; cw_gen_value_tracking_internal(gen, &tone, queue_state); #ifdef IAMBIC_KEY_HAS_TIMER /* Also look at call to cw_key_ik_update_graph_state_internal() made below. Both calls are about updating some internals of key. This one is done before blocking write, the other is done after blocking write. */ if (gen->key) { cw_key_ik_increment_timer_internal(gen->key, tone.duration); } #endif #ifdef LIBCW_WITH_DEV cw_debug_ev (&cw_debug_object_ev, 0, tone.frequency ? CW_DEBUG_EVENT_TONE_HIGH : CW_DEBUG_EVENT_TONE_LOW); #endif /* This is a blocking write. */ if (gen->sound_system == CW_AUDIO_NULL || gen->sound_system == CW_AUDIO_CONSOLE) { cw_assert (NULL != gen->write_tone_to_sound_device, "'gen->write_tone_to_sound_device' pointer is NULL"); gen->write_tone_to_sound_device(gen, &tone); } else { if (gen->silencing_initialized) { /* Don't play a tone that has been just dequeued. Instead prepare a tone that will silence current source sink. Use current tone ('tone' variable) as starting point/basis for this silencing tone. */ CW_TONE_COPY(&tone, &prev_tone); cw_gen_silencing_tone_calculate_samples_size_internal(gen, &tone); } else if (is_empty_tone) { /* No valid tone dequeued from tone queue. 'tone' argument doesn't represent a valid tone. We need samples to complete filling buffer, but they have to be empty samples. */ cw_gen_empty_tone_calculate_samples_size_internal(gen, &tone); } else { /* Valid tone dequeued from tone queue and nothing prohibits us from playing it (we aren't in 'silencing' phase). Use the tone to calculate samples in buffer. */ cw_gen_tone_calculate_samples_size_internal(gen, &tone); } cw_gen_write_to_soundcard_internal(gen, &tone); } if (prev_tone.is_forever && tone.is_forever) { /* Don't notify about dequeueing two consecutive 'forever' tones. For any listener this is still the same tone. TODO: make the check more precise. What if first 'forever' tone is silent and the next is non-silent, or if they have two different frequencies? */ ; /* NOOP */ } else { #ifdef GENERATOR_CLIENT_THREAD fprintf(stderr, MSG_PREFIX " sending signal on dequeue, target thread id = %ld\n", gen->library_client.thread_id); #endif pthread_mutex_lock(&gen->tq->wait_mutex); pthread_cond_broadcast(&gen->tq->wait_var); pthread_mutex_unlock(&gen->tq->wait_mutex); } #ifdef GENERATOR_CLIENT_THREAD /* Original implementation using signals. */ /* This code has been disabled some time before 2017-01-19. */ /* When sending text from text input, the signal: - allows client code to observe moment when state of tone queue is "low/critical"; client code then can add more characters to the queue; the observation is done using cw_tq_wait_for_level_internal(); - allows client code to observe any dequeue event by waiting for signal in cw_tq_wait_for_end_of_current_tone_internal(); */ pthread_kill(gen->library_client.thread_id, SIGALRM); #endif /* Generator may be used by iambic keyer to measure periods of time (durations of Mark and Space). This is achieved by enqueueing Marks and Spaces by keyer in generator. A soundcard playing samples is surprisingly good at measuring time intervals. At this point the generator has finished generating a tone of specified duration. A duration of Mark or Space has elapsed. Inform iambic keyer that the tone it has enqueued has elapsed. The keyer may want to change state of its internal state machine. (Whether iambic keyer has enqueued any tones or not, and whether it is waiting for the notification, is a different story. We will let the iambic keyer function called below to decide what to do with the notification. If keyer is in idle state, it will ignore the notification.) Notice that this mechanism is needed only for iambic keyer. Inner workings of straight key are much more simple, the straight key doesn't need to use generator as a timer. */ if (CW_FAILURE == cw_key_ik_update_graph_state_internal(gen->key)) { /* just try again, once */ usleep(1000); cw_key_ik_update_graph_state_internal(gen->key); } if (gen->silencing_initialized) { /* We are in silencing phase. A last tone (silencing tone) has been played, and we shouldn't play anything else. Discard tones remaining in queue. Remember that we are in silencing phase, which may or may not mean that generator is being stopped and deleted. */ cw_tq_flush_internal(gen->tq); gen->silencing_initialized = false; } #ifdef LIBCW_WITH_DEV cw_debug_ev (&cw_debug_object_ev, 0, tone.frequency ? CW_DEBUG_EVENT_TONE_LOW : CW_DEBUG_EVENT_TONE_HIGH); #endif /* And finally, at the very end... */ CW_TONE_COPY(&prev_tone, &tone); } /* while (gen->do_dequeue_and_generate) */ cw_debug_msg (&cw_debug_object, CW_DEBUG_GENERATOR, CW_DEBUG_INFO, MSG_PREFIX "EXIT: generator stopped (gen->do_dequeue_and_generate = %d)", gen->do_dequeue_and_generate); /* Some functions in main thread may be waiting for the last notification from the generator thread to continue/finalize their business. Let's send that notification right before exiting. */ /* This small delay before sending the notification turns out to be helpful. TODO: this is one of most mysterious comments in this code base. What was I thinking? */ cw_usleep_internal(CW_USECS_PER_SEC / 2); pthread_mutex_lock(&gen->tq->wait_mutex); /* There may be many listeners, so use broadcast(). */ pthread_cond_broadcast(&gen->tq->wait_var); pthread_mutex_unlock(&gen->tq->wait_mutex); #ifdef GENERATOR_CLIENT_THREAD /* Original implementation using signals. */ /* This code has been disabled some time before 2017-01-19. */ pthread_kill(gen->library_client.thread_id, SIGALRM); #endif gen->thread.running = false; return NULL; } /** @brief Calculate a fragment of sine wave Calculate a fragment of sine wave, as many samples as can be fitted in generator buffer's subarea. The function calculates values of (gen->buffer_sub_stop - gen->buffer_sub_start + 1) samples and puts them into gen->buffer[], starting from gen->buffer[gen->buffer_sub_start]. The function takes into account all state variables from gen, so initial phase of new fragment of sine wave in the buffer matches ending phase of a sine wave generated in previous call. @internal @reviewed 2020-08-04 @endinternal @param[in] gen generator that generates sine wave @param[in,out] tone specification of samples that should be calculated @return number of calculated samples */ int cw_gen_calculate_sine_wave_internal(cw_gen_t * gen, cw_tone_t * tone) { assert (gen->buffer_sub_stop <= gen->buffer_n_samples); /* We need two separate iterators to correctly generate sine wave: -- i -- for iterating through output buffer (generator buffer's subarea), it can travel between buffer cells delimited by start and stop (inclusive); -- t -- for calculating phase of a sine wave; 't' always has to start from zero for every calculated subarea (i.e. for every call of this function); Initial/starting phase of generated fragment is always retained in gen->phase_offset, it is the only "memory" of previously calculated fragment of sine wave (to be precise: it stores phase of last sample in previously calculated fragment). Therefore iterator used to calculate phase of sine wave can't have the memory too. Therefore it has to always start from zero for every new fragment of sine wave. Therefore a separate t. */ float phase = 0.0F; int t = 0; for (int i = gen->buffer_sub_start; i <= gen->buffer_sub_stop; i++) { phase = (2.0F * CW_PI * (float) (tone->frequency * t) / (float) gen->sample_rate) + gen->phase_offset; const int amplitude = cw_gen_calculate_sample_amplitude_internal(gen, tone); gen->buffer[i] = ((float) amplitude) * sinf(phase); tone->sample_iterator++; t++; } phase = (2.0F * CW_PI * (float) (tone->frequency * t) / (float) gen->sample_rate) + gen->phase_offset; /* "phase" is now phase of the first sample in next fragment to be calculated. However, for long fragments this can be a large value, well beyond <0; 2*Pi) range. The value of phase may further accumulate in different calculations, and at some point it may overflow. This would result in an audible click. Let's bring back the phase from beyond <0; 2*Pi) range into the <0; 2*Pi) range, in other words lets "normalize" it. Or, in yet other words, lets apply modulo operation to the phase. The normalized phase will be used as a phase offset for next fragment (during next function call). It will be added phase of every sample calculated in next function call. */ /* TODO: check if n_periods can be a float. We could avoid the casts. */ const int n_periods = (int) floorf(phase / (2.0F * CW_PI)); gen->phase_offset = phase - (float) n_periods * 2.0F * CW_PI; return t; } /** @brief Calculate value of a single sample of sine wave This function calculates an amplitude (a value) of a single sample in sine wave PCM data. Actually "calculation" is a bit too big word. The function just makes a decision which of precalculated values to return. There are no complicated arithmetical calculations being made each time the function is called, so the execution time should be pretty small. The precalcuated values depend on some factors, so the values should be re-calculated each time these factors change. See cw_gen_set_tone_slope() for list of these factors. A generator contains some of information needed to get an amplitude of every sample in a sine wave - this is why this function needs @p gen. If tone's slopes are non-rectangular, the duration of slopes is defined in generator. If a tone is non-silent, the volume is also defined in generator. However, decision tree for getting the amplitude also depends on some parameters that are strictly bound to tone, such as what is the shape of slopes for a given tone - this is why we have @p tone. The @p tone also stores iterator of samples - this is how we know for which sample in a tone to calculate the amplitude. @internal @reviewed 2020-08-05 @endinternal @param[in] gen generator used to generate a sine wave @param[in] tone tone being generated @return value of a sample of sine wave, a non-negative number */ int cw_gen_calculate_sample_amplitude_internal(cw_gen_t * gen, const cw_tone_t * tone) { #if 0 /* Blunt algorithm for calculating amplitude. For debug purposes only. */ return tone->frequency ? gen->volume_abs : 0; #else if (tone->frequency <= 0) { return 0; } float amplitude = 0.0F; /* Every tone, regardless of slope mode (CW_SLOPE_MODE_*), has three components. It has rising slope + plateau + falling slope. There can be four variants of rising and falling slope length, just as there are four CW_SLOPE_MODE_* values. There can be also tones with zero-length plateau, and there can be also tones with zero-length slopes. */ if (tone->sample_iterator < tone->rising_slope_n_samples) { /* Beginning of tone, rising slope. */ const int i = tone->sample_iterator; amplitude = gen->tone_slope.amplitudes[i]; assert (amplitude >= 0); } else if (tone->sample_iterator >= tone->rising_slope_n_samples && tone->sample_iterator < tone->n_samples - tone->falling_slope_n_samples) { /* Middle of tone, plateau, constant amplitude. */ amplitude = (float) gen->volume_abs; assert (amplitude >= 0); } else if (tone->sample_iterator >= tone->n_samples - tone->falling_slope_n_samples) { /* Falling slope. */ const cw_sample_iter_t i = tone->n_samples - tone->sample_iterator - 1; assert (i >= 0); amplitude = gen->tone_slope.amplitudes[i]; assert (amplitude >= 0); } else { cw_assert (0, MSG_PREFIX "->sample_iterator out of bounds:\n" "tone->sample_iterator: %ld\n" "tone->n_samples: %"PRId64"\n" "tone->rising_slope_n_samples: %d\n" "tone->falling_slope_n_samples: %d\n", tone->sample_iterator, tone->n_samples, tone->rising_slope_n_samples, tone->falling_slope_n_samples); } assert (amplitude >= 0.0F); return (int) amplitude; #endif } /** @brief Set parameters describing slopes of tones generated by generator Most of variables related to slope of tones is in @p tone, but there are still some variables that are generator-specific, as they are common for all tones. This function sets two of these generator-specific variables. A: If you pass to function conflicting values of @p slope_shape and @p slope_duration, the function will return CW_FAILURE. These conflicting values are rectangular slope shape and larger than zero slope length. You just can't have rectangular slopes that have non-zero length. B: If you pass to function '\-1' as value of both @p slope_shape and @p slope_duration, the function won't change any of the related two generator's parameters. C1: If you pass to function '\-1' as value of either @p slope_shape or @p slope_duration, the function will attempt to set only this generator's parameter that is different than '\-1'. C2: However, if selected slope shape is rectangular, function will set generator's slope length to zero, even if value of \p slope_duration is '\-1'. D: Notice that the function allows non-rectangular slope shape with zero length of the slopes. The slopes will be non-rectangular, but just unusually short. @internal TODO: Seriously, these rules (A-D) for setting a slope are too complicated. Simplify them. Accept only a small subset of valid/sane values. Perhaps split the function into two separate functions: for setting slope shape and slope duration. @endinternal The function should be called every time one of following parameters change: @li shape of slope, @li duration of slope, @li generator's sample rate, @li generator's volume. There are four supported shapes of slopes: @li linear (the only one supported by libcw until version 4.1.1), @li raised cosine (supposedly the most desired shape), @li sine, @li rectangular. Use CW_TONE_SLOPE_SHAPE_* symbolic names as values of @p slope_shape. FIXME: first argument of this public function is gen, but no function provides access to generator variable. @internal @reviewed 2020-08-05 @endinternal @param[in] gen generator for which to set tone slope parameters @param[in] slope_shape shape of slope: linear, raised cosine, sine, rectangular @param[in] slope_duration duration of slope [microseconds] @return CW_SUCCESS on success @return CW_FAILURE on failure */ int cw_generator_set_tone_slope(cw_gen_t * gen, int slope_shape, int slope_duration) { return cw_gen_set_tone_slope(gen, slope_shape, slope_duration); } /** See comment for cw_generator_set_tone_slope() @internal @reviewed 2020-08-05 @endinternal */ cw_ret_t cw_gen_set_tone_slope(cw_gen_t * gen, int slope_shape, int slope_duration) { assert (gen); /* Handle conflicting values of arguments. */ if (slope_shape == CW_TONE_SLOPE_SHAPE_RECTANGULAR && slope_duration > 0) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_ERROR, MSG_PREFIX "requested a rectangular slope shape, but also requested slope duration > 0"); return CW_FAILURE; } /* Assign new values from arguments. */ if (slope_shape != -1) { gen->tone_slope.shape = slope_shape; } if (slope_duration != -1) { gen->tone_slope.duration = slope_duration; } /* Override of slope duration. */ if (slope_shape == CW_TONE_SLOPE_SHAPE_RECTANGULAR) { /* TODO: what's going on here? Why do we set this to zero? */ gen->tone_slope.duration = 0; } int slope_n_samples = ((gen->sample_rate / 100) * gen->tone_slope.duration) / 10000; cw_assert (slope_n_samples >= 0, MSG_PREFIX "negative slope_n_samples: %d", slope_n_samples); /* Reallocate the table of slope amplitudes only when necessary. In practice the function will be called foremost when user changes volume of tone, and then the function may be called several times in a row if volume is changed in steps. In such situation the size of amplitudes table doesn't change. TODO: do we really need to change type/duration of slopes when volume changes? Perhaps a call to cw_gen_recalculate_slope_amplitudes_internal() would be enough? */ if (gen->tone_slope.n_amplitudes != slope_n_samples) { /* Remember that slope_n_samples may be zero. In that case realloc() would equal to free(). We don't want to have NULL ->amplitudes, so don't modify ->amplitudes for zero-duration slopes. Since with zero-duration slopes we won't be referring to ->amplitudes[], it is ok that the table will not be up-to-date. */ if (slope_n_samples > 0) { gen->tone_slope.amplitudes = realloc(gen->tone_slope.amplitudes, sizeof(float) * slope_n_samples); if (!gen->tone_slope.amplitudes) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_ERROR, MSG_PREFIX "failed to realloc() table of slope amplitudes"); return CW_FAILURE; } } gen->tone_slope.n_amplitudes = slope_n_samples; } cw_gen_recalculate_slope_amplitudes_internal(gen); return CW_SUCCESS; } /** @brief Recalculate amplitudes of PCM samples that form tone's slopes @internal @reviewed 2020-08-05 @endinternal TODO: consider writing unit test code for the function. @param[in] gen generator */ void cw_gen_recalculate_slope_amplitudes_internal(cw_gen_t * gen) { /* The values in amplitudes[] change from zero to max (at least for any sane slope shape), so naturally they can be used in forming rising slope. However they can be used in forming falling slope as well - just iterate the table from end to beginning. */ for (int i = 0; i < gen->tone_slope.n_amplitudes; i++) { if (gen->tone_slope.shape == CW_TONE_SLOPE_SHAPE_LINEAR) { gen->tone_slope.amplitudes[i] = (float) (i * gen->volume_abs) / (float) gen->tone_slope.n_amplitudes; } else if (gen->tone_slope.shape == CW_TONE_SLOPE_SHAPE_SINE) { const float radian = (float) i * (CW_PI / 2.0F) / (float) gen->tone_slope.n_amplitudes; const float y = sinf(radian); gen->tone_slope.amplitudes[i] = y * (float) gen->volume_abs; } else if (gen->tone_slope.shape == CW_TONE_SLOPE_SHAPE_RAISED_COSINE) { const float radian = (float) i * CW_PI / (float) gen->tone_slope.n_amplitudes; const float y = (1 - ((1 + cosf(radian)) / 2)); gen->tone_slope.amplitudes[i] = y * (float) gen->volume_abs; } else if (gen->tone_slope.shape == CW_TONE_SLOPE_SHAPE_RECTANGULAR) { /* CW_TONE_SLOPE_SHAPE_RECTANGULAR is covered before entering this "for" loop. */ /* TODO: to avoid treating CW_TONE_SLOPE_SHAPE_RECTANGULAR as special case, add the calculation here. */ cw_assert (0, MSG_PREFIX "we shouldn't be here, calculating rectangular slopes"); } else { cw_assert (0, MSG_PREFIX "unsupported slope shape %d", gen->tone_slope.shape); } } return; } /** @brief Write tone to soundcard @internal @reviewed 2020-10-13 @endinternal @param[in] gen @param[in] tone tone dequeued from queue (if dequeueing was successful); must always be non-NULL @return 0 */ int cw_gen_write_to_soundcard_internal(cw_gen_t * gen, cw_tone_t * tone) { cw_assert (NULL != tone, MSG_PREFIX "'tone' argument should always be non-NULL"); /* Total number of samples to write in a loop below. */ int64_t samples_to_write = tone->n_samples; #define LIBCW_WRITE_LOOP_DEBUG_LEVEL 0 #if LIBCW_WRITE_LOOP_DEBUG_LEVEL > 0 /* Debug code. */ int n_loops = 0; const float n_loops_expected = floorf(1.0 * samples_to_write / gen->buffer_n_samples); /* In reality number of loops executed is sometimes n_loops_expected, but mostly it's n_loops_expected+1. */ fprintf(stderr, MSG_PREFIX "entering loop (~%.1f iterations expected), tone->frequency = %d, buffer->n_samples = %d, samples_to_write = %"PRId64"\n", (double) n_loops_expected, tone->frequency, gen->buffer_n_samples, samples_to_write); #endif // cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_DEBUG, MSG_PREFIX "%lld samples, %d us, %d Hz", tone->n_samples, tone->duration, gen->frequency); while (samples_to_write > 0) { const int64_t free_space = gen->buffer_n_samples - gen->buffer_sub_start; if (samples_to_write > free_space) { /* There will be some tone samples left for next iteration of this loop. But buffer in this iteration will be ready to be pushed to sound sink. */ gen->buffer_sub_stop = gen->buffer_n_samples - 1; } else if (samples_to_write == free_space) { /* How nice, end of tone samples aligns with end of buffer (last sample of tone will be placed in last cell of buffer). But the result is the same - a full buffer ready to be pushed to sound sink. */ gen->buffer_sub_stop = gen->buffer_n_samples - 1; } else { /* There will be too few samples to fill a buffer. We can't send an under-filled buffer to sound sink. We will have to get more samples to fill the buffer completely. */ gen->buffer_sub_stop = gen->buffer_sub_start + samples_to_write - 1; } /* How many samples of sound buffer's subarea will be calculated in a given cycle of "calculate sine wave" code, i.e. in current iteration of the 'while' loop? */ const int buffer_sub_n_samples = gen->buffer_sub_stop - gen->buffer_sub_start + 1; #if LIBCW_WRITE_LOOP_DEBUG_LEVEL > 0 /* Debug code. */ ++n_loops; #if LIBCW_WRITE_LOOP_DEBUG_LEVEL > 1 /* Debug code. */ fprintf(stderr, MSG_PREFIX " loop #%d, buffer_sub_n_samples = %d\n", n_loops, buffer_sub_n_samples); cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_DEBUG, MSG_PREFIX "sub start: %d, sub stop: %d, sub size: %d / %"PRIu64, gen->buffer_sub_start, gen->buffer_sub_stop, buffer_sub_n_samples, samples_to_write); #endif #endif const int calculated = cw_gen_calculate_sine_wave_internal(gen, tone); cw_assert (calculated == buffer_sub_n_samples, MSG_PREFIX "calculated wrong number of samples: %d != %d", calculated, buffer_sub_n_samples); if (gen->buffer_sub_stop == gen->buffer_n_samples - 1) { /* We have a buffer full of samples. The buffer is ready to be pushed to sound sink. */ gen->write_buffer_to_sound_device(gen); #if CW_DEV_RAW_SINK cw_dev_debug_raw_sink_write_internal(gen); #endif gen->buffer_sub_start = 0; gen->buffer_sub_stop = 0; } else { /* #needmoresamples There is still some space left in the buffer, go fetch new tone from tone queue. */ gen->buffer_sub_start = gen->buffer_sub_stop + 1; cw_assert (gen->buffer_sub_start <= gen->buffer_n_samples - 1, MSG_PREFIX "sub start out of range: sub start = %d, buffer n samples = %d", gen->buffer_sub_start, gen->buffer_n_samples); } samples_to_write -= buffer_sub_n_samples; #if LIBCW_WRITE_LOOP_DEBUG_LEVEL > 0 /* Debug code. */ if (samples_to_write < 0) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_DEBUG, MSG_PREFIX "samples left = %"PRId64, samples_to_write); } #endif } /* while (samples_to_write > 0) { */ #if LIBCW_WRITE_LOOP_DEBUG_LEVEL > 0 /* Debug code. */ fprintf(stderr, MSG_PREFIX "left loop, %d iterations executed from %.1f iterations planned, samples left = %d\n", n_loops, (double) n_loops_expected, (int) samples_to_write); #endif return 0; } /** @brief Construct empty tone with correct/needed values of samples count The function sets values tone->..._n_samples fields of empty @p tone based on information from @p gen (i.e. looking on how many samples of silence need to be "created"). The sample count values are set in a way that allows filling remainder of generator's buffer with silence. After this point tone duration should not be used - it's the samples count that is correct. @internal @reviewed 2020-08-05 @endinternal @param[in] gen @param[in] tone tone for which to calculate samples count. */ void cw_gen_empty_tone_calculate_samples_size_internal(const cw_gen_t * gen, cw_tone_t * tone) { /* All tones have been already dequeued from tone queue. @p tone does not represent a valid tone to generate. At first sight there is no need to write anything to soundcard. But... It may happen that during previous call to the function there were too few samples in a tone to completely fill a buffer (see #needmoresamples tag below). We need to fill the buffer until it is full and ready to be sent to sound sink. Since there are no new tones for which we could generate samples, we need to generate silence samples. Padding the buffer with silence seems to be a good idea (it will work regardless of value (Mark/Space) of last valid tone). We just need to know how many samples of the silence to produce. Number of these samples will be stored in samples_to_write. */ /* We don't have a valid tone, so let's construct a fake one for purposes of padding. */ /* Required length of padding space is from end of last buffer subarea to end of buffer. */ tone->n_samples = gen->buffer_n_samples - (gen->buffer_sub_stop + 1);; tone->duration = 0; /* This value matters no more, because now we only deal with samples. */ tone->frequency = 0; /* This fake tone is a piece of silence. */ /* The silence tone used for padding doesn't require any slopes. A slope falling to silence has been already provided by last non-fake and non-silent tone. */ tone->slope_mode = CW_SLOPE_MODE_NO_SLOPES; tone->rising_slope_n_samples = 0; tone->falling_slope_n_samples = 0; /* This is part of initialization of tone. Zero samples from the tone have been calculated and put into generator's buffer. */ tone->sample_iterator = 0; //fprintf(stderr, "++++ count of samples in empty tone '%c' = %d\n", tone->debug_id, tone->n_samples); return; } /** @brief Calculate tone suitable for silencing a generator Calculate a @p tone variable that, when passed to generator's write() function, will silence a generator. @internal @reviewed 2020-10-13 @endinternal @param[in] gen @param[in] tone tone for which to calculate samples */ void cw_gen_silencing_tone_calculate_samples_size_internal(const cw_gen_t * gen, cw_tone_t * tone) { /* Make sure that we fill a buffer to the end (i.e. calculate as many samples as there are from current position in the buffer to the end of the buffer). Otherwise a buffer underrun may occur. */ tone->n_samples = gen->buffer_n_samples - (gen->buffer_sub_stop + 1); /* Also make sure that a tone is long enough for it to be heard (otherwise it may be perceived as click). */ tone->n_samples += (5 * gen->buffer_n_samples); tone->duration = 0; /* This value matters no more, because now we only deal with samples. */ /* Length of a single slope. */ cw_sample_iter_t slope_n_samples = gen->sample_rate / 100; slope_n_samples *= gen->tone_slope.duration; slope_n_samples /= 10000; switch (tone->slope_mode) { case CW_SLOPE_MODE_NO_SLOPES: case CW_SLOPE_MODE_RISING_SLOPE: /* Previous tone has ended with full level. Let the silencing tone fall from the full level to zero. */ tone->slope_mode = CW_SLOPE_MODE_FALLING_SLOPE; break; case CW_SLOPE_MODE_FALLING_SLOPE: case CW_SLOPE_MODE_STANDARD_SLOPES: /* Level of previous slope has already fallen to zero. Keep the level at zero. */ default: tone->slope_mode = CW_SLOPE_MODE_NO_SLOPES; tone->frequency = 0; break; } tone->rising_slope_n_samples = 0; tone->falling_slope_n_samples = slope_n_samples; /* This is part of initialization of tone. Zero samples from the tone have been calculated and put into generator's buffer. */ tone->sample_iterator = 0; //fprintf(stderr, "++++ count of samples in silencing tone '%c' = %d\n", tone->debug_id, tone->n_samples); return; } /** @brief Recalculate non-empty tone parameters from microseconds into samples The function sets tone->..._n_samples fields of non-empty @p tone based on other information from @p tone and from @p gen. After this point tone duration should not be used - it's the samples count that is correct. @internal @reviewed 2020-08-05 @endinternal @param[in] gen @param[in] tone tone for which to calculate samples count. */ void cw_gen_tone_calculate_samples_size_internal(const cw_gen_t * gen, cw_tone_t * tone) { /* 100 * 10000 = 1.000.000 usecs per second. */ tone->n_samples = gen->sample_rate / 100; tone->n_samples *= tone->duration; tone->n_samples /= 10000; //fprintf(stderr, MSG_PREFIX "length of regular tone = %d [samples]\n", tone->n_samples); /* Length of a single slope (rising or falling). */ cw_sample_iter_t slope_n_samples = gen->sample_rate / 100; slope_n_samples *= gen->tone_slope.duration; slope_n_samples /= 10000; if (tone->slope_mode == CW_SLOPE_MODE_RISING_SLOPE) { tone->rising_slope_n_samples = slope_n_samples; tone->falling_slope_n_samples = 0; } else if (tone->slope_mode == CW_SLOPE_MODE_FALLING_SLOPE) { tone->rising_slope_n_samples = 0; tone->falling_slope_n_samples = slope_n_samples; } else if (tone->slope_mode == CW_SLOPE_MODE_STANDARD_SLOPES) { tone->rising_slope_n_samples = slope_n_samples; tone->falling_slope_n_samples = slope_n_samples; } else if (tone->slope_mode == CW_SLOPE_MODE_NO_SLOPES) { tone->rising_slope_n_samples = 0; tone->falling_slope_n_samples = 0; } else { cw_assert (0, MSG_PREFIX "unknown tone slope mode %d", tone->slope_mode); } /* This is part of initialization of tone. Zero samples from the tone have been calculated and put into generator's buffer. */ tone->sample_iterator = 0; #if 0 /* Debug code. */ if (tone->is_forever) { fprintf(stderr, "++++ count of samples in forever tone '%c' = %d\n", tone->debug_id, tone->n_samples); } else { fprintf(stderr, "++++ count of samples in regular tone '%c' = %d\n", tone->debug_id, tone->n_samples); } #endif return; } cw_ret_t cw_gen_set_speed(cw_gen_t * gen, int new_value) { if (new_value < CW_SPEED_MIN || new_value > CW_SPEED_MAX) { errno = EINVAL; return CW_FAILURE; } if (new_value != gen->send_speed) { gen->send_speed = new_value; /* Changes of send speed require resynchronization. */ gen->parameters_in_sync = false; cw_gen_sync_parameters_internal(gen); } return CW_SUCCESS; } cw_ret_t cw_gen_set_frequency(cw_gen_t * gen, int new_value) { if (new_value < CW_FREQUENCY_MIN || new_value > CW_FREQUENCY_MAX) { errno = EINVAL; return CW_FAILURE; } else { gen->frequency = new_value; return CW_SUCCESS; } } cw_ret_t cw_gen_set_volume(cw_gen_t * gen, int new_value) { if (new_value < CW_VOLUME_MIN || new_value > CW_VOLUME_MAX) { errno = EINVAL; return CW_FAILURE; } else { gen->volume_percent = new_value; gen->volume_abs = (gen->volume_percent * CW_AUDIO_VOLUME_RANGE) / 100; cw_gen_set_tone_slope(gen, -1, -1); return CW_SUCCESS; } } cw_ret_t cw_gen_set_gap(cw_gen_t * gen, int new_value) { if (new_value < CW_GAP_MIN || new_value > CW_GAP_MAX) { errno = EINVAL; return CW_FAILURE; } if (new_value != gen->gap) { gen->gap = new_value; /* Changes of gap require resynchronization. */ gen->parameters_in_sync = false; cw_gen_sync_parameters_internal(gen); } return CW_SUCCESS; } cw_ret_t cw_gen_set_weighting(cw_gen_t * gen, int new_value) { if (new_value < CW_WEIGHTING_MIN || new_value > CW_WEIGHTING_MAX) { errno = EINVAL; return CW_FAILURE; } if (new_value != gen->weighting) { gen->weighting = new_value; /* Changes of weighting require resynchronization. */ gen->parameters_in_sync = false; cw_gen_sync_parameters_internal(gen); } return CW_SUCCESS; } int cw_gen_get_speed(const cw_gen_t * gen) { return gen->send_speed; } int cw_gen_get_frequency(const cw_gen_t * gen) { return gen->frequency; } int cw_gen_get_volume(const cw_gen_t * gen) { return gen->volume_percent; } int cw_gen_get_gap(const cw_gen_t * gen) { return gen->gap; } int cw_gen_get_weighting(const cw_gen_t * gen) { return gen->weighting; } /** @brief Get timing parameters for sending Return the low-level timing parameters calculated from the speed, gap, tolerance, and weighting set. Units of returned parameter values are microseconds. Use NULL for the pointer argument to any parameter value not required. @internal @reviewed 2020-08-06 @endinternal @param[in] gen @param[out] dot_duration @param[out] dash_duration @param[out] ims_duration duration of inter-mark-space @param[out] ics_duration duration of inter-character-space @param[out] iws_duration duration of inter-word-space @param[out] additional_space_duration @param[out] adjustment_space_duration */ void cw_gen_get_timing_parameters_internal(cw_gen_t * gen, int * dot_duration, int * dash_duration, int * ims_duration, int * ics_duration, int * iws_duration, int * additional_space_duration, int * adjustment_space_duration) { cw_gen_sync_parameters_internal(gen); if (dot_duration) { *dot_duration = gen->dot_duration; } if (dash_duration) { *dash_duration = gen->dash_duration; } if (ims_duration) { *ims_duration = gen->ims_duration; } if (ics_duration) { *ics_duration = gen->ics_duration; } if (iws_duration) { *iws_duration = gen->iws_duration; } if (additional_space_duration) { *additional_space_duration = gen->additional_space_duration; } if (adjustment_space_duration) { *adjustment_space_duration = gen->adjustment_space_duration; } return; } /** @brief Enqueue a mark (Dot or Dash) Low level primitive to enqueue a tone for mark of the given type, followed by the standard inter-mark-space. Function sets errno to EINVAL if an argument is invalid, and returns CW_FAILURE. Function also returns CW_FAILURE if adding the element to queue of tones failed. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator to be used to enqueue a mark and inter-mark-space @param[in] mark mark to send: Dot (CW_DOT_REPRESENTATION) or Dash (CW_DASH_REPRESENTATION) @param[in] is_first is it a first mark in a character? @return CW_FAILURE on failure @return CW_SUCCESS on success */ cw_ret_t cw_gen_enqueue_mark_internal(cw_gen_t * gen, char mark, bool is_first) { cw_ret_t status = CW_FAILURE; /* Synchronize low-level timings if required. */ cw_gen_sync_parameters_internal(gen); /* TODO: do we need to synchronize here receiver as well? */ /* Send either a dot or a dash mark, depending on representation. */ if (mark == CW_DOT_REPRESENTATION) { cw_tone_t tone; CW_TONE_INIT(&tone, gen->frequency, gen->dot_duration, CW_SLOPE_MODE_STANDARD_SLOPES); tone.is_first = is_first; status = cw_tq_enqueue_internal(gen->tq, &tone); } else if (mark == CW_DASH_REPRESENTATION) { cw_tone_t tone; CW_TONE_INIT(&tone, gen->frequency, gen->dash_duration, CW_SLOPE_MODE_STANDARD_SLOPES); tone.is_first = is_first; status = cw_tq_enqueue_internal(gen->tq, &tone); } else { errno = EINVAL; status = CW_FAILURE; } if (CW_SUCCESS != status) { return CW_FAILURE; } /* Send the inter-mark-space. */ cw_tone_t tone; CW_TONE_INIT(&tone, 0, gen->ims_duration, CW_SLOPE_MODE_NO_SLOPES); if (CW_SUCCESS != cw_tq_enqueue_internal(gen->tq, &tone)) { return CW_FAILURE; } else { return CW_SUCCESS; } } /** @brief Enqueue 2-Unit inter-character-space The function enqueues space of length 2 Units. The function is intended to be used after inter-mark-space has already been enqueued. In such situation standard inter-mark-space (one Unit) and two Units enqueued by this function form a full standard inter-character-space (three Units). Inter-character adjustment space is added at the end. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator in which to enqueue the space @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_gen_enqueue_2u_ics_internal(cw_gen_t * gen) { /* Synchronize low-level timing parameters. */ cw_gen_sync_parameters_internal(gen); /* Enqueue standard inter-character-space, plus any additional inter-character gap. */ cw_tone_t tone; CW_TONE_INIT(&tone, 0, gen->ics_duration + gen->additional_space_duration, CW_SLOPE_MODE_NO_SLOPES); return cw_tq_enqueue_internal(gen->tq, &tone); } /** @brief Enqueue space character (' ') in generator, to be sent using Morse code The function should be used to enqueue a regular ' ' character. The function enqueues space of length 5 Units. The function is intended to be used after inter-mark-space and inter-character-space have already been enqueued. In such situation standard inter-mark-space (one Unit) and inter-character-space (two Units) and regular space (five units) form a full standard inter-word-space (seven Units). TODO: review this description again. This function alone doesn't send space character, it sends a part of what can be seen as inter-word-space. Inter-word adjustment space is added at the end. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator in which to enqueue the space @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_gen_enqueue_iws_internal(cw_gen_t * gen) { /* Synchronize low-level timing parameters. */ cw_gen_sync_parameters_internal(gen); /* Send silence for the word delay period, plus any adjustment that may be needed at end of word. Make it in two tones, and here is why. Let's say that 'tone queue low watermark' is one element (i.e. one tone). In order for tone queue to recognize that a 'low tone queue' callback needs to be called, the level in tq needs to drop from 2 to 1. Almost every queued character guarantees that there will be at least two tones, e.g for 'E' it is dash + following space. But what about a ' ' character? If we enqueue ' ' character as single tone, there is only one tone in tone queue, and the tone queue manager can't recognize when the level drops from 2 to 1 (and thus the 'low level' callback won't be called). If we enqueue ' ' character as two separate tones (as we do this in this function), the tone queue manager can recognize level dropping from 2 to 1. Then the passing of critical level can be noticed, and "low level" callback can be called. BUT: Sometimes the first tone is dequeued before/during the second one is enqueued, and we can't recognize 2->1 event. So, to be super-sure that there is a recognizable event of passing tone queue level from 2 to 1, we split the inter-word-space into N parts and enqueue them. This way we have N + 1 tones per space, and client applications that rely on low level threshold == 1 can correctly work when enqueueing spaces. At 60 wpm duration of gen->iws_duration is 100000 [us], so it's large enough to safely divide it by small integer value. */ int enqueued = 0; cw_tone_t tone; #if 0 /* This section is incorrect. Enable this section only for tests. This section "implements" a bug that was present in libcw until version 6.4.1 and that is now tested by src/libcw/tests/libcw_test_tq_short_space.c */ const int n = 1; /* No division. Old situation causing an error in client applications. */ #else const int n = 2; /* "small integer value" - used to have more tones per inter-word-space. */ #endif CW_TONE_INIT(&tone, 0, gen->iws_duration / n, CW_SLOPE_MODE_NO_SLOPES); for (int i = 0; i < n; i++) { if (CW_SUCCESS != cw_tq_enqueue_internal(gen->tq, &tone)) { return CW_FAILURE; } enqueued++; } CW_TONE_INIT(&tone, 0, gen->adjustment_space_duration, CW_SLOPE_MODE_NO_SLOPES); if (CW_SUCCESS != cw_tq_enqueue_internal(gen->tq, &tone)) { return CW_FAILURE; } enqueued++; cw_debug_msg (&cw_debug_object, CW_DEBUG_GENERATOR, CW_DEBUG_DEBUG, MSG_PREFIX "enqueued %d tones per iw space, tq len = %zu", enqueued, cw_tq_length_internal(gen->tq)); return CW_SUCCESS; } cw_ret_t cw_gen_enqueue_representation(cw_gen_t * gen, const char * representation) { if (!cw_representation_is_valid(representation)) { errno = EINVAL; return CW_FAILURE; } /* Before we let this representation loose on tone generation, we'd really like to know that all of its tones will get queued up successfully. The right way to do this is to calculate the number of tones in our representation, then check that the space exists in the tone queue. However, since the queue is comfortably long, we can get away with just looking for a high water mark. */ if (cw_tq_length_internal(gen->tq) >= gen->tq->high_water_mark) { errno = EAGAIN; return CW_FAILURE; } /* Enqueue the marks. Every mark is followed by inter-mark-space. */ for (int i = 0; representation[i] != '\0'; i++) { const bool is_first = i == 0; if (CW_SUCCESS != cw_gen_enqueue_mark_internal(gen, representation[i], is_first)) { return CW_FAILURE; } } /* This function will add additional 2 Units. Together with 1 Unit of inter-mark-space added after last Mark, it will form a full 3-Unit inter-character-space. */ if (CW_SUCCESS != cw_gen_enqueue_2u_ics_internal(gen)) { return CW_FAILURE; } return CW_SUCCESS; } cw_ret_t cw_gen_enqueue_representation_no_ics(cw_gen_t * gen, const char * representation) { if (!cw_representation_is_valid(representation)) { errno = EINVAL; return CW_FAILURE; } /* Before we let this representation loose on tone generation, we'd really like to know that all of its tones will get queued up successfully. The right way to do this is to calculate the number of tones in our representation, then check that the space exists in the tone queue. However, since the queue is comfortably long, we can get away with just looking for a high water mark. TODO: do the check the proper way. TODO: wrap this check into a function and reuse it in cw_gen_enqueue_representation() */ if (cw_tq_length_internal(gen->tq) >= gen->tq->high_water_mark) { errno = EAGAIN; return CW_FAILURE; } /* Enqueue the marks. Every mark is followed by inter-mark-space. */ for (int i = 0; representation[i] != '\0'; i++) { const bool is_first = i == 0; if (CW_SUCCESS != cw_gen_enqueue_mark_internal(gen, representation[i], is_first)) { return CW_FAILURE; } } /* No inter-character-space added here. */ return CW_SUCCESS; } /** @brief Enqueue a given valid ASCII character in generator, to be sent using Morse code _valid_character_ in function's name means that the function expects the character @p character to be valid (@p character should be validated by caller before passing it to the function). no_ics in function's name means that the inter-character-space is not appended at the end of Marks and Spaces enqueued in generator (but the last inter-mark-space is). @exception ENOENT @p character is not a valid character. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator to be used to enqueue character @param[in] character character to enqueue @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_gen_enqueue_valid_character_no_ics_internal(cw_gen_t * gen, char character) { if (NULL == gen) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_GENERATOR, CW_DEBUG_ERROR, MSG_PREFIX "no generator available"); return CW_FAILURE; } /* ' ' character (i.e. inter-word-space) is a special case. */ if (character == ' ') { return cw_gen_enqueue_iws_internal(gen); } const char * representation = cw_character_to_representation_internal(character); /* This shouldn't happen since we are in _valid_character_ function... */ cw_assert (NULL != representation, MSG_PREFIX "failed to find representation for character '%c'/%hhx", character, character); /* ... but fail gracefully anyway. */ if (NULL == representation) { errno = ENOENT; return CW_FAILURE; } if (CW_SUCCESS != cw_gen_enqueue_representation_no_ics(gen, representation)) { return CW_FAILURE; } /* No inter-character-space here. */ return CW_SUCCESS; } /** @brief Enqueue a given valid ASCII character in generator, to be sent using Morse code After enqueueing last Mark (Dot or Dash) comprising a character, an inter-mark-space is enqueued. Inter-character-space is enqueued after that last inter-mark-space. _valid_character_ in function's name means that the function expects the character @p character to be valid (@p character should be validated by caller before passing it to the function). @exception ENOENT @p character is not a valid character. @internal @reviewed 2020-08-23 @endinternal @param[in] gen generator to be used to enqueue character @param[in] character character to enqueue @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_gen_enqueue_valid_character_internal(cw_gen_t * gen, char character) { /* This function will add 1 Unit of inter-mark-space at the end. */ if (CW_SUCCESS != cw_gen_enqueue_valid_character_no_ics_internal(gen, character)) { return CW_FAILURE; } /* This function will add additional 2 Units. Together with previous 1 Unit, it will form a full 3-Unit inter-character-space. */ if (CW_SUCCESS != cw_gen_enqueue_2u_ics_internal(gen)) { return CW_FAILURE; } return CW_SUCCESS; } cw_ret_t cw_gen_enqueue_character(cw_gen_t * gen, char character) { if (!cw_character_is_valid(character)) { errno = ENOENT; return CW_FAILURE; } /* This function adds inter-character-space at the end of character. */ if (CW_SUCCESS != cw_gen_enqueue_valid_character_internal(gen, character)) { return CW_FAILURE; } return CW_SUCCESS; } cw_ret_t cw_gen_enqueue_character_no_ics(cw_gen_t * gen, char character) { if (!cw_character_is_valid(character)) { errno = ENOENT; return CW_FAILURE; } /* This function doesn't add inter-character-space at the end of character. */ if (CW_SUCCESS != cw_gen_enqueue_valid_character_no_ics_internal(gen, character)) { return CW_FAILURE; } return CW_SUCCESS; } cw_ret_t cw_gen_enqueue_string(cw_gen_t * gen, const char * string) { /* Check that the string is composed of valid characters. */ if (!cw_string_is_valid(string)) { errno = ENOENT; return CW_FAILURE; } /* Send every character in the string. */ for (int i = 0; string[i] != '\0'; i++) { /* This function adds inter-character-space at the end of character. */ if (CW_SUCCESS != cw_gen_enqueue_valid_character_internal(gen, string[i])) { return CW_FAILURE; } } return CW_SUCCESS; } /** @brief Reset generator's essential parameters to their initial values You need to call cw_gen_sync_parameters_internal() after call to this function. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator for which to reset parameters */ void cw_gen_reset_parameters_internal(cw_gen_t * gen) { cw_assert (NULL != gen, MSG_PREFIX "generator is NULL"); gen->send_speed = CW_SPEED_INITIAL; gen->frequency = CW_FREQUENCY_INITIAL; gen->volume_percent = CW_VOLUME_INITIAL; gen->volume_abs = (gen->volume_percent * CW_AUDIO_VOLUME_RANGE) / 100; gen->gap = CW_GAP_INITIAL; gen->weighting = CW_WEIGHTING_INITIAL; gen->parameters_in_sync = false; return; } /** @brief Synchronize generator's low level timing parameters @internal @reviewed 2020-10-24 @endinternal @param[in] gen generator for which to synchronize parameters */ void cw_gen_sync_parameters_internal(cw_gen_t * gen) { cw_assert (NULL != gen, MSG_PREFIX "generator is NULL"); /* Do nothing if we are already synchronized. */ if (gen->parameters_in_sync) { return; } /* Set the length of a Dot to be a Unit with any weighting adjustment, and the length of a Dash as three Dot lengths. The weighting adjustment is by adding or subtracting a length based on 50 % as a neutral weighting. TODO: use cw_gen_calculate_durations_internal() here. */ const int unit_duration = CW_DOT_CALIBRATION / gen->send_speed; const int weighting_duration = (2 * (gen->weighting - 50) * unit_duration) / 100; gen->dot_duration = unit_duration + weighting_duration; gen->dash_duration = 3 * gen->dot_duration; /* In proper Morse code timing the following three rules are given: 1. Duration of inter-mark-space is one Unit, perhaps adjusted. 2. Duration of inter-character-space is three Units total. 3. Duration of inter-word-space is seven Units total. ics_duration and iws_duration variables are *additional* durations. Notice how they are calculated below. They aren't full 3 units and 7 units. inter-character-space: is 2 Units, which takes into account preceding one inter-mark-space added after a Mark, inter-word-space: is 5 Units, which takes into account preceding inter-character-space. So these two durations are *additional* ones, i.e. in addition to (already existing) inter-mark-space or inter-character-space. Whether this is good or bad idea to calculate them like this is a separate topic. Just be aware of this fact. TODO: make sure that we never add inter-word-space after inter-character-space which was added after inter-mark-space. That would sum up to 8 Units total, and a proper inter-word-space is 7 Units. The duration of inter-mark-space is adjusted by 28/22 times weighting length to keep PARIS calibration correctly timed (PARIS has 22 full units, and 28 empty ones). Inter-mark-space and end of character delays take weightings into account. */ gen->ims_duration = unit_duration - (28 * weighting_duration) / 22; gen->ics_duration = 3 * unit_duration - gen->ims_duration; gen->iws_duration = 7 * unit_duration - gen->ics_duration; gen->additional_space_duration = gen->gap * unit_duration; /* For "Farnsworth", there also needs to be an adjustment delay added to the end of words, otherwise the rhythm is lost on word end. I don't know if there is an "official" value for this, but 2.33 or so times the gap is the correctly scaled value, and seems to sound okay. Thanks to Michael D. Ivey for identifying this in earlier versions of libcw. */ gen->adjustment_space_duration = (7 * gen->additional_space_duration) / 3; cw_debug_msg (&cw_debug_object, CW_DEBUG_PARAMETERS, CW_DEBUG_INFO, MSG_PREFIX "'%s': gen durations [us] at speed %d [wpm]:\n" "[II] " MSG_PREFIX " dot: %11d\n" "[II] " MSG_PREFIX " dash: %11d\n" "[II] " MSG_PREFIX " ims: %11d\n" "[II] " MSG_PREFIX " ics: %11d\n" "[II] " MSG_PREFIX " iws: %11d\n" "[II] " MSG_PREFIX " adsd: %11d\n" "[II] " MSG_PREFIX " ajsd: %11d", gen->label, gen->send_speed, gen->dot_duration, gen->dash_duration, gen->ims_duration, gen->ics_duration, gen->iws_duration, gen->additional_space_duration, gen->adjustment_space_duration); /* Generator parameters are now in sync. */ gen->parameters_in_sync = true; return; } /** Helper function intended to hide details of tone queue and of enqueueing a tone from cw_key module. The function should be called only on "key down" (begin mark) event from hardware straight key. The function is called in very specific context, see cw_key module for details. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_gen_enqueue_begin_mark_internal(cw_gen_t * gen) { /* In case of straight key we don't know at all how long the tone should be (we don't know for how long the straight key will be closed). Let's enqueue a beginning of mark (rising slope) + "forever" (constant) tone. The constant tone will be generated until key goes into CW_KEY_VALUE_OPEN state. */ /* Enqueue rising slope */ cw_tone_t tone; CW_TONE_INIT(&tone, gen->frequency, gen->tone_slope.duration, CW_SLOPE_MODE_RISING_SLOPE); cw_ret_t cwret = cw_tq_enqueue_internal(gen->tq, &tone); if (cwret != CW_SUCCESS) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_TONE_QUEUE, CW_DEBUG_ERROR, MSG_PREFIX "enqueue begin mark: failed to enqueue rising slope: '%s'", strerror(errno)); /* TODO: what do we do with this error now? The cwret variable will be overwritten below. */ } /* Enqueue plateau - forever tone. */ /* If there was an error during enqueue of rising slope of mark, assume that it was a transient error, and proceed to enqueueing forever tone. Only after we fail to enqueue the "main" tone, we are allowed to return failure to caller. */ CW_TONE_INIT(&tone, gen->frequency, gen->quantum_duration, CW_SLOPE_MODE_NO_SLOPES); tone.is_forever = true; cwret = cw_tq_enqueue_internal(gen->tq, &tone); if (cwret != CW_SUCCESS) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_TONE_QUEUE, CW_DEBUG_ERROR, MSG_PREFIX "enqueue begin mark: failed to enqueue forever tone: '%s'", strerror(errno)); } cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_TONE_QUEUE, CW_DEBUG_DEBUG, MSG_PREFIX "enqueue begin mark: tone queue len = %zu", cw_tq_length_internal(gen->tq)); return cwret; } /** Helper function intended to hide details of tone queue and of enqueueing a tone from cw_key module. The function should be called only on "key up" (begin space) event from hardware straight key. The function is called in very specific context, see cw_key module for details. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_gen_enqueue_begin_space_internal(cw_gen_t * gen) { if (gen->sound_system == CW_AUDIO_CONSOLE) { /* FIXME: I think that enqueueing tone is not just a matter of generating it using generator, but also a matter of timing events using generator. Enqueueing tone here and dequeueing it later will be used to control state of a key. How does enqueueing a quantum tone influences the key state? */ /* Generate just a bit of silence, just to switch buzzer from generating a sound to being silent. */ cw_tone_t tone; CW_TONE_INIT(&tone, 0, gen->quantum_duration, CW_SLOPE_MODE_NO_SLOPES); return cw_tq_enqueue_internal(gen->tq, &tone); } else { /* For soundcards a falling slope with volume from max to zero should be enough, but... */ cw_tone_t tone; CW_TONE_INIT(&tone, gen->frequency, gen->tone_slope.duration, CW_SLOPE_MODE_FALLING_SLOPE); cw_ret_t cwret = cw_tq_enqueue_internal(gen->tq, &tone); if (CW_SUCCESS == cwret) { /* ... but on some occasions, on some platforms, some sound systems may need to constantly generate "silent" tone. These four lines of code are just for them. FIXME: what occasions? what platforms? what sound systems? It would be better to avoid queueing silent "forever" tone because this increases CPU usage. It would be better to simply not to queue any new tones after "falling slope" tone. Silence after the last falling slope would simply last on itself until there is new tone in queue to dequeue. */ CW_TONE_INIT(&tone, 0, gen->quantum_duration, CW_SLOPE_MODE_NO_SLOPES); tone.is_forever = true; cwret = cw_tq_enqueue_internal(gen->tq, &tone); } return cwret; } } /** Helper function intended to hide details of tone queue and of enqueueing a tone from cw_key module. The function should be called on hardware key events only. Since we enqueue symbols, we know that they have limited, specified length. This means that the function should be called for events from iambic keyer. The function doesn't add any inter-mark-space (hence "no_ims" in name). The function is called in very specific context, see cw_key module for details. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator @param[in] symbol symbol to enqueue (Space/Dot/Dash) @return CW_SUCCESS on success @return CW_FAILURE on failure */ cw_ret_t cw_gen_enqueue_symbol_no_ims_internal(cw_gen_t * gen, char symbol) { cw_tone_t tone = { 0 }; switch (symbol) { case CW_DOT_REPRESENTATION: CW_TONE_INIT(&tone, gen->frequency, gen->dot_duration, CW_SLOPE_MODE_STANDARD_SLOPES); break; case CW_DASH_REPRESENTATION: CW_TONE_INIT(&tone, gen->frequency, gen->dash_duration, CW_SLOPE_MODE_STANDARD_SLOPES); break; case CW_SYMBOL_SPACE: /* TODO: how come we enqueue IMS in "no_ims" function? */ CW_TONE_INIT(&tone, 0, gen->ims_duration, CW_SLOPE_MODE_NO_SLOPES); break; default: cw_assert (0, MSG_PREFIX "unknown key symbol '%d'", symbol); break; } return cw_tq_enqueue_internal(gen->tq, &tone); } cw_ret_t cw_gen_wait_for_queue_level(cw_gen_t * gen, size_t level) { return cw_tq_wait_for_level_internal(gen->tq, level); } void cw_gen_flush_queue(cw_gen_t * gen) { /* This function locks and unlocks mutex. */ cw_tq_flush_internal(gen->tq); /* TODO: we probably want to have these two functions separated. Function called cw_gen_flush_queue() probably shouldn't also silence generator. */ /* Force silence on the speaker anyway, and stop any background soundcard tone generation. */ cw_gen_silence_internal(gen); return; } cw_ret_t cw_gen_remove_last_character(cw_gen_t * gen) { return cw_tq_remove_last_character_internal(gen->tq); } cw_ret_t cw_gen_get_sound_device(cw_gen_t const * gen, char * buffer, size_t size) { cw_assert (NULL != gen, MSG_PREFIX "generator is NULL"); snprintf(buffer, size, "%s", gen->picked_device_name); return CW_SUCCESS; } int cw_gen_get_sound_system(cw_gen_t const * gen) { cw_assert (NULL != gen, MSG_PREFIX "generator is NULL"); return gen->sound_system; } size_t cw_gen_get_queue_length(cw_gen_t const * gen) { return cw_tq_length_internal(gen->tq); } cw_ret_t cw_gen_register_low_level_callback(cw_gen_t * gen, cw_queue_low_callback_t callback_func, void * callback_arg, size_t level) { return cw_tq_register_low_level_callback_internal(gen->tq, callback_func, callback_arg, level); } cw_ret_t cw_gen_wait_for_end_of_current_tone(cw_gen_t * gen) { return cw_tq_wait_for_end_of_current_tone_internal(gen->tq); } bool cw_gen_is_queue_full(cw_gen_t const * gen) { return cw_tq_is_full_internal(gen->tq); } cw_ret_t cw_gen_set_label(cw_gen_t * gen, const char * label) { if (NULL == gen) { cw_debug_msg (&cw_debug_object, CW_DEBUG_CLIENT_CODE, CW_DEBUG_ERROR, MSG_PREFIX "'gen' argument is NULL"); return CW_FAILURE; } if (NULL == label) { cw_debug_msg (&cw_debug_object, CW_DEBUG_CLIENT_CODE, CW_DEBUG_ERROR, MSG_PREFIX "'%s': 'label' argument is NULL", gen->label); return CW_FAILURE; } if (strlen(label) > (LIBCW_OBJECT_INSTANCE_LABEL_SIZE - 1)) { cw_debug_msg (&cw_debug_object, CW_DEBUG_CLIENT_CODE, CW_DEBUG_WARNING, MSG_PREFIX "'%s': new label '%s' too long, truncating", gen->label, label); /* Not an error, just log warning. New label will be truncated. */ } /* Notice that empty label is acceptable. In such case we will erase old label. */ snprintf(gen->label, sizeof (gen->label), "%s", label); /* Generator's tone queue is not publicly available, but we have to set some label for the queue as well. */ if (NULL == gen->tq) { cw_debug_msg (&cw_debug_object, CW_DEBUG_INTERNAL, CW_DEBUG_ERROR, MSG_PREFIX "'%s': tq is NULL, not setting label for it", gen->label); /* This is not a good place to investigate why tq is NULL, so simply continue. */ } else { snprintf(gen->tq->label, sizeof (gen->tq->label), "%s", label); } return CW_SUCCESS; } cw_ret_t cw_gen_get_label(const cw_gen_t * gen, char * label, size_t size) { if (NULL == gen) { cw_debug_msg (&cw_debug_object, CW_DEBUG_CLIENT_CODE, CW_DEBUG_ERROR, MSG_PREFIX "'gen' argument is NULL"); return CW_FAILURE; } if (NULL == label) { cw_debug_msg (&cw_debug_object, CW_DEBUG_CLIENT_CODE, CW_DEBUG_ERROR, MSG_PREFIX "'%s': 'label' argument is NULL", gen->label); return CW_FAILURE; } /* Notice that we don't care if size is zero. */ snprintf(label, size, "%s", gen->label); return CW_SUCCESS; } /** @brief Function used to set and track value of generator @internal @reviewed 2020-10-03 @endinternal @param[in] gen generator @param[in] tone tone dequeued from generator's tone queue @param[in] queue_state state of queue after dequeueing current tone @return CW_SUCCESS */ static cw_ret_t cw_gen_value_tracking_internal(cw_gen_t * gen, const cw_tone_t * tone, cw_queue_state_t queue_state) { cw_key_value_t value = CW_KEY_VALUE_OPEN; switch (queue_state) { case CW_TQ_JUST_EMPTIED: case CW_TQ_NONEMPTY: /* A valid tone has been dequeued just now. */ value = tone->frequency ? CW_KEY_VALUE_CLOSED : CW_KEY_VALUE_OPEN; break; case CW_TQ_EMPTY: /* Tone queue remains empty. No new tone == no sound. */ value = CW_KEY_VALUE_OPEN; break; default: cw_assert (0, MSG_PREFIX "unexpected state of tone queue: %d", queue_state); break; } cw_gen_value_tracking_set_value_internal(gen, gen->key, value); return CW_SUCCESS; } /** @brief Set new value of generator Filter successive calls with identical value of @p value single action (successive calls with the same value of @p value don't change internally registered value of generator). If and only if the function registers change of generator value, an external callback function (if configured) is called. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator for which to set new value @param[in] key TODO: document @param[in] value value of generator to be set */ void cw_gen_value_tracking_set_value_internal(cw_gen_t * gen, __attribute__((unused)) volatile cw_key_t * key, cw_key_value_t value) { //cw_assert (NULL != key, MSG_PREFIX "gen track value: key is NULL"); if (gen->value_tracking.value == value) { /* This is not an error. This may happen when dequeueing 'forever' tone multiple times in a row. */ /* TODO: uncomment this fprintf() and see how often it's called for straight key actions in xcwcp. 2020-10-17: the frequency may be now decreased after we now call pthread_cond_broadcast() in cw_gen_dequeue_and_generate_internal() only conditionally, if two consecutive tones aren't 'forever'. */ // fprintf(stderr, "gen: dropping the same value %d -> %d\n", gen->value_tracking.value, value); return; } cw_debug_msg (&cw_debug_object, CW_DEBUG_KEYING, CW_DEBUG_INFO, MSG_PREFIX "set gen value: %d->%d", gen->value_tracking.value, value); /* Remember the new generator value. */ gen->value_tracking.value = value; /* In theory client code should register either a receiver (so events from key are passed to receiver directly), or a callback (so events from key are passed to receiver through callback). See comment for cw_key_register_receiver() in libcw_key.c: "Receiver should somehow receive key events from physical or logical key. This can be done in one of two ways:" These two ways are represented by the two 'if' blocks below. So *in theory* only one of these "if" blocks will be executed. */ #if 0 if (false && key->rec) { if (CW_KEY_VALUE_CLOSED == gen->value_tracking.value) { /* Key down. */ cw_rec_mark_begin(key->rec, #ifdef IAMBIC_KEY_HAS_TIMER key->ik.ik_timer #else NULL #endif ); } else { /* Key up. */ cw_rec_mark_end(key->rec, #ifdef IAMBIC_KEY_HAS_TIMER key->ik.ik_timer #else NULL #endif ); } } #endif #if 1 if (gen->value_tracking.value_tracking_callback_func) { cw_debug_msg (&cw_debug_object_dev, CW_DEBUG_KEYING, CW_DEBUG_INFO, MSG_PREFIX "set gen value: about to call value tracking callback, generator value = %d\n", gen->value_tracking.value); (*gen->value_tracking.value_tracking_callback_func)(gen->value_tracking.value_tracking_callback_arg, gen->value_tracking.value); } #endif return; } /** @brief Register external callback function for tracking state of generator Register a @p callback_func function that should be called when a state of a @p gen changes of value from CW_KEY_VALUE_OPEN to CW_KEY_VALUE_CLOSED or vice-versa. The first argument passed to the registered callback function is the supplied @p callback_arg, if any. The second argument passed to registered callback function is the generator's value: CW_KEY_VALUE_OPEN or CW_KEY_VALUE_CLOSED. Calling this routine with a NULL function address removes previously registered callback. @internal @reviewed 2020-08-06 @endinternal @param[in] gen generator for which to register a callback @param[in] callback_func callback function to be called on generator state changes @param[in] callback_arg first argument to callback_func */ void cw_gen_register_value_tracking_callback_internal(cw_gen_t * gen, cw_gen_value_tracking_callback_t callback_func, void * callback_arg) { gen->value_tracking.value_tracking_callback_func = callback_func; gen->value_tracking.value_tracking_callback_arg = callback_arg; return; } /** @brief Pick a device name for given sound system Don't call this function for CW_AUDIO_SOUNDCARD because this function doesn't implement logic for selecting a current sound system out of a set of systems that are able to use a soundcard (PulseAudio, ALSA, OSS). For ALSA sound system the value returned through @p picked_device_name is guaranteed to be non-empty. For PulseAudio the value returned through @p picked_device_name will be empty if default device is to be used. PA API accepts NULL pointer as indication to use default device, so the empty value of @p picked_device_name should be somehow "converted" to NULL pointer passed to PulseAudio API. TODO: selection of device names in libcw is a mess full of repetitions. Re-think and re-implement it. Keep in mind that for PulseAudio sound system a NULL passed to the sound system's API is allowed value, indicating "use default". So we want to be able to pass NULL to the API. @reviewed 2020-11-14 @param[in] alternative_device_name device name provided by library's client code (may be NULL or empty) @param[in] sound_system sound system for which the device name is being picked @param[out] picked_device_name output buffer @param[in] size size of @p picked_device_name buffer @return CW_SUCCESS if the device name has been picked successfully @return CW_FAILURE otherwise */ cw_ret_t cw_gen_pick_device_name_internal(const char * alternative_device_name, enum cw_audio_systems sound_system, char * picked_device_name, size_t size) { snprintf(picked_device_name, size, "%s", ""); cw_ret_t cwret = CW_FAILURE; switch (sound_system) { case CW_AUDIO_NULL: /* This is an internal type of sound system (and device name isn't really used), and I decided that for simplicity the returned pointer will never be NULL. So behaviour is the same as for ALSA. */ case CW_AUDIO_CONSOLE: case CW_AUDIO_OSS: /* For above 2 sound systems the Unix open() function doesn't do any special interpretation of NULL pointer argument or empty string argument. We have to provide explicit device name or path. So behaviour is the same as for ALSA. */ case CW_AUDIO_ALSA: /* When you want to tell ALSA to use ALSA's default device, don't pass NULL or empty string, because ALSA's snd_pcm_open() doesn't interpret NULL or empty string in any special way. Use explicit value of 'device name' argument. */ if (NULL == alternative_device_name || '\0' == alternative_device_name[0] || 0 == strcmp(alternative_device_name, default_sound_devices[sound_system])) { /* No alternative device provided, use libcw's default. */ snprintf(picked_device_name, size, "%s", default_sound_devices[sound_system]); } else { /* Use non-default device provided by client code. */ snprintf(picked_device_name, size, "%s", alternative_device_name); } cwret = CW_SUCCESS; break; case CW_AUDIO_PA: if (NULL == alternative_device_name || '\0' == alternative_device_name[0] || 0 == strcmp(alternative_device_name, default_sound_devices[sound_system])) { /* Empty: code that will call PulseAudio API will have to recognize empty string and pass NULL to PulseAudio API. The API will recognize the NULL as 'select PulseAudio's default device'. */ snprintf(picked_device_name, size, "%s", ""); } else { /* Use non-default device name provided by client code. */ snprintf(picked_device_name, size, "%s", alternative_device_name); } cwret = CW_SUCCESS; break; case CW_AUDIO_SOUNDCARD: /* This function should never be called for SOUNDCARD sound device. It should be called for specific sound systems only. */ cw_debug_msg (&cw_debug_object, CW_DEBUG_KEYING, CW_DEBUG_ERROR, MSG_PREFIX "%s:%d unexpected sound system %d\n", __func__, __LINE__, sound_system); cwret = CW_FAILURE; break; case CW_AUDIO_NONE: default: cw_debug_msg (&cw_debug_object, CW_DEBUG_KEYING, CW_DEBUG_ERROR, MSG_PREFIX "%s:%d invalid sound system %d\n", __func__, __LINE__, sound_system); cwret = CW_FAILURE; break; } return cwret; } void cw_gen_calculate_durations_internal(cw_gen_durations_t * durations, int speed, int weighting) { durations->unit_duration = CW_DOT_CALIBRATION / speed; durations->weighting_duration = (2 * (weighting - 50) * durations->unit_duration) / 100; durations->dot_duration = durations->unit_duration + durations->weighting_duration; return; } unixcw-3.6.0/src/libcw/clang_tidy.sh0000755000175000017500000000117514000344554014324 00000000000000CHECK_INCLUDE_PATHS="-I. -I.. -I../cwutils/" CHECK_CHECKS="*" CHECK_CHECKS+=",-llvm-header-guard" CHECK_CHECKS+=",-readability-braces-around-statements,-hicpp-braces-around-statements" CHECK_CHECKS+=",-readability-else-after-return,-clang-diagnostic-deprecated-declarations" CHECK_CHECKS+=",-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers" CHECK_CHECKS+=",-readability-redundant-control-flow" CHECK_CHECKS+=",-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling" #clang-tidy -checks=$CHECK_CHECKS *.c *.h -- $CHECK_INCLUDE_PATHS clang-tidy-10 -checks=$CHECK_CHECKS *.c *.h -- $CHECK_INCLUDE_PATHS unixcw-3.6.0/src/libcw/libcw.pc.in0000644000175000017500000000030214000376276013676 00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libcw Description: CW (Morse code) library Version: 7.0.0 Libs: -L${libdir} -lcw Cflags: -I${includedir} unixcw-3.6.0/src/libcw/libcw_null.c0000644000175000017500000001006214000344554014141 00000000000000/* Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** @file libcw_null.c @brief Null sound sink. No sound is being played, but time periods necessary for generator to operate are being measured. */ #include #include #include #include #include "libcw_gen.h" #include "libcw_null.h" #include "libcw_utils.h" static cw_ret_t cw_null_open_and_configure_sound_device_internal(cw_gen_t * gen, const cw_gen_config_t * gen_conf); static void cw_null_close_sound_device_internal(cw_gen_t * gen); static cw_ret_t cw_null_write_tone_to_sound_device_internal(cw_gen_t * gen, const cw_tone_t * tone); /** @brief Configure given @p gen variable to work with Null sound system This function only initializes @p gen by setting some of its members. It doesn't interact with sound system (doesn't try to open or configure it). @reviewed 2020-11-12 @param[in,out] gen generator structure to initialize @return CW_SUCCESS */ cw_ret_t cw_null_init_gen_internal(cw_gen_t * gen) { assert (gen); gen->sound_system = CW_AUDIO_NULL; gen->open_and_configure_sound_device = cw_null_open_and_configure_sound_device_internal; gen->close_sound_device = cw_null_close_sound_device_internal; gen->write_tone_to_sound_device = cw_null_write_tone_to_sound_device_internal; gen->sample_rate = 48000; /* Some asserts may check for non-zero value of sample rate or its derivatives. */ return CW_SUCCESS; } /** @brief Check if it is possible to open Null sound output @reviewed 2020-07-12 @param[in] device_name name of Null device to be used. Value is ignored for Null sound system. @return true it's always possible to write to Null device */ bool cw_is_null_possible(__attribute__((unused)) const char * device_name) { return true; } /** @brief Open and configure Null sound system handle stored in given generator @reviewed 2020-07-12 @param[in] gen generator for which to open and configure sound system handle @param[in] gen_conf @return CW_SUCCESS */ static cw_ret_t cw_null_open_and_configure_sound_device_internal(cw_gen_t * gen, __attribute__((unused)) const cw_gen_config_t * gen_conf) { gen->sound_device_is_open = true; return CW_SUCCESS; } /** @brief Close Null device stored in given generator @reviewed 2020-07-12 @param[in] gen generator for which to close its sound device */ static void cw_null_close_sound_device_internal(cw_gen_t * gen) { gen->sound_device_is_open = false; return; } /** @brief Write to Null sound device configured and opened for generator The function doesn't really write the samples anywhere, it just sleeps for period of time that would be necessary to write the samples to a real sound device and play/sound them. @reviewed 2020-07-12 @param[in] gen generator that will write to sound device @param[in] tone tone to write to sound device @return CW_SUCCESS */ static cw_ret_t cw_null_write_tone_to_sound_device_internal(__attribute__((unused)) cw_gen_t * gen, const cw_tone_t * tone) { assert (gen); assert (gen->sound_system == CW_AUDIO_NULL); assert (tone->duration >= 0); /* TODO: shouldn't the condition be "tone->duration > 0"? */ cw_usleep_internal(tone->duration); return CW_SUCCESS; } unixcw-3.6.0/src/libcw/libcw_signal.c0000644000175000017500000004476614000344554014466 00000000000000/* Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** \file libcw_signal.c \brief Signal handling routines. There are some static variables in this file, maybe they should be moved to some common structure. I've noticed that these functions are used in libcw_gen, libcw_tq and libcw_key. Perhaps these static variables should be members of cw_gen_t. */ #include "config.h" #include #include #include #include "libcw.h" #include "libcw_debug.h" #include "libcw_gen.h" #include "libcw_signal.h" #include "libcw_utils.h" #if defined(HAVE_STRING_H) # include #endif #if defined(HAVE_STRINGS_H) # include #endif /* http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=403043 */ #if defined(NSIG) /* Debian GNU/Linux: signal.h; Debian kFreeBSD: signal.h (libc0.1-dev_2.13-21_kfreebsd-i386.deb) */ #define CW_SIG_MAX (NSIG) #elif defined(_NSIG) /* Debian GNU/Linux: asm-generic/signal.h; Debian kFreeBSD: i386-kfreebsd-gnu/bits/signum.h->signal.h (libc0.1-dev_2.13-21_kfreebsd-i386.deb) */ #define CW_SIG_MAX (_NSIG) #elif defined(RTSIG_MAX) /* Debian GNU/Linux: linux/limits.h */ #define CW_SIG_MAX ((RTSIG_MAX)+1) #elif defined(__FreeBSD__) #define CW_SIG_MAX (_SIG_MAXSIG) #else #error "unknown number of signals" #endif #define MSG_PREFIX "libcw: " extern cw_debug_t cw_debug_object; extern cw_debug_t cw_debug_object_ev; extern cw_debug_t cw_debug_object_dev; extern cw_gen_t *cw_generator; static int cw_timer_run_internal(int usecs); static void cw_sigalrm_handlers_caller_internal(int signal_number); static int cw_sigalrm_block_internal(bool block); static void cw_signal_main_handler_internal(int signal_number); /* The library keeps a single central non-sparse list of SIGALRM signal handlers. The handler functions will be called sequentially on each SIGALRM received. */ enum { CW_SIGALRM_HANDLERS_MAX = 32 }; static void (*cw_sigalrm_handlers[CW_SIGALRM_HANDLERS_MAX])(void); /* Flag to tell us if the SIGALRM handler is installed, and a place to keep the old SIGALRM disposition, so we can restore it when the library decides it can stop handling SIGALRM for a while. */ static bool cw_is_sigalrm_handlers_caller_installed = false; static struct sigaction cw_sigalrm_original_disposition; /** \brief Call handlers of SIGALRM signal This function calls the SIGALRM signal handlers of the library subsystems, expecting them to ignore unexpected calls. The handlers are kept in cw_sigalrm_handlers[] table, and can be added to the table with cw_timer_run_with_handler_internal(). SIGALRM is sent to a process every time an itimer timer expires. The timer is set with cw_timer_run_internal(). */ void cw_sigalrm_handlers_caller_internal(__attribute__((unused)) int signal_number) { /* Call the known functions that are interested in SIGALRM signal. Stop on the first free slot found; valid because the array is filled in order from index 0, and there are no deletions. */ for (int handler = 0; handler < CW_SIGALRM_HANDLERS_MAX && cw_sigalrm_handlers[handler]; handler++) { cw_debug_msg ((&cw_debug_object_dev), CW_DEBUG_INTERNAL, CW_DEBUG_DEBUG, MSG_PREFIX "SIGALRM handler #%d", handler); (cw_sigalrm_handlers[handler])(); } return; } /** \brief Set up a timer for specified number of microseconds Convenience function to set the itimer for a single shot timeout after a given number of microseconds. SIGALRM is sent to caller process when the timer expires. \param usecs - time in microseconds \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_timer_run_internal(int usecs) { struct itimerval itimer; /* Set up a single shot timeout for the given period. */ itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; itimer.it_value.tv_sec = usecs / CW_USECS_PER_SEC; itimer.it_value.tv_usec = usecs % CW_USECS_PER_SEC; int status = setitimer(ITIMER_REAL, &itimer, NULL); if (status == -1) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "setitimer(%d): %s", usecs, strerror(errno)); return CW_FAILURE; } return CW_SUCCESS; } /** \brief Register SIGALRM handler(s), and send SIGALRM signal Install top level handler of SIGALRM signal (cw_sigalrm_handlers_caller_internal()) if it is not already installed. Register given \p sigalrm_handler lower level handler, if not NULL and if not yet registered. Then send SIGALRM signal after delay equal to \p usecs microseconds. \param usecs - time for itimer \param sigalrm_handler - SIGALRM handler to register \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_timer_run_with_handler_internal(int usecs, void (*sigalrm_handler)(void)) { if (!cw_sigalrm_install_top_level_handler_internal()) { return CW_FAILURE; } /* If it's not already present, and one was given, add address of the lower level SIGALRM handler to the list of known handlers. */ if (sigalrm_handler) { int handler = 0; /* Search for this handler, or the first free entry, stopping at the last entry in the table even if it's not a match and not free. */ for (handler = 0; handler < CW_SIGALRM_HANDLERS_MAX - 1; handler++) { if (!cw_sigalrm_handlers[handler] || cw_sigalrm_handlers[handler] == sigalrm_handler) { break; } } /* If the handler is already there, do no more. Otherwise, if we ended the search at an unused entry, add it to the list of lower level handlers. */ if (cw_sigalrm_handlers[handler] != sigalrm_handler) { if (cw_sigalrm_handlers[handler]) { errno = ENOMEM; cw_debug_msg ((&cw_debug_object), CW_DEBUG_INTERNAL, CW_DEBUG_ERROR, MSG_PREFIX "overflow cw_sigalrm_handlers"); return CW_FAILURE; } else { cw_sigalrm_handlers[handler] = sigalrm_handler; } } } /* The fact that we receive a call means that something is using timeouts and sound, so make sure that any pending finalization doesn't happen. */ cw_finalization_cancel_internal(); /* Depending on the value of usec, either set an itimer, or send ourselves SIGALRM right away. */ if (usecs <= 0) { /* Send ourselves SIGALRM immediately. */ if (pthread_kill(cw_generator->thread.id, SIGALRM) != 0) { // if (raise(SIGALRM) != 0) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "raise()"); return CW_FAILURE; } } else { /* Set the itimer to produce a single interrupt after the given duration. */ if (!cw_timer_run_internal(usecs)) { return CW_FAILURE; } } return CW_SUCCESS; } int cw_sigalrm_install_top_level_handler_internal(void) { if (!cw_is_sigalrm_handlers_caller_installed) { /* Install the main SIGALRM handler routine (a.k.a. top level SIGALRM handler) - a function that calls all registered lower level handlers), and keep the old information (disposition) so we can put it back when useful to do so. */ struct sigaction action; action.sa_handler = cw_sigalrm_handlers_caller_internal; action.sa_flags = SA_RESTART; sigemptyset(&action.sa_mask); int status = sigaction(SIGALRM, &action, &cw_sigalrm_original_disposition); if (status == -1) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "sigaction(): %s", strerror(errno)); return CW_FAILURE; } cw_is_sigalrm_handlers_caller_installed = true; } return CW_SUCCESS; } /** \brief Uninstall the SIGALRM handler, if installed Restores SIGALRM's disposition for the system to the state we found it in before we installed our own SIGALRM handler. \return CW_FAILURE on failure \return CW_SUCCESS on success */ int cw_sigalrm_restore_internal(void) { /* Ignore the call if we haven't installed our handler. */ if (cw_is_sigalrm_handlers_caller_installed) { /* Cancel any pending itimer setting. */ if (!cw_timer_run_internal(0)) { return CW_FAILURE; } /* Put back the SIGALRM information saved earlier. */ int status = sigaction(SIGALRM, &cw_sigalrm_original_disposition, NULL); if (status == -1) { perror (MSG_PREFIX "sigaction"); return CW_FAILURE; } cw_is_sigalrm_handlers_caller_installed = false; } return CW_SUCCESS; } /** \brief Check if SIGALRM is currently blocked Check the signal mask of the process, and return false, with errno set to EDEADLK, if SIGALRM is blocked. If function returns true, but errno is set, the function has failed to check if SIGALRM is blocked. \return true if SIGALRM is currently blocked (errno is zero) \return true on errors (errno is set by system call that failed) \return false if SIGALRM is currently not blocked */ bool cw_sigalrm_is_blocked_internal(void) { sigset_t empty_set; sigset_t current_set; /* Prepare empty set of signals */ int status = sigemptyset(&empty_set); if (status == -1) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "sigemptyset(): %s", strerror(errno)); return true; } /* Block an empty set of signals to obtain the current mask. */ status = sigprocmask(SIG_BLOCK, &empty_set, ¤t_set); if (status == -1) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "sigprocmask(): %s", strerror(errno)); return true; } /* Check that SIGALRM is not blocked in the current mask. */ if (sigismember(¤t_set, SIGALRM)) { errno = 0; return true; } else { return false; } } /** \brief Block or unblock SIGALRM signal Function blocks or unblocks SIGALRM. It may be used to block the signal for the duration of certain critical sections, and to unblock the signal afterwards. \param block - pass true to block SIGALRM, and false to unblock it \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_sigalrm_block_internal(bool block) { sigset_t set; /* Prepare empty set of signals */ int status = sigemptyset(&set); if (status == -1) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "sigemptyset(): %s", strerror(errno)); return CW_FAILURE; } /* Add single signal to the set */ status = sigaddset(&set, SIGALRM); if (status == -1) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "sigaddset(): %s", strerror(errno)); return CW_FAILURE; } /* Block or unblock SIGALRM for the process using the set of signals */ status = pthread_sigmask(block ? SIG_BLOCK : SIG_UNBLOCK, &set, NULL); if (status == -1) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "pthread_sigmask(): %s", strerror(errno)); return CW_FAILURE; } return CW_SUCCESS; } /** \brief Block the callback from being called Function blocks the callback from being called for a critical section of caller code if \p block is true, and unblocks the callback if \p block is false. Function works by blocking SIGALRM; a block should always be matched by an unblock, otherwise the tone queue will suspend forever. \param block - pass 1 to block SIGALRM, and 0 to unblock it */ void cw_block_callback(int block) { cw_sigalrm_block_internal((bool) block); return; } /** \brief Wait for a signal, usually a SIGALRM Function assumes that SIGALRM is not blocked. Function may return CW_FAILURE on failure, i.e. when call to sigemptyset(), sigprocmask() or sigsuspend() fails. \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_signal_wait_internal(void) { sigset_t empty_set; sigset_t current_set; /* Prepare empty set of signals */ int status = sigemptyset(&empty_set); if (status == -1) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "sigemptyset(): %s", strerror(errno)); return CW_FAILURE; } /* Block an empty set of signals to obtain the current mask. */ status = sigprocmask(SIG_BLOCK, &empty_set, ¤t_set); if (status == -1) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "sigprocmask(): %s", strerror(errno)); return CW_FAILURE; } /* Wait on the current mask */ status = sigsuspend(¤t_set); if (status == -1 && errno != EINTR) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, MSG_PREFIX "suspend(): %s", strerror(errno)); return CW_FAILURE; } return CW_SUCCESS; } /* Array of callbacks registered for convenience signal handling. They're initialized dynamically to SIG_DFL (if SIG_DFL is not NULL, which it seems that it is in most cases). */ static void (*cw_signal_callbacks[CW_SIG_MAX])(int); /** @brief Generic function calling signal handlers Depending on value of signal handler for given @p signal_number the function calls the registered signal handler. The signal handler for given @p signal_number is a value registered earlier with cw_register_signal_handler(). @internal @reviewed 2020-11-11 @endinternal @param[in] signal_number number of signal to handle */ void cw_signal_main_handler_internal(int signal_number) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_FINALIZATION, CW_DEBUG_INFO, MSG_PREFIX "caught signal %d", signal_number); /* Don't call this function in signal handler context. It will lead to calling pthread_cond_*() functions, and that will lead to program hanging. */ //cw_complete_reset(); void (*callback_func)(int) = cw_signal_callbacks[signal_number]; if (callback_func == SIG_DFL) { /* Calling exit() would lead to calls of functions registered with atexit(), which would be executed in signal handler context. Those functions called by atexit() could call pthread_cond_*() functions, and that would lead to program hanging. */ //exit(EXIT_FAILURE); } else if (callback_func == SIG_IGN) { /* continue */ } else { /* invoke any additional handler callback function */ (*callback_func)(signal_number); } return; } /** \brief Register a signal handler and optional callback function for given signal number On receipt of that signal, all library features will be reset to their default states. Following the reset, if \p callback_func is a function pointer, the function is called; if it is SIG_DFL, the library calls exit(); and if it is SIG_IGN, the library returns from the signal handler. This is a convenience function for clients that need to clean up library on signals, with either exit, continue, or an additional function call; in effect, a wrapper round a restricted form of sigaction. The \p signal_number argument indicates which signal to catch. On problems errno is set to EINVAL if \p signal_number is invalid or if a handler is already installed for that signal, or to the sigaction error code. \param signal_number \param callback_func \return CW_SUCCESS - if the signal handler installs correctly \return CW_FAILURE - on errors or problems */ int cw_register_signal_handler(int signal_number, void (*callback_func)(int)) { static bool is_initialized = false; /* On first call, initialize all signal_callbacks to SIG_DFL. */ if (!is_initialized) { for (int i = 0; i < CW_SIG_MAX; i++) { cw_signal_callbacks[i] = SIG_DFL; } is_initialized = true; } /* Reject invalid signal numbers, and SIGALRM, which we use internally. */ if (signal_number < 0 || signal_number >= CW_SIG_MAX || signal_number == SIGALRM) { errno = EINVAL; return CW_FAILURE; } /* Install our handler as the actual handler. */ struct sigaction action; struct sigaction original_disposition; action.sa_handler = cw_signal_main_handler_internal; action.sa_flags = SA_RESTART; sigemptyset(&action.sa_mask); int status = sigaction(signal_number, &action, &original_disposition); if (status == -1) { perror(MSG_PREFIX "sigaction"); return CW_FAILURE; } /* If we trampled another handler, replace it and return false. */ if (!(original_disposition.sa_handler == cw_signal_main_handler_internal || original_disposition.sa_handler == SIG_DFL || original_disposition.sa_handler == SIG_IGN)) { status = sigaction(signal_number, &original_disposition, NULL); if (status == -1) { perror(MSG_PREFIX "sigaction"); return CW_FAILURE; } errno = EINVAL; return CW_FAILURE; } /* Save the callback function (it may validly be SIG_DFL or SIG_IGN). */ cw_signal_callbacks[signal_number] = callback_func; return CW_SUCCESS; } /** \brief Unregister a signal handler interception Function removes a signal handler interception previously registered with cw_register_signal_handler(). \param signal_number \return true if the signal handler uninstalls correctly \return false otherwise (with errno set to EINVAL or to the sigaction error code) */ int cw_unregister_signal_handler(int signal_number) { /* Reject unacceptable signal numbers. */ if (signal_number < 0 || signal_number >= CW_SIG_MAX || signal_number == SIGALRM) { errno = EINVAL; return CW_FAILURE; } /* See if the current handler was put there by us. */ struct sigaction original_disposition; int status = sigaction(signal_number, NULL, &original_disposition); if (status == -1) { perror(MSG_PREFIX "sigaction"); return CW_FAILURE; } if (original_disposition.sa_handler != cw_signal_main_handler_internal) { /* No, it's not our signal handler. Don't touch it. */ errno = EINVAL; return CW_FAILURE; } /* Remove the signal handler by resetting to SIG_DFL. */ struct sigaction action; action.sa_handler = SIG_DFL; action.sa_flags = 0; sigemptyset(&action.sa_mask); status = sigaction(signal_number, &action, NULL); if (status == -1) { perror(MSG_PREFIX "sigaction"); return CW_FAILURE; } /* Reset the callback entry for tidiness. */ cw_signal_callbacks[signal_number] = SIG_DFL; return CW_SUCCESS; } unixcw-3.6.0/src/libcw/libcw_tq_internal.h0000644000175000017500000000166714000344554015527 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_TQ_INTERNAL_H_ #define _LIBCW_TQ_INTERNAL_H_ #include "libcw_tq.h" #include "libcw_utils.h" /* Internal functions of this module, exposed to unit tests code. */ CW_STATIC_FUNC cw_ret_t cw_tq_set_capacity_internal(cw_tone_queue_t * tq, size_t capacity, size_t high_water_mark); CW_STATIC_FUNC size_t cw_tq_get_high_water_mark_internal(const cw_tone_queue_t * tq) __attribute__((unused)); CW_STATIC_FUNC size_t cw_tq_prev_index_internal(const cw_tone_queue_t * tq, size_t ind) __attribute__((unused)); CW_STATIC_FUNC size_t cw_tq_next_index_internal(const cw_tone_queue_t * tq, size_t ind); CW_STATIC_FUNC bool cw_tq_dequeue_sub_internal(cw_tone_queue_t * tq, cw_tone_t * tone); CW_STATIC_FUNC void cw_tq_make_empty_internal(cw_tone_queue_t * tq); #endif /* #ifndef _LIBCW_TQ_INTERNAL_H_ */ unixcw-3.6.0/src/libcw/libcw.c0000644000175000017500000015030714000344554013116 00000000000000/* Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** \file libcw.c */ #include /* EINVAL on FreeBSD */ #include #include "libcw.h" #include "libcw_debug.h" #include "libcw_gen.h" #include "libcw_key.h" #include "libcw_rec.h" #include "libcw_utils.h" /* Main container for data related to generating audible Morse code. This is a global variable in legacy library. New library code (with API declared in libcw2.h) will not use global generator variable. */ cw_gen_t *cw_generator = NULL; /* From libcw_debug.c. */ extern cw_debug_t cw_debug_object; extern cw_debug_t cw_debug_object_ev; extern cw_debug_t cw_debug_object_dev; static cw_rec_t cw_receiver = { .state = RS_IDLE, .speed = CW_SPEED_INITIAL, .tolerance = CW_TOLERANCE_INITIAL, .gap = CW_GAP_INITIAL, .is_adaptive_receive_mode = CW_REC_ADAPTIVE_MODE_INITIAL, .noise_spike_threshold = CW_REC_NOISE_THRESHOLD_INITIAL, /* TODO: this variable is not set in cw_rec_reset_parameters_internal(). Why is it separated from the four main variables? Is it because it is a derivative of speed? But speed is a derivative of this variable in adaptive speed mode. */ .adaptive_speed_threshold = CW_REC_SPEED_THRESHOLD_INITIAL, .parameters_in_sync = false, .label = "global rec", /* Single global receiver available in libcw library, used by legacy API. */ }; CW_STATIC_FUNC volatile cw_key_t cw_key = { .gen = NULL, .rec = &cw_receiver, .sk = { .key_value = CW_KEY_STATE_OPEN }, .ik = { .graph_state = KS_IDLE, .key_value = CW_KEY_VALUE_OPEN, .dot_paddle_value = CW_KEY_VALUE_OPEN, .dash_paddle_value = CW_KEY_VALUE_OPEN, .dot_latch = false, .dash_latch = false, .curtis_mode_b = false, .curtis_b_latch = false, .lock = false, }, .label = "global key", /* Single global key available in libcw library, used by legacy API. */ }; /* ******************************************************************** */ /* Generator */ /* ******************************************************************** */ /** \brief Create new generator Allocate memory for new generator data structure, set up default values of some of the generator's properties. The function does not start the generator (generator does not produce a sound), you have to use cw_generator_start() for this. Notice that the function doesn't return a generator variable. There is at most one generator variable at any given time. You can't have two generators. In some future version of the library the function will return pointer to newly allocated generator, and then you could have as many of them as you want, but not yet. \p audio_system can be one of following: NULL, console, OSS, ALSA, PulseAudio, soundcard. See "enum cw_audio_systems" in libcw.h for exact names of symbolic constants. \param audio_system - audio system to be used by the generator \param device - name of audio device to be used; if NULL then library will use default device. */ int cw_generator_new(int audio_system, const char *device) { cw_gen_config_t gen_conf = { .sound_system = audio_system }; if (NULL != device) { snprintf(gen_conf.sound_device, sizeof (gen_conf.sound_device), "%s", device); } return cw_generator_new_internal(&gen_conf); } /** \brief Create new generator from given config Allocate memory for new generator data structure, set up default values of some of the generator's properties. The function does not start the generator (generator does not produce a sound), you have to use cw_generator_start() for this. Notice that the function doesn't return a generator variable. There is at most one generator variable at any given time. You can't have two generators. In some future version of the library the function will return pointer to newly allocated generator, and then you could have as many of them as you want, but not yet. \param gen_conf configuration for generator */ int cw_generator_new_internal(const cw_gen_config_t * gen_conf) { cw_generator = cw_gen_new(gen_conf); if (NULL == cw_generator) { cw_debug_msg ((&cw_debug_object), CW_DEBUG_STDLIB, CW_DEBUG_ERROR, "libcw: can't create generator"); return CW_FAILURE; } else { cw_gen_set_label(cw_generator, "global gen"); /* Single global generator available in libcw library, used by legacy API. */ /* For some (all?) applications a key needs to have some generator associated with it. */ cw_key_register_generator(&cw_key, cw_generator); return CW_SUCCESS; } } /** \brief Deallocate generator Deallocate/destroy generator data structure created with call to cw_generator_new(). You can't start nor use the generator after the call to this function. */ void cw_generator_delete(void) { cw_gen_delete(&cw_generator); return; } /** \brief Start a generator Start producing tones using generator created with cw_generator_new(). The source of tones is a tone queue associated with the generator. If the tone queue is empty, the generator will wait for new tones to be queued. \return CW_FAILURE on errors \return CW_SUCCESS on success */ int cw_generator_start(void) { return cw_gen_start(cw_generator); } /** \brief Shut down a generator Silence tone generated by generator (level of generated sine wave is set to zero, with falling slope), and shut the generator down. The shutdown does not erase generator's configuration. If you want to have this generator running again, you have to call cw_generator_start(). */ void cw_generator_stop(void) { cw_gen_stop(cw_generator); return; } /** \brief Delete a generator - wrapper used in libcw_utils.c */ void cw_generator_delete_internal(void) { if (cw_generator) { cw_gen_delete(&cw_generator); } return; } /** \brief Set sending speed of generator See libcw.h/CW_SPEED_{INITIAL|MIN|MAX} for initial/minimal/maximal value of send speed. errno is set to EINVAL if \p new_value is out of range. \param new_value - new value of send speed to be assigned to generator \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_set_send_speed(int new_value) { int rv = cw_gen_set_speed(cw_generator, new_value); return rv; } /** \brief Set frequency of generator Set frequency of sound wave generated by generator. The frequency must be within limits marked by CW_FREQUENCY_MIN and CW_FREQUENCY_MAX. See libcw.h/CW_FREQUENCY_{INITIAL|MIN|MAX} for initial/minimal/maximal value of frequency. errno is set to EINVAL if \p new_value is out of range. \param new_value - new value of frequency to be assigned to generator \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_set_frequency(int new_value) { int rv = cw_gen_set_frequency(cw_generator, new_value); return rv; } /** \brief Set volume of generator Set volume of sound wave generated by generator. The volume must be within limits marked by CW_VOLUME_MIN and CW_VOLUME_MAX. Note that volume settings are not fully possible for the console speaker. In this case, volume settings greater than zero indicate console speaker sound is on, and setting volume to zero will turn off console speaker sound. See libcw.h/CW_VOLUME_{INITIAL|MIN|MAX} for initial/minimal/maximal value of volume. errno is set to EINVAL if \p new_value is out of range. \param new_value - new value of volume to be assigned to generator \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_set_volume(int new_value) { int rv = cw_gen_set_volume(cw_generator, new_value); return rv; } /** \brief Set sending gap of generator See libcw.h/CW_GAP_{INITIAL|MIN|MAX} for initial/minimal/maximal value of gap. errno is set to EINVAL if \p new_value is out of range. Notice that this function also sets the same gap value for library's receiver. \param new_value - new value of gap to be assigned to generator \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_set_gap(int new_value) { int rv = cw_gen_set_gap(cw_generator, new_value); if (rv != CW_FAILURE) { /* Ideally generator and receiver should have their own, separate cw_set_gap() functions. Unfortunately this is not the case so gap should be set here for receiver as well. */ rv = cw_rec_set_gap(&cw_receiver, new_value); } return rv; } /** \brief Set sending weighting for generator See libcw.h/CW_WEIGHTING_{INITIAL|MIN|MAX} for initial/minimal/maximal value of weighting. errno is set to EINVAL if \p new_value is out of range. \param new_value - new value of weighting to be assigned for generator \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_set_weighting(int new_value) { int rv = cw_gen_set_weighting(cw_generator, new_value); return rv; } /** \brief Get sending speed from generator \return current value of the generator's send speed */ int cw_get_send_speed(void) { return cw_gen_get_speed(cw_generator); } /** \brief Get frequency from generator Function returns "frequency" parameter of generator, even if the generator is stopped, or volume of generated sound is zero. \return current value of generator's frequency */ int cw_get_frequency(void) { return cw_gen_get_frequency(cw_generator); } /** \brief Get sound volume from generator Function returns "volume" parameter of generator, even if the generator is stopped. \return current value of generator's sound volume */ int cw_get_volume(void) { return cw_gen_get_volume(cw_generator); } /** \brief Get sending gap from generator \return current value of generator's sending gap */ int cw_get_gap(void) { return cw_gen_get_gap(cw_generator); } /** \brief Get sending weighting from generator \return current value of generator's sending weighting */ int cw_get_weighting(void) { return cw_gen_get_weighting(cw_generator); } /** \brief Get timing parameters for sending Return the low-level timing parameters calculated from the speed, gap, tolerance, and weighting set. Parameter values are returned in microseconds. Use NULL for the pointer argument to any parameter value not required. \param dot_usecs \param dash_usecs \param end_of_element_usecs \param end_of_character_usecs \param end_of_word_usecs \param additional_usecs \param adjustment_usecs */ void cw_get_send_parameters(int *dot_usecs, int *dash_usecs, int *end_of_element_usecs, int *end_of_character_usecs, int *end_of_word_usecs, int *additional_usecs, int *adjustment_usecs) { cw_gen_get_timing_parameters_internal(cw_generator, dot_usecs, dash_usecs, end_of_element_usecs, end_of_character_usecs, end_of_word_usecs, additional_usecs, adjustment_usecs); return; } /** \brief Low-level primitive for sending a Dot Mark Low-level primitive function able to play/send single Dot Mark. The function appends to a tone queue a normal inter-mark-space after the Dot Mark. \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_send_dot(void) { const bool is_first_mark = false; /* cw_send_dot() doesn't accept 'is first mark' argument, so we have to assume that it's not a first mark. */ return cw_gen_enqueue_mark_internal(cw_generator, CW_DOT_REPRESENTATION, is_first_mark); } /** \brief Low-level primitive for sending a Dash Mark Low-level primitive function able to play/send single Dash Mark. The function appends to a tone queue a normal inter-mark-space after the Dash Mark. \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_send_dash(void) { const bool is_first_mark = false; /* cw_send_dash() doesn't accept 'is first mark' argument, so we have to assume that it's not a first mark. */ return cw_gen_enqueue_mark_internal(cw_generator, CW_DASH_REPRESENTATION, is_first_mark); } /** The function enqueues space of specific length: together with previously implicitly enqueued inter-mark-space of size 1 Unit, enqueueing this space will form a full inter-character-space of size 3 Units. \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_send_character_space(void) { return cw_gen_enqueue_2u_ics_internal(cw_generator); } /** The function enqueues space of specific length: together with previously implicitly enqueued inter-mark-space of size 1 Unit and implicitly or explicitly enqueued inter-character-space, enqueueing this space will form a full inter-word-space of size 7 Units. \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_send_word_space(void) { return cw_gen_enqueue_iws_internal(cw_generator); } /** \brief Check, then send the given string as dots and dashes. The representation passed in is assumed to be a complete Morse character; that is, all post-character delays will be added when the character is sent. On success, the routine returns CW_SUCCESS. On failure, it returns CW_FAILURE, with errno set to EINVAL if any character of the representation is invalid, EBUSY if the sound card, console speaker, or keying system is busy, or EAGAIN if the tone queue is full, or if there is insufficient space to queue the tones or the representation. \param representation - representation to send \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_send_representation(const char *representation) { return cw_gen_enqueue_representation(cw_generator, representation); } /** \brief Check, then send the given string as dots and dashes The \p representation passed in is assumed to be only part of a larger Morse representation; that is, no post-character delays will be added when the character is sent. On success, the routine returns CW_SUCCESS. On failure, it returns CW_FAILURE, with errno set to EINVAL if any character of the representation is invalid, EBUSY if the sound card, console speaker, or keying system is busy, or EAGAIN if the tone queue is full, or if there is insufficient space to queue the tones for the representation. */ int cw_send_representation_partial(const char *representation) { return cw_gen_enqueue_representation_no_ics(cw_generator, representation); } /** \brief Look up and send a given ASCII character as Morse The end of character delay is appended to the Morse sent. On success the routine returns CW_SUCCESS. On failure the function returns CW_FAILURE and sets errno. errno is set to ENOENT if the given character \p c is not a valid Morse character. errno is set to EBUSY if current audio sink or keying system is busy. errno is set to EAGAIN if the generator's tone queue is full, or if there is insufficient space to queue the tones for the character. This routine returns as soon as the character has been successfully queued for sending; that is, almost immediately. The actual sending happens in background processing. See cw_wait_for_tone() and cw_wait_for_tone_queue() for ways to check the progress of sending. \param c - character to send \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_send_character(char c) { return cw_gen_enqueue_character(cw_generator, c); } /** \brief Look up and send a given ASCII character as Morse code "partial" means that the "end of character" delay is not appended to the Morse code sent by the function, to support the formation of combination characters. On success the function returns CW_SUCCESS. On failure the function returns CW_FAILURE and sets errno. errno is set to ENOENT if the given character \p c is not a valid Morse character. errno is set to EBUSY if the audio sink or keying system is busy. errno is set to EAGAIN if the tone queue is full, or if there is insufficient space to queue the tones for the character. This routine queues its arguments for background processing. See cw_wait_for_tone() and cw_wait_for_tone_queue() for ways to check the progress of sending. \param c - character to send \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_send_character_partial(char c) { return cw_gen_enqueue_character_no_ics(cw_generator, c); } /** \brief Send a given ASCII string in Morse code errno is set to ENOENT if any character in the string is not a valid Morse character. errno is set to EBUSY if audio sink or keying system is busy. errno is set to EAGAIN if the tone queue is full or if the tone queue runs out of space part way through queueing the string. However, an indeterminate number of the characters from the string will have already been queued. For safety, clients can ensure the tone queue is empty before queueing a string, or use cw_send_character() if they need finer control. This routine queues its arguments for background processing, the actual sending happens in background processing. See cw_wait_for_tone() and cw_wait_for_tone_queue() for ways to check the progress of sending. \param string - string to send \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_send_string(const char *string) { return cw_gen_enqueue_string(cw_generator, string); } /** \brief Reset send/receive parameters Reset the library speed, frequency, volume, gap, tolerance, weighting, adaptive receive, and noise spike threshold to their initial default values: send/receive speed 12 WPM, volume 70 %, frequency 800 Hz, gap 0 dots, tolerance 50 %, and weighting 50 %. */ void cw_reset_send_receive_parameters(void) { cw_gen_reset_parameters_internal(cw_generator); cw_rec_reset_parameters_internal(&cw_receiver); /* Reset requires resynchronization. */ cw_gen_sync_parameters_internal(cw_generator); cw_rec_sync_parameters_internal(&cw_receiver); return; } /** \brief Return char string with console device path Returned pointer is owned by library. \return char string with current console device path */ const char *cw_get_console_device(void) { return cw_generator->picked_device_name; } /** \brief Return char string with soundcard device name/path Returned pointer is owned by library. \return char string with current soundcard device name or device path */ const char *cw_get_soundcard_device(void) { return cw_generator->picked_device_name; } /** \brief Get a readable label of current audio system The function returns one of following strings: None, Null, Console, OSS, ALSA, PulseAudio, Soundcard \return audio system's label */ const char *cw_generator_get_audio_system_label(void) { return cw_get_audio_system_label(cw_generator->sound_system); } /** \brief Remove last character from queue of already enqueued characters If the character is not actually played by sound sink yet, library may be able to remove the character. The character's Dots and Dashes won't be played. This function may be useful if user presses backspace in UI to remove/undo a character. If your application doesn't enqueue whole characters or strings of characters but is using low-level cw_send_dot()/cw_send_dash() functions to enqueue individual Marks, don't use this function. This function won't be able to recognize whole characters and is likely to remove more tones than expected. \return CW_SUCCESS if function managed to remove a last character before it has been played \return CW_FAILURE otherwise */ int cw_generator_remove_last_character(void) { return cw_gen_remove_last_character(cw_generator); } /* ******************************************************************** */ /* Tone queue */ /* ******************************************************************** */ /** \brief Register callback for low queue state Register a function to be called automatically by the dequeue routine whenever the count of tones in tone queue falls to a given @p level. To be more precise: the callback is called by queue's dequeue function if, after dequeueing a tone, the function notices that tone queue length has become equal or less than \p level. \p level can't be negative. \p level can't be equal to or larger than tone queue capacity. If \p level is zero, the behaviour of the mechanism is not guaranteed to work correctly. If \p callback_func is NULL then the mechanism becomes disabled. \p callback_arg will be passed to \p callback_func. errno is set to EINVAL when \p level is invalid. \param callback_func - callback function to be registered \param callback_arg - argument for callback_func to pass return value \param level - low level of queue triggering callback call \return CW_SUCCESS on successful registration \return CW_FAILURE on failure */ int cw_register_tone_queue_low_callback(void (*callback_func)(void*), void *callback_arg, int level) { if (level < 0) { errno = EINVAL; /* cw_tq_register_low_level_callback_internal() won't recognize negative level. */ return CW_FAILURE; } return cw_tq_register_low_level_callback_internal(cw_generator->tq, callback_func, callback_arg, level); } /** \brief Check if tone sender is busy Indicate if the tone sender is busy. \return true if there are still entries in the tone queue \return false if the queue is empty */ bool cw_is_tone_busy(void) { return cw_tq_is_nonempty_internal(cw_generator->tq); } /** \brief Wait for the current tone to complete \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_wait_for_tone(void) { return cw_tq_wait_for_end_of_current_tone_internal(cw_generator->tq); } /** \brief Wait for the tone queue to drain Notice that generator must be running (started with cw_generator_start()) when this function is called, otherwise it will be waiting forever for a change of tone queue's level that will never happen. \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_wait_for_tone_queue(void) { return cw_tq_wait_for_level_internal(cw_generator->tq, 0); } /** \brief Wait for the tone queue to drain until only as many tones as given in level remain queued This routine is for use by programs that want to optimize themselves to avoid the cleanup that happens when the tone queue drains completely; such programs have a short time in which to add more tones to the queue. Notice that generator must be running (started with cw_generator_start()) when this function is called, otherwise it will be waiting forever for a change of tone queue's level that will never happen. If \p level is negative, function sets errno to EINVAL and returns CW_FAILURE. \param level - low level in queue, at which to return \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_wait_for_tone_queue_critical(int level) { if (level < 0) { errno = EINVAL; return CW_FAILURE; } return cw_tq_wait_for_level_internal(cw_generator->tq, (size_t) level); } /** \brief Indicate if the tone queue is full \return true if tone queue is full \return false if tone queue is not full */ bool cw_is_tone_queue_full(void) { return cw_tq_is_full_internal(cw_generator->tq); } /** \brief Return the number of entries the tone queue can accommodate */ int cw_get_tone_queue_capacity(void) { return (int) cw_tq_capacity_internal(cw_generator->tq); } /** \brief Return the number of entries currently pending in the tone queue */ int cw_get_tone_queue_length(void) { return (int) cw_tq_length_internal(cw_generator->tq); } /** \brief Cancel all pending queued tones, and return to silence. If there is a tone in progress, the function will wait until this last one has completed, then silence the tones. */ void cw_flush_tone_queue(void) { /* This function locks and unlocks mutex. */ cw_tq_flush_internal(cw_generator->tq); /* Force silence on the speaker anyway, and stop any background soundcard tone generation. */ cw_gen_silence_internal(cw_generator); //cw_finalization_schedule_internal(); return; } /** Cancel all pending queued tones, reset any queue low callback registered, and return to silence. This function is suitable for calling from an application exit handler. */ void cw_reset_tone_queue(void) { cw_tq_flush_internal(cw_generator->tq); /* Silence sound and stop any background soundcard tone generation. */ cw_gen_silence_internal(cw_generator); //cw_finalization_schedule_internal(); cw_debug_msg ((&cw_debug_object), CW_DEBUG_TONE_QUEUE, CW_DEBUG_INFO, "libcw: tone queue: reset"); return; } /** \brief Primitive access to simple tone generation This routine queues a tone of given duration and frequency. The routine returns CW_SUCCESS on success. If usec or frequency are invalid, it returns CW_FAILURE with errno set to EINVAL. If the sound card, console speaker, or keying function are busy, it returns CW_FAILURE with errno set to EBUSY. If the tone queue is full, it returns false with errno set to EAGAIN. \param usecs - duration of queued tone, in microseconds \param frequency - frequency of queued tone \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_queue_tone(int usecs, int frequency) { /* Check the arguments given for realistic values. This test is left here for legacy reasons. Don't change it. */ if (usecs < 0 || frequency < CW_FREQUENCY_MIN || frequency > CW_FREQUENCY_MAX) { errno = EINVAL; return CW_FAILURE; } cw_tone_t tone; CW_TONE_INIT(&tone, frequency, usecs, CW_SLOPE_MODE_STANDARD_SLOPES); int rv = cw_tq_enqueue_internal(cw_generator->tq, &tone); return rv; } /* ******************************************************************** */ /* Receiver */ /* ******************************************************************** */ /** \brief Set receiving speed of receiver See documentation of cw_set_send_speed() for more information. See libcw.h/CW_SPEED_{INITIAL|MIN|MAX} for initial/minimal/maximal value of receive speed. errno is set to EINVAL if \p new_value is out of range. errno is set to EPERM if adaptive receive speed tracking is enabled. \param new_value - new value of receive speed to be assigned to receiver \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_set_receive_speed(int new_value) { return cw_rec_set_speed(&cw_receiver, new_value); } /** \brief Get receiving speed from receiver \return current value of the receiver's receive speed */ int cw_get_receive_speed(void) { return (int) cw_rec_get_speed(&cw_receiver); } /** \brief Set tolerance for receiver See libcw.h/CW_TOLERANCE_{INITIAL|MIN|MAX} for initial/minimal/maximal value of tolerance. errno is set to EINVAL if \p new_value is out of range. \param new_value - new value of tolerance to be assigned to receiver \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_set_tolerance(int new_value) { return cw_rec_set_tolerance(&cw_receiver, new_value); } /** \brief Get tolerance from receiver \return current value of receiver's tolerance */ int cw_get_tolerance(void) { return cw_rec_get_tolerance(&cw_receiver); } /** \brief Get timing parameters for receiving, and adaptive threshold Return the low-level timing parameters calculated from the speed, gap, tolerance, and weighting set. Parameter values are returned in microseconds. Use NULL for the pointer argument to any parameter value not required. \param dot_usecs \param dash_usecs \param dot_min_usecs \param dot_max_usecs \param dash_min_usecs \param dash_max_usecs \param end_of_element_min_usecs \param end_of_element_max_usecs \param end_of_element_ideal_usecs \param end_of_character_min_usecs \param end_of_character_max_usecs \param end_of_character_ideal_usecs \param adaptive_threshold */ void cw_get_receive_parameters(int *dot_usecs, int *dash_usecs, int *dot_min_usecs, int *dot_max_usecs, int *dash_min_usecs, int *dash_max_usecs, int *end_of_element_min_usecs, int *end_of_element_max_usecs, int *end_of_element_ideal_usecs, int *end_of_character_min_usecs, int *end_of_character_max_usecs, int *end_of_character_ideal_usecs, int *adaptive_threshold) { cw_rec_get_parameters_internal(&cw_receiver, dot_usecs, dash_usecs, dot_min_usecs, dot_max_usecs, dash_min_usecs, dash_max_usecs, end_of_element_min_usecs, end_of_element_max_usecs, end_of_element_ideal_usecs, end_of_character_min_usecs, end_of_character_max_usecs, end_of_character_ideal_usecs, adaptive_threshold); return; } /** \brief Set noise spike threshold for receiver Set the period shorter than which, on receive, received marks are ignored. This allows the "receive mark" functions to apply noise canceling for very short apparent marks. For useful results the value should never exceed the dot length of a dot at maximum speed: 20000 microseconds (the dot length at 60WPM). Setting a noise threshold of zero turns off receive mark noise canceling. The default noise spike threshold is 10000 microseconds. errno is set to EINVAL if \p new_value is out of range. \param new_value - new value of noise spike threshold to be assigned to receiver \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_set_noise_spike_threshold(int new_value) { return cw_rec_set_noise_spike_threshold(&cw_receiver, new_value); } /** \brief Get noise spike threshold from receiver See documentation of cw_set_noise_spike_threshold() for more information \return current value of receiver's threshold */ int cw_get_noise_spike_threshold(void) { return cw_rec_get_noise_spike_threshold(&cw_receiver); } /** \brief Calculate and return receiver's timing statistics These statistics may be used to obtain a measure of the accuracy of received CW. The values \p dot_sd and \p dash_sd contain the standard deviation of dot and dash lengths from the ideal values, and \p element_end_sd and \p character_end_sd the deviations for inter element and inter character spacing. Statistics are held for all timings in a 256 element circular buffer. If any statistic cannot be calculated, because no records for it exist, the returned value is 0.0. Use NULL for the pointer argument to any statistic not required. \param dot_sd \param dash_sd \param element_end_sd \param character_end_sd */ void cw_get_receive_statistics(double *dot_sd, double *dash_sd, double *element_end_sd, double *character_end_sd) { float dot_sd_f = 0.0F; float dash_sd_f = 0.0F; float element_end_sd_f = 0.0F; float character_end_sd_f = 0.0F; cw_rec_get_statistics_internal(&cw_receiver, &dot_sd_f, &dash_sd_f, &element_end_sd_f, &character_end_sd_f); *dot_sd = (double) dot_sd_f; *dash_sd = (double) dash_sd_f; *element_end_sd = (double) element_end_sd_f; *character_end_sd = (double) character_end_sd_f; return; } /** \brief Clear the receive statistics buffer Clear the receive statistics buffer by removing all records from it and returning it to its initial default state. */ void cw_reset_receive_statistics(void) { cw_rec_reset_statistics(&cw_receiver); return; } /** \brief Enable adaptive receive speed tracking If adaptive speed tracking is enabled, the receive functions will attempt to automatically adjust the receive speed setting to match the speed of the incoming Morse code. If it is disabled, the receive functions will use fixed speed settings, and reject incoming Morse which is not at the expected speed. Adaptive speed tracking uses a moving average length of the past N marks as its baseline for tracking speeds. The default state is adaptive speed tracking disabled. */ void cw_enable_adaptive_receive(void) { cw_rec_set_adaptive_mode_internal(&cw_receiver, true); return; } /** \brief Disable adaptive receive speed tracking See documentation of cw_enable_adaptive_receive() for more information */ void cw_disable_adaptive_receive(void) { cw_rec_set_adaptive_mode_internal(&cw_receiver, false); return; } /** \brief Get adaptive receive speed tracking flag The function returns state of "adaptive receive enabled" flag. See documentation of cw_enable_adaptive_receive() for more information \return true if adaptive speed tracking is enabled \return false otherwise */ bool cw_get_adaptive_receive_state(void) { return cw_rec_get_adaptive_mode(&cw_receiver); } /** \brief Signal beginning of receive mark Called on the start of a receive mark. If the \p timestamp is NULL, the current timestamp is used as beginning of mark. The function should be called by client application when pressing a key down (closing a circuit) has been detected by client application. On error the function returns CW_FAILURE, with errno set to ERANGE if the call is directly after another cw_start_receive_tone() call or if an existing received character has not been cleared from the buffer, or EINVAL if the timestamp passed in is invalid. \param timestamp - time stamp of "key down" event \return CW_SUCCESS on success \return CW_FAILURE otherwise (with errno set) */ int cw_start_receive_tone(const struct timeval *timestamp) { return cw_rec_mark_begin(&cw_receiver, timestamp); } /** \brief Signal end of mark The function should be called by client application when releasing a key (opening a circuit) has been detected by client application. If the \p timestamp is NULL, the current time is used as timestamp of end of mark. On success, the routine adds a dot or dash to the receiver's representation buffer, and returns CW_SUCCESS. On failure, it returns CW_FAIURE, with errno set to: ERANGE if the call was not preceded by a cw_start_receive_tone() call, EINVAL if the timestamp passed in is not valid, ENOENT if function can't tell from duration of the Mark if it's Dot or Dash, ENOMEM if the receiver's representation buffer is full, EAGAIN if the mark was shorter than the threshold for noise and was therefore ignored. \param timestamp - time stamp of "key up" event \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_end_receive_tone(const struct timeval *timestamp) { return cw_rec_mark_end(&cw_receiver, timestamp); } /** \brief Add a dot to the receiver's representation buffer Documentation for both cw_receive_buffer_dot() and cw_receive_buffer_dash(): Since we can't add a mark to the buffer without any accompanying timing information, the functions accepts \p timestamp of the "end of mark" event. If the \p timestamp is NULL, the current timestamp is used. These routines are for client code that has already determined whether a dot or dash was received by a method other than calling the routines cw_start_receive_tone() and cw_end_receive_tone(). On success, the relevant mark is added to the receiver's representation buffer. On failure, the routines return CW_FAILURE and set errno: ERANGE if preceded by a cw_start_receive_tone() call with no matching cw_end_receive_tone() or if an error condition currently exists within the receiver's buffer, EINVAL if the timestamp passed in is not valid, ENOMEM if the receiver's representation buffer is full. \param timestamp - timestamp of "end of dot" event \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_receive_buffer_dot(const struct timeval *timestamp) { return cw_rec_add_mark(&cw_receiver, timestamp, CW_DOT_REPRESENTATION); } /** \brief Add a dash to the receiver's representation buffer See documentation of cw_receive_buffer_dot() for more information. \param timestamp - timestamp of "end of dash" event \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_receive_buffer_dash(const struct timeval *timestamp) { return cw_rec_add_mark(&cw_receiver, timestamp, CW_DASH_REPRESENTATION); } /** \brief Get the current buffered representation from the receiver's representation buffer On success the function fills in \p representation with the contents of the current representation buffer and returns CW_SUCCESS. On failure, it returns CW_FAILURE and sets errno to: ERANGE if not preceded by a cw_end_receive_tone() call, a prior successful cw_receive_representation call, or a prior cw_receive_buffer_dot or cw_receive_buffer_dash, EINVAL if the timestamp passed in is invalid, EAGAIN if the call is made too early to determine whether a complete representation has yet been placed in the buffer (that is, less than the inter-character-space duration elapsed since the last cw_end_receive_tone() or cw_receive_buffer_dot/dash call). This is not a *hard* error, just an information that the caller should try to get the representation later. \p is_end_of_word indicates that the space after the last mark received is longer that the inter-character-space, so it must be qualified as inter-word-space. \p is_error indicates that the representation was terminated by an error condition. The function is called periodically (poll()-like function) by client code in hope that at some attempt receiver will be ready to pass \p representation. The attempt succeeds only if data stream is in "space" state. To mark end of the space, client code has to provide a timestamp (or pass NULL timestamp, the function will get time stamp at function call). The receiver needs to know the "end of space" event - thus the \p timestamp parameter. \param timestamp - timestamp of event that ends inter-character-space or inter-word-space \param representation - buffer for representation (output parameter) \param is_end_of_word - buffer for "is end of word" state (output parameter) \param is_error - buffer for "error" state (output parameter) \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_receive_representation(const struct timeval *timestamp, /* out */ char *representation, /* out */ bool *is_end_of_word, /* out */ bool *is_error) { int rv = cw_rec_poll_representation(&cw_receiver, timestamp, representation, is_end_of_word, is_error); return rv; } /** \brief Get a current character Function returns the character currently stored in receiver's representation buffer. On success the function returns CW_SUCCESS, and fills \p c with the contents of the current representation buffer, translated into a character. On failure the function returns CW_FAILURE, with errno set to: ERANGE if not preceded by a cw_end_receive_tone() call, a prior successful cw_receive_character() call, or a cw_receive_buffer_dot() or cw_receive_buffer_dash() call, EINVAL if the timestamp passed in is invalid, or EAGAIN if the call is made too early to determine whether a complete character has yet been placed in the buffer (that is, less than the inter-character-space duration elapsed since the last cw_end_receive_tone() or cw_receive_buffer_dot/dash call). ENOENT if character stored in receiver cannot be recognized as valid \p is_end_of_word indicates that the space after the last mark received is longer that the inter-character-space, so it must be qualified as inter-word-space. \p is_error indicates that the character was terminated by an error condition. \param timestamp - timestamp of event that ends inter-character-space or inter-word-space \param c - buffer for character (output parameter) \param is_end_of_word - buffer for "is end of word" state (output parameter) \param is_error - buffer for "error" state (output parameter) \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_receive_character(const struct timeval *timestamp, /* out */ char *c, /* out */ bool *is_end_of_word, /* out */ bool *is_error) { int rv = cw_rec_poll_character(&cw_receiver, timestamp, c, is_end_of_word, is_error); return rv; } /** \brief Clear receiver's representation buffer Clears the receiver's representation buffer, resets receiver's internal state. This prepares the receiver to receive marks and spaces again. This routine must be called after successful, or terminating, cw_receive_representation() or cw_receive_character() calls, to clear the states and prepare the buffer to receive more marks and spaces. When migrating to libcw2.h, use cw_rec_reset_state() instead of this function. */ void cw_clear_receive_buffer(void) { /* In 3.5.1 this was implemented by cw_rec_clear_buffer_internal() like this: */ memset(cw_receiver.representation, 0, sizeof (cw_receiver.representation)); cw_receiver.representation_ind = 0; cw_rec_set_state_internal(&cw_receiver, RS_IDLE); return; } /** \brief Get the number of elements (dots/dashes) the receiver's buffer can accommodate The maximum number of elements written out by cw_receive_representation() is the capacity + 1, the extra character being used for the terminating NUL. \return number of elements that can be stored in receiver's representation buffer */ int cw_get_receive_buffer_capacity(void) { return CW_REC_REPRESENTATION_CAPACITY; } /** \brief Get the number of elements (dots/dashes) currently pending in the cw_receiver's representation buffer \return number of elements in receiver's representation buffer */ int cw_get_receive_buffer_length(void) { return cw_rec_get_buffer_length_internal(&cw_receiver); } /** \brief Clear receive data Clear the receiver's representation buffer, statistics, and any retained receiver's state. This function is suitable for calling from an application exit handler. When migrating to libcw2.h, use cw_rec_reset_state() and cw_rec_reset_statistics() instead of this function. */ void cw_reset_receive(void) { /* In 3.5.1 this was implemented by cw_rec_reset_internal() like this: */ memset(cw_receiver.representation, 0, sizeof (cw_receiver.representation)); cw_receiver.representation_ind = 0; cw_rec_set_state_internal(&cw_receiver, RS_IDLE); cw_rec_reset_statistics(&cw_receiver); return; } /* ******************************************************************** */ /* Key */ /* ******************************************************************** */ /** \brief Register external callback function for keying Register a \p callback_func function that should be called when a state of a key changes from "key open" to "key closed", or vice-versa. The first argument passed to the registered callback function is the supplied \p callback_arg, if any. The second argument passed to registered callback function is the key state: CW_KEY_STATE_CLOSED (one/true) for "key closed", and CW_KEY_STATE_OPEN (zero/false) for "key open". Calling this routine with a NULL function address disables keying callbacks. Any callback supplied will be called in signal handler context (??). \param callback_func - callback function to be called on key state changes \param callback_arg - first argument to callback_func */ void cw_register_keying_callback(void (*callback_func)(void*, int), void *callback_arg) { cw_gen_register_value_tracking_callback_internal(cw_generator, callback_func, callback_arg); return; } /* Most of the time libcw just passes around key_callback_arg, not caring of what type it is, and not attempting to do any operations on it. On one occasion however, it needs to know whether key_callback_arg is of type 'struct timeval', and if so, it must do some operation on it. I could pass struct with ID as key_callback_arg, but that may break some old client code. Instead I've created this function that has only one, very specific purpose: to pass to libcw a pointer to timer. The timer is owned by client code, and is used to measure and clock iambic keyer. */ void cw_iambic_keyer_register_timer(struct timeval *timer) { cw_key_ik_register_timer_internal(&cw_key, timer); return; } /** \brief Enable iambic Curtis mode B Normally, the iambic keying functions will emulate Curtis 8044 Keyer mode A. In this mode, when both paddles are pressed together, the last dot or dash being sent on release is completed, and nothing else is sent. In mode B, when both paddles are pressed together, the last dot or dash being sent on release is completed, then an opposite element is also sent. Some operators prefer mode B, but timing is more critical in this mode. The default mode is Curtis mode A. */ void cw_enable_iambic_curtis_mode_b(void) { cw_key_ik_enable_curtis_mode_b(&cw_key); return; } /** See documentation of cw_enable_iambic_curtis_mode_b() for more information */ void cw_disable_iambic_curtis_mode_b(void) { cw_key_ik_disable_curtis_mode_b(&cw_key); return; } /** See documentation of cw_enable_iambic_curtis_mode_b() for more information */ int cw_get_iambic_curtis_mode_b_state(void) { return (int) cw_key_ik_get_curtis_mode_b(&cw_key); } /** \brief Inform about changed state of iambic keyer's paddles Function informs the library that the iambic keyer paddles have changed state. The new paddle states are recorded, and if either transition from false to true, paddle latches, for iambic functions, are also set. On success, the routine returns CW_SUCCESS. On failure, it returns CW_FAILURE, with errno set to EBUSY if the tone queue or straight key are using the sound card, console speaker, or keying system. If appropriate, this routine starts the keyer functions sending the relevant element. Element send and timing occurs in the background, so this routine returns almost immediately. \param dot_paddle_state \param dash_paddle_state \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_notify_keyer_paddle_event(int dot_paddle_state, int dash_paddle_state) { return cw_key_ik_notify_paddle_event(&cw_key, dot_paddle_state, dash_paddle_state); } /** \brief Change state of dot paddle Alter the state of just one of the two iambic keyer paddles. The other paddle state of the paddle pair remains unchanged. See cw_notify_keyer_paddle_event() for details of iambic keyer background processing, and how to check its status. \param dot_paddle_state */ int cw_notify_keyer_dot_paddle_event(int dot_paddle_state) { return cw_notify_keyer_paddle_event(dot_paddle_state, cw_key.ik.dash_paddle_value); } /** See documentation of cw_notify_keyer_dot_paddle_event() for more information */ int cw_notify_keyer_dash_paddle_event(int dash_paddle_state) { return cw_notify_keyer_paddle_event(cw_key.ik.dot_paddle_value, dash_paddle_state); } /** \brief Get the current saved states of the two paddles \param dot_paddle_state \param dash_paddle_state */ void cw_get_keyer_paddles(int *dot_paddle_state, int *dash_paddle_state) { cw_key_ik_get_paddles(&cw_key, (cw_key_value_t *) dot_paddle_state, (cw_key_value_t *) dash_paddle_state); return; } /** \brief Get the current states of paddle latches Function returns the current saved states of the two paddle latches. A paddle latches is set to true when the paddle state becomes true, and is cleared if the paddle state is false when the element finishes sending. \param dot_paddle_latch_state \param dash_paddle_latch_state */ void cw_get_keyer_paddle_latches(int *dot_paddle_latch_state, int *dash_paddle_latch_state) { cw_key_ik_get_paddle_latches_internal(&cw_key, dot_paddle_latch_state, dash_paddle_latch_state); return; } /** \brief Check if a keyer is busy \return true if keyer is busy \return false if keyer is not busy */ bool cw_is_keyer_busy(void) { return cw_key_ik_is_busy_internal(&cw_key); } /** \brief Wait for end of element from the keyer Waits until the end of the current element, dot or dash, from the keyer. \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_wait_for_keyer_element(void) { /* TODO: update function description: errno and return values. */ return cw_key_ik_wait_for_end_of_current_element(&cw_key); } /** \brief Wait for the current keyer cycle to complete \return CW_SUCCESS on success \return CW_FAILURE on failure */ int cw_wait_for_keyer(void) { return cw_key_ik_wait_for_keyer(&cw_key); } /** \brief Reset iambic keyer data Clear all latches and paddle states of iambic keyer, return to Curtis 8044 Keyer mode A, and return to silence. This function is suitable for calling from an application exit handler. */ void cw_reset_keyer(void) { cw_key_ik_reset_internal(&cw_key); return; } #if 0 /* unused */ /* Period of constant tone generation after which we need another timeout, to ensure that the soundcard doesn't run out of data. */ static const int STRAIGHT_KEY_TIMEOUT = 500000; /** \brief Generate a tone while straight key is down Soundcard tone data is only buffered to last about a second on each cw_generate_sound_internal() call, and holding down the straight key for longer than this could cause a soundcard data underrun. To guard against this, a timeout is generated every half-second or so while the straight key is down. The timeout generates a chunk of sound data for the soundcard. */ void cw_straight_key_clock_internal(void) { if (cw_straight_key->key_value == CW_KEY_STATE_CLOSED) { /* Generate a quantum of tone data, and request another timeout. */ // cw_generate_sound_internal(); cw_timer_run_with_handler_internal(STRAIGHT_KEY_TIMEOUT, NULL); } return; } #endif /** \brief Inform the library that the straight key has changed state This routine returns CW_SUCCESS on success. On error, it returns CW_FAILURE, with errno set to EBUSY if the tone queue or iambic keyer are using the sound card, console speaker, or keying control system. If \p key_state indicates no change of state, the call is ignored. \p key_state may be either CW_KEY_STATE_OPEN (false) or CW_KEY_STATE_CLOSED (true). \param key_state - state of straight key */ int cw_notify_straight_key_event(int key_state) { return cw_key_sk_set_value(&cw_key, key_state); } /** \brief Get saved state of straight key Returns the current saved state of the straight key. \return CW_KEY_STATE_CLOSED (true) if the key is down \return CW_KEY_STATE_OPEN (false) if the key up */ int cw_get_straight_key_state(void) { cw_key_value_t key_value = CW_KEY_VALUE_OPEN; cw_key_sk_get_value(&cw_key, &key_value); return (int) key_value; } /** \brief Check if the straight key is busy This routine is just a pseudonym for cw_get_straight_key_state(), and exists to fill a hole in the API naming conventions. \return true if the straight key is busy \return false if the straight key is not busy */ bool cw_is_straight_key_busy(void) { return CW_KEY_STATE_CLOSED == cw_get_straight_key_state(); } /** \brief Clear the straight key state, and return to silence This function is suitable for calling from an application exit handler. */ void cw_reset_straight_key(void) { cw_key_sk_reset_internal(&cw_key); return; } unixcw-3.6.0/src/libcw/tests/0000755000175000017500000000000014001105773013065 500000000000000unixcw-3.6.0/src/libcw/tests/libcw_key_tests.h0000644000175000017500000000216314000344554016353 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_KEY_TESTS_H_ #define _LIBCW_KEY_TESTS_H_ #include "test_framework.h" int test_keyer(cw_test_executor_t * cte); int test_straight_key(cw_test_executor_t * cte); typedef struct test_straight_key_data_t { /* How many times to alternate between key states? */ int loops; /* For how long to stay in each state? */ int usecs; /* Values of keys, between which the test will alternate. */ cw_key_value_t values_set[2]; /* Test name, displayed in console. */ char test_name[32]; int (* legacy_set)(int key_state); int (* legacy_get)(void); bool (* legacy_is_busy)(void); cw_ret_t (* modern_set)(volatile cw_key_t * key, cw_key_value_t key_value); cw_ret_t (* modern_get)(const volatile cw_key_t * key, cw_key_value_t * key_value); } test_straight_key_data_t; #define TEST_STRAIGHT_KEY_DATA_COUNT 4 cwt_retv test_helper_test_straight_key(cw_test_executor_t * cte, volatile cw_key_t * key, test_straight_key_data_t * test_data); #endif /* #ifndef _LIBCW_KEY_TESTS_H_ */ unixcw-3.6.0/src/libcw/tests/libcw_legacy_api_tests.c0000644000175000017500000014052114000344554017654 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #if defined(HAVE_STRING_H) # include #endif #if defined(HAVE_STRINGS_H) # include #endif #include "libcw.h" #include "libcw_debug.h" #include "libcw_tq.h" #include "libcw_utils.h" #include "libcw_gen.h" #include "libcw_legacy_api_tests.h" #include "libcw_key_tests.h" /* TODO: when you do this: 1. disable the workaround 2. comment out cw_key_Xk_reset_state_internal() in libcw_gen.c 3. run './tests/libcw_tests -S p -A k' you will notice that 'legacy_api_test_iambic_key_dash' test hangs */ //#define LIBCW_KEY_TESTS_WORKAROUND extern const char * test_valid_representations[]; extern const char * test_invalid_representations[]; extern test_straight_key_data_t g_test_straight_key_data[]; extern volatile cw_key_t cw_key; static void test_helper_tq_callback(void * ptr); /* Helper function for iambic key tests. */ static void legacy_api_test_iambic_key_paddles_common(cw_test_executor_t * cte, const int intended_dot_paddle, const int intended_dash_paddle, char character, int n_elements); static int legacy_api_standalone_test_setup(cw_test_executor_t * cte, bool start_gen); static int legacy_api_standalone_test_teardown(__attribute__((unused)) cw_test_executor_t * cte); /** @brief Setup test environment for a test of legacy function @param start_gen whether a prepared generator should be started @reviewed on 2020-10-04 */ int legacy_api_standalone_test_setup(cw_test_executor_t * cte, bool start_gen) { if (CW_SUCCESS != cw_generator_new(cte->current_gen_conf.sound_system, cte->current_gen_conf.sound_device)) { cte->log_error(cte, "Can't create generator, stopping the test\n"); return cwt_retv_err; } if (start_gen) { if (CW_SUCCESS != cw_generator_start()) { cte->log_error(cte, "Can't start generator, stopping the test\n"); cw_generator_delete(); return cwt_retv_err; } } cw_reset_send_receive_parameters(); cw_set_send_speed(30); cw_set_receive_speed(30); cw_disable_adaptive_receive(); cw_reset_receive_statistics(); cw_unregister_signal_handler(SIGUSR1); errno = 0; return cwt_retv_ok; } /** @brief Deconfigure test environment after running a test of legacy function @reviewed on 2020-10-04 */ int legacy_api_standalone_test_teardown(__attribute__((unused)) cw_test_executor_t * cte) { sleep(1); cw_generator_stop(); sleep(1); cw_generator_delete(); return 0; } /** @reviewed on 2019-10-13 */ int legacy_api_test_low_level_gen_parameters(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); int txdot_usecs = -1; int txdash_usecs = -1; int end_of_element_usecs = -1; int end_of_character_usecs = -1; int end_of_word_usecs = -1; int additional_usecs = -1; int adjustment_usecs = -1; /* Print and verify default low level timing values. */ cw_reset_send_receive_parameters(); LIBCW_TEST_FUT(cw_get_send_parameters)(&txdot_usecs, &txdash_usecs, &end_of_element_usecs, &end_of_character_usecs, &end_of_word_usecs, &additional_usecs, &adjustment_usecs); cte->log_info(cte, "cw_get_send_parameters():\n" " %d, %d, %d, %d, %d, %d, %d\n", txdot_usecs, txdash_usecs, end_of_element_usecs, end_of_character_usecs,end_of_word_usecs, additional_usecs, adjustment_usecs); cte->expect_op_int(cte, txdot_usecs, ">=", 0, "send parameters: txdot_usecs"); cte->expect_op_int(cte, txdash_usecs, ">=", 0, "send parameters: txdash_usecs"); cte->expect_op_int(cte, end_of_element_usecs, ">=", 0, "send parameters: end_of_element_usecs"); cte->expect_op_int(cte, end_of_character_usecs, ">=", 0, "send parameters: end_of_character_usecs"); cte->expect_op_int(cte, end_of_word_usecs, ">=", 0, "send parameters: end_of_word_usecs"); cte->expect_op_int(cte, additional_usecs, ">=", 0, "send parameters: additional_usecs"); cte->expect_op_int(cte, adjustment_usecs, ">=", 0, "send parameters: adjustment_usecs"); legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-13 */ int legacy_api_test_parameter_ranges(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); const int off_limits = 10000; /* Test setting and getting of some basic parameters. */ struct { /* There are tree functions that take part in the test: first gets range of acceptable values, seconds sets a new value of parameter, and third reads back the value. */ void (* get_limits)(int *min, int *max); int (* set_new_value)(int new_value); int (* get_value)(void); const int expected_min; /* Expected value of minimum. */ const int expected_max; /* Expected value of maximum. */ int readback_min; /* Value returned by 'get_limits()' function. */ int readback_max; /* Value returned by 'get_limits()' function. */ const char *name; } test_data[] = { { LIBCW_TEST_FUT(cw_get_speed_limits), LIBCW_TEST_FUT(cw_set_send_speed), LIBCW_TEST_FUT(cw_get_send_speed), CW_SPEED_MIN, CW_SPEED_MAX, off_limits, -off_limits, "send_speed" }, { LIBCW_TEST_FUT(cw_get_speed_limits), LIBCW_TEST_FUT(cw_set_receive_speed), LIBCW_TEST_FUT(cw_get_receive_speed), CW_SPEED_MIN, CW_SPEED_MAX, off_limits, -off_limits, "receive_speed" }, { LIBCW_TEST_FUT(cw_get_frequency_limits), LIBCW_TEST_FUT(cw_set_frequency), LIBCW_TEST_FUT(cw_get_frequency), CW_FREQUENCY_MIN, CW_FREQUENCY_MAX, off_limits, -off_limits, "frequency" }, { LIBCW_TEST_FUT(cw_get_volume_limits), LIBCW_TEST_FUT(cw_set_volume), LIBCW_TEST_FUT(cw_get_volume), CW_VOLUME_MIN, CW_VOLUME_MAX, off_limits, -off_limits, "volume" }, { LIBCW_TEST_FUT(cw_get_gap_limits), LIBCW_TEST_FUT(cw_set_gap), LIBCW_TEST_FUT(cw_get_gap), CW_GAP_MIN, CW_GAP_MAX, off_limits, -off_limits, "gap" }, { LIBCW_TEST_FUT(cw_get_tolerance_limits), LIBCW_TEST_FUT(cw_set_tolerance), LIBCW_TEST_FUT(cw_get_tolerance), CW_TOLERANCE_MIN, CW_TOLERANCE_MAX, off_limits, -off_limits, "tolerance" }, { LIBCW_TEST_FUT(cw_get_weighting_limits), LIBCW_TEST_FUT(cw_set_weighting), LIBCW_TEST_FUT(cw_get_weighting), CW_WEIGHTING_MIN, CW_WEIGHTING_MAX, off_limits, -off_limits, "weighting" }, { NULL, NULL, NULL, 0, 0, 0, 0, NULL } }; for (int i = 0; test_data[i].get_limits; i++) { int cwret; /* Get limits of values to be tested. */ test_data[i].get_limits(&test_data[i].readback_min, &test_data[i].readback_max); cte->expect_op_int(cte, test_data[i].expected_min, "==", test_data[i].readback_min, "get %s limits: min", test_data[i].name); cte->expect_op_int(cte, test_data[i].expected_max, "==", test_data[i].readback_max, "get %s limits: min", test_data[i].name); /* Test setting out-of-range value lower than minimum. */ errno = 0; cwret = test_data[i].set_new_value(test_data[i].readback_min - 1); cte->expect_op_int(cte, EINVAL, "==", errno, "cw_set_%s(min - 1):", test_data[i].name); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "cw_set_%s(min - 1):", test_data[i].name); /* Test setting out-of-range value higher than maximum. */ errno = 0; cwret = test_data[i].set_new_value(test_data[i].readback_max + 1); cte->expect_op_int(cte, EINVAL, "==", errno, "cw_set_%s(max + 1):", test_data[i].name); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "cw_set_%s(max + 1):", test_data[i].name); /* Test setting and reading back of in-range values. There will be many, many iterations, so use ::expect_op_int(errors_only). */ bool set_within_range_cwret_failure = false; bool set_within_range_errno_failure = false; bool set_within_range_readback_failure = false; for (int value_to_set = test_data[i].readback_min; value_to_set <= test_data[i].readback_max; value_to_set++) { errno = 0; cwret = test_data[i].set_new_value(value_to_set); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "set %s within limits (cwret) (value to set = %d)", test_data[i].name, value_to_set)) { set_within_range_cwret_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, 0, "==", errno, "set %s within limits (errno) (value to set = %d)", test_data[i].name, value_to_set)) { set_within_range_errno_failure = true; break; } const int readback_value = test_data[i].get_value(); if (!cte->expect_op_int_errors_only(cte, readback_value, "==", value_to_set, "readback %s within limits (value to set = %d)", test_data[i].name, value_to_set)) { set_within_range_readback_failure = true; break; } } cte->expect_op_int(cte, false, "==", set_within_range_cwret_failure, "cw_get/set_%s() within range: cwret", test_data[i].name); cte->expect_op_int(cte, false, "==", set_within_range_errno_failure, "cw_get/set_%s() within range: errno", test_data[i].name); cte->expect_op_int(cte, false, "==", set_within_range_readback_failure, "cw_get/set_%s(): within range: readback", test_data[i].name); } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** Fill a queue and then wait for each tone separately - repeat until all tones are dequeued. @reviewed on 2019-10-13 */ int legacy_api_test_cw_wait_for_tone(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, false); int cwret; /* This is a simple test, so only a handful of tones. */ const int n_tones_to_add = 6; /* Duration long enough to not allow generator dequeue two tones in a row too fast. */ const int tone_duration = 1000 * 1000; /* Test setup. */ { cw_set_volume(70); int freq_min, freq_max; cw_get_frequency_limits(&freq_min, &freq_max); const int delta_freq = ((freq_max - freq_min) / (n_tones_to_add - 1)); /* Delta of frequency in loops. */ /* Test 1: enqueue n_tones_to_add tones, and wait for each of them separately. Control length of tone queue in the process. */ for (int i = 0; i < n_tones_to_add; i++) { int readback_length = 0; /* Measured length of tone queue. */ int expected_length = 0; /* Expected length of tone queue. */ /* Monitor length of a queue as it is filled - before adding a new tone. */ readback_length = LIBCW_TEST_FUT(cw_get_tone_queue_length)(); expected_length = i; cte->expect_op_int(cte, expected_length, "==", readback_length, "setup: queue length before adding tone = %d", readback_length); /* Add a tone to queue. All frequencies should be within allowed range, so there should be no error. */ const int freq = freq_min + i * delta_freq; cwret = LIBCW_TEST_FUT(cw_queue_tone)(tone_duration, freq); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "setup: cw_queue_tone() #%02d", i); /* Monitor length of a queue as it is filled - after adding a new tone. */ readback_length = LIBCW_TEST_FUT(cw_get_tone_queue_length)(); expected_length = i + 1; cte->expect_op_int(cte, expected_length, "==", readback_length, "setup: queue length after adding tone = %d", readback_length); } } /* Test. */ { cw_generator_start(); /* This is the proper test - waiting for dequeueing tones. Notice "-1" in loop initialization. We assume here that first tone has been already dequeued after generator has been started. TODO: redesign the test to avoid guessing how many tones have been already dequeued by running generator. */ for (int i = n_tones_to_add - 1; i > 0; i--) { int readback_length = 0; /* Measured length of tone queue. */ int expected_length = 0; /* Expected length of tone queue. */ /* Monitor length of a queue as it is emptied - before dequeueing. */ readback_length = LIBCW_TEST_FUT(cw_get_tone_queue_length)(); expected_length = i; cte->expect_op_int(cte, expected_length, "==", readback_length, "test: queue length before dequeueing = %d", readback_length); /* Wait for each of n_tones_to_add tones to be dequeued. */ cwret = LIBCW_TEST_FUT(cw_wait_for_tone)(); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "test: cw_wait_for_tone():"); /* Monitor length of a queue as it is emptied - after dequeueing single tone. */ readback_length = LIBCW_TEST_FUT(cw_get_tone_queue_length)(); expected_length = i - 1; cte->expect_op_int(cte, expected_length, "==", readback_length, "test: queue length after dequeueing = %d", readback_length); } cw_generator_stop(); } /* Test tear-down. */ { } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** Fill a queue, don't wait for each tone separately, but wait for a whole queue to become empty. @reviewed on 2019-10-13 */ int legacy_api_test_cw_wait_for_tone_queue(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Don't let generator run and dequeue tones just yet. Running/dequeueing generator will break our 'length' test. */ const bool start_generator = false; legacy_api_standalone_test_setup(cte, start_generator); const int n_tones_to_add = 6; /* This is a simple test, so only a handful of tones. */ /* Test setup: Add tones to tone queue. */ { cw_set_volume(70); int freq_min, freq_max; cw_get_frequency_limits(&freq_min, &freq_max); const int delta_freq = ((freq_max - freq_min) / (n_tones_to_add - 1)); const int tone_duration = 100000; for (int i = 0; i < n_tones_to_add; i++) { const int freq = freq_min + i * delta_freq; int cwret = LIBCW_TEST_FUT(cw_queue_tone)(tone_duration, freq); const bool success = cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "%s:%d setup: cw_queue_tone(%d, %d):", __func__, __LINE__, tone_duration, freq); if (!success) { break; } } } /* Test 1 (supplementary): Queue with enqueued tones should have some specific length. */ { const int len = LIBCW_TEST_FUT(cw_get_tone_queue_length)(); cte->expect_op_int(cte, n_tones_to_add, "==", len, "%s:%d: test: cw_get_tone_queue_length()", __func__, __LINE__); } /* Test 2 (main): We should be able to wait for emptying of non-empty queue while running generator is dequeueing tones. */ { cw_generator_start(); int cwret = LIBCW_TEST_FUT(cw_wait_for_tone_queue)(); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "%s:%d: test: cw_wait_for_tone_queue()", __func__, __LINE__); } /* Test tear-down. */ { } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** Run the complete range of tone generation, at X Hz intervals, first up the octaves, and then down. If the queue fills, though it shouldn't with this amount of data, then pause until it isn't so full. TODO: this test doesn't really test anything well. It just ensures that in some conditions cw_queue_tone() works correctly. @reviewed on 2019-10-13 */ int legacy_api_test_cw_queue_tone(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); cw_set_volume(70); int duration = 20000; int freq_min, freq_max; cw_get_frequency_limits(&freq_min, &freq_max); const int freq_delta = 10; bool wait_success = true; bool queue_success = true; for (int freq = freq_min; freq < freq_max; freq += freq_delta) { while (true == cw_is_tone_queue_full()) { /* TODO: we may never get to test cw_wait_for_tone() function because the queue will never be full in this test. */ int cwret = cw_wait_for_tone(); if (!cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_wait_for_tone(#1, %d)", freq)) { wait_success = false; break; } } int cwret = LIBCW_TEST_FUT(cw_queue_tone)(duration, freq); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_queue_tone(#1, %d)", freq)) { queue_success = false; break; } } for (int freq = freq_max; freq > freq_min; freq -= freq_delta) { while (true == cw_is_tone_queue_full()) { /* TODO: we may never get to test cw_wait_for_tone() function because the queue will never be full in this test. */ int cwret = cw_wait_for_tone(); if (!cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_wait_for_tone(#2, %d)", freq)) { wait_success = false; break; } } int cwret = LIBCW_TEST_FUT(cw_queue_tone)(duration, freq); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_queue_tone(#2, %d)", freq)) { queue_success = false; break; } } /* Final expect for 'queue' and 'wait' calls in the loop above. */ cte->expect_op_int(cte, true, "==", queue_success, "cw_queue_tone() - enqueueing"); cte->expect_op_int(cte, true, "==", wait_success, "cw_queue_tone() - waiting"); /* We have been adding tones to the queue, so we can test waiting for the queue to be emptied. */ int cwret = cw_wait_for_tone_queue(); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_wait_for_tone_queue()"); legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-13 */ int legacy_api_test_empty_tone_queue(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); /* Test setup. */ { cw_set_volume(70); /* Clear tone queue and make sure that it is really empty (wait for info from libcw). */ cw_flush_tone_queue(); cw_wait_for_tone_queue(); } /* Test. */ { const int capacity = LIBCW_TEST_FUT(cw_get_tone_queue_capacity)(); cte->expect_op_int(cte, CW_TONE_QUEUE_CAPACITY_MAX, "==", capacity, "cw_get_tone_queue_capacity()"); const int len_empty = LIBCW_TEST_FUT(cw_get_tone_queue_length)(); cte->expect_op_int(cte, 0, "==", len_empty, "cw_get_tone_queue_length() when tq is empty"); } /* Test tear-down. */ { } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-13 */ int legacy_api_test_full_tone_queue(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); /* Test setup. */ { cw_set_volume(70); /* FIXME: we call cw_queue_tone() until tq is full, and then expect the tq to be full while we perform tests. Doesn't the tq start dequeuing tones right away? Can we expect the tq to be full for some time after adding last tone? Hint: check when a length of tq is decreased. Probably after playing first tone on tq, which - in this test - is pretty long. Or perhaps not. */ const int duration = 1000000; int i = 0; /* FIXME: cw_is_tone_queue_full() is not tested */ while (!cw_is_tone_queue_full()) { const int freq = 100 + (i++ & 1) * 100; LIBCW_TEST_FUT(cw_queue_tone)(duration, freq); } } /* Test 1 Test properties (capacity and length) of full tq. */ { const int capacity = LIBCW_TEST_FUT(cw_get_tone_queue_capacity)(); cte->expect_op_int(cte, CW_TONE_QUEUE_CAPACITY_MAX, "==", capacity, "cw_get_tone_queue_capacity()"); const int len_full = LIBCW_TEST_FUT(cw_get_tone_queue_length)(); cte->log_info(cte, "*** you may now see \"EE: can't enqueue tone, tq is full\" message ***\n"); cte->expect_op_int(cte, CW_TONE_QUEUE_CAPACITY_MAX, "==", len_full, "cw_get_tone_queue_length() when tq is full"); } /* Test 2 Attempt to add tone to full queue. */ { errno = 0; int cwret = LIBCW_TEST_FUT(cw_queue_tone)(1000000, 100); cte->expect_op_int(cte, EAGAIN, "==", errno, "cw_queue_tone() for full tq (errno)"); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "cw_queue_tone() for full tq (cwret)"); } /* Test 3 Check again properties (capacity and length) of empty tq after it has been in use. */ { int cwret; /* Empty tone queue and make sure that it is really empty (wait for info from libcw). */ LIBCW_TEST_FUT(cw_flush_tone_queue)(); cwret = LIBCW_TEST_FUT(cw_wait_for_tone_queue)(); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_wait_for_tone_queue() after flushing"); const int capacity = LIBCW_TEST_FUT(cw_get_tone_queue_capacity)(); cte->expect_op_int(cte, CW_TONE_QUEUE_CAPACITY_MAX, "==", capacity, "cw_get_tone_queue_capacity() after flushing"); /* Test that the tq is really empty after cw_wait_for_tone_queue() has returned. */ const int len_empty = LIBCW_TEST_FUT(cw_get_tone_queue_length)(); cte->expect_op_int(cte, 0, "==", len_empty, "cw_get_tone_queue_length() after flushing"); } /* Test tear-down. */ { } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } typedef struct callback_data { bool can_capture; int captured_level; } callback_data; /** @reviewed on 2019-10-13 */ int legacy_api_test_tone_queue_callback(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); for (int i = 1; i < 10; i++) { /* Test the callback mechanism for very small values, but for a bit larger as well. */ int level = i <= 5 ? i : 10 * i; callback_data data = { 0 }; data.captured_level = 9999999; int cwret = LIBCW_TEST_FUT(cw_register_tone_queue_low_callback)(test_helper_tq_callback, (void *) &data, level); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_register_tone_queue_low_callback(): threshold = %d:", level); sleep(1); /* Add a lot of tones to tone queue. "a lot" means three times more than a value of trigger level. */ for (int j = 0; j < 3 * level; j++) { int duration = 10000; int f = 440; int rv = cw_queue_tone(duration, f); assert (rv); } /* Allow the callback to work only after initial filling of queue. */ data.can_capture = true; /* Wait for the queue to be drained to zero. While the tq is drained, and level of tq reaches trigger level, a callback will be called. Its only task is to copy the current level (tq level at time of calling the callback) value into callback_data::captured_level. Since the value of trigger level is different in consecutive iterations of loop, we can test the callback for different values of trigger level. */ cw_wait_for_tone_queue(); /* Because of order of calling callback and decreasing length of queue, I think that it's safe to assume that captured level may be in a range of values. */ const int expected_lower = level - 1; const int expected_higher = level; cte->expect_between_int(cte, expected_lower, data.captured_level, expected_higher, "tone queue callback: level at callback = %d", data.captured_level); cw_reset_tone_queue(); } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-13 */ static void test_helper_tq_callback(void * ptr) { callback_data * data = (callback_data *) ptr; if (data->can_capture) { data->captured_level = cw_get_tone_queue_length(); data->can_capture = false; } return; } /** \brief Test control of volume Fill tone queue with short tones, then check that we can move the volume through its entire range. Flush the queue when complete. @reviewed on 2019-10-13 */ int legacy_api_test_volume_functions(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); int vol_min = -1; int vol_max = -1; const int vol_delta = 5; /* Test: get range of allowed volumes. */ { LIBCW_TEST_FUT(cw_get_volume_limits)(&vol_min, &vol_max); cte->expect_op_int(cte, CW_VOLUME_MIN, "==", vol_min, "cw_get_volume_limits(): min = %d%%", vol_min); cte->expect_op_int(cte, CW_VOLUME_MAX, "==", vol_max, "cw_get_volume_limits(): max = %d%%", vol_max); } /* Test setup. Fill the tone queue with valid tones. */ { while (!cw_is_tone_queue_full()) { cw_queue_tone(100000, 440); } } /* Test: decrease volume from max to min. */ { bool set_failure = false; bool get_failure = false; for (int volume = vol_max; volume >= vol_min; volume -= vol_delta) { /* We wait here for next tone so that changes in volume happen once per tone - not more often and not less. */ cw_wait_for_tone(); const int cwret = LIBCW_TEST_FUT(cw_set_volume)(volume); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_set_volume(%d) (down)", volume)) { set_failure = true; break; } const int readback = LIBCW_TEST_FUT(cw_get_volume)(); if (!cte->expect_op_int_errors_only(cte, volume, "==", readback, "cw_get_volume() (down) -> %d", readback)) { get_failure = true; break; } } cte->expect_op_int(cte, false, "==", set_failure, "cw_set_volume() (down)"); cte->expect_op_int(cte, false, "==", get_failure, "cw_get_volume() (down)"); } /* Test tear-down. */ { cw_flush_tone_queue(); } /* ---------------- */ /* Test setup. Fill the tone queue with valid tones. */ { while (!cw_is_tone_queue_full()) { cw_queue_tone(100000, 440); } } /* Test: increase volume from min to max. */ { bool set_failure = false; bool get_failure = false; for (int volume = vol_min; volume <= vol_max; volume += vol_delta) { /* We wait here for next tone so that changes in volume happen once per tone - not more often and not less. */ cw_wait_for_tone(); const int cwret = LIBCW_TEST_FUT(cw_set_volume)(volume); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_set_volume(%d) (up)", volume)) { set_failure = true; break; } const int readback = LIBCW_TEST_FUT(cw_get_volume)(); if (!cte->expect_op_int_errors_only(cte, volume, "==", readback, "cw_get_volume() (up) -> %d", readback)) { get_failure = true; break; } } cte->expect_op_int(cte, false, "==", set_failure, "cw_set_volume() (up)"); cte->expect_op_int(cte, false, "==", get_failure, "cw_get_volume() (up)"); } /* Test tear-down. */ { cw_flush_tone_queue(); } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** \brief Test enqueueing most basic elements of Morse code @reviewed on 2019-10-13 */ int legacy_api_test_send_primitives(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); legacy_api_standalone_test_setup(cte, true); /* Test: sending dot. */ { bool failure = false; for (int i = 0; i < loops; i++) { const int cwret = LIBCW_TEST_FUT(cw_send_dot)(); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_send_dot() #%d", i)) { failure = true; break; } } cw_wait_for_tone_queue(); cte->expect_op_int(cte, false, "==", failure, "cw_send_dot()"); } /* Test: sending dash. */ { bool failure = false; for (int i = 0; i < loops; i++) { const int cwret = LIBCW_TEST_FUT(cw_send_dash)(); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_send_dash() #%d", i)) { failure = true; break; } } cw_wait_for_tone_queue(); cte->expect_op_int(cte, false, "==", failure, "cw_send_dash()"); } /* Test: sending character space. */ { bool failure = false; for (int i = 0; i < loops; i++) { const int cwret = LIBCW_TEST_FUT(cw_send_character_space)(); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_send_character_space() #%d", i)) { failure = true; break; } } cw_wait_for_tone_queue(); cte->expect_op_int(cte, false, "==", failure, "cw_send_character_space()"); } /* Test: sending word space. */ { bool failure = false; for (int i = 0; i < loops; i++) { const int cwret = LIBCW_TEST_FUT(cw_send_word_space)(); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_send_word_space() #%d", i)) { failure = true; break; } } cw_wait_for_tone_queue(); cte->expect_op_int(cte, false, "==", failure, "cw_send_word_space()"); } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** \brief Enqueueing representations of characters @reviewed on 2019-10-13 */ int legacy_api_test_representations(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); /* Test: sending valid representations. */ { bool failure = false; int i = 0; while (NULL != test_valid_representations[i]) { const int cwret = LIBCW_TEST_FUT(cw_send_representation)(test_valid_representations[i]); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_send_representation(valid #%d)", i)) { failure = true; break; } i++; } cte->expect_op_int(cte, false, "==", failure, "cw_send_representation(valid)"); cw_wait_for_tone_queue(); } /* Test: sending invalid representations. */ { bool failure = false; int i = 0; while (NULL != test_invalid_representations[i]) { const int cwret = LIBCW_TEST_FUT(cw_send_representation)(test_invalid_representations[i]); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "cw_send_representation(invalid #%d)", i)) { failure = true; break; } i++; } cte->expect_op_int(cte, false, "==", failure, "cw_send_representation(invalid)"); cw_wait_for_tone_queue(); } /* Test: sending partial representation of a valid string. */ { bool failure = false; int i = 0; while (NULL != test_valid_representations[i]) { const int cwret = LIBCW_TEST_FUT(cw_send_representation_partial)(test_valid_representations[i]); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_send_representation_partial(valid #%d)", i)) { failure = true; break; } i++; } cte->expect_op_int(cte, false, "==", failure, "cw_send_representation_partial(valid)"); cw_wait_for_tone_queue(); } /* Test: sending partial representation of a invalid string. */ { bool failure = false; int i = 0; while (NULL != test_invalid_representations[i]) { const int cwret = LIBCW_TEST_FUT(cw_send_representation_partial)(test_invalid_representations[i]); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "cw_send_representation_partial(invalid #%d)", i)) { failure = true; break; } i++; } cte->expect_op_int(cte, false, "==", failure, "cw_send_representation_partial(invalid)"); cw_wait_for_tone_queue(); } cw_wait_for_tone_queue(); legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** Send all supported characters: first as individual characters, and then as a string. @reviewed on 2019-10-13 */ int legacy_api_test_send_character_and_string(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); /* Test: sending all supported characters as individual characters. */ { char charlist[UCHAR_MAX + 1] = { 0 }; /* TODO: get size of this buffer through cw_get_character_count(). */ LIBCW_TEST_FUT(cw_list_characters)(charlist); bool failure = false; /* Send all the characters from the charlist individually. */ cte->log_info(cte, "cw_send_character():\n" " "); for (int i = 0; charlist[i] != '\0'; i++) { const char character = charlist[i]; cte->log_info_cont(cte, "%c", character); cte->flush_info(cte); const int cwret = LIBCW_TEST_FUT(cw_send_character)(character); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "cw_send_character(%c)", character)) { failure = true; break; } cw_wait_for_tone_queue(); } cte->log_info_cont(cte, "\n"); cte->flush_info(cte); cte->expect_op_int(cte, false, "==", failure, "cw_send_character()"); } /* Test: sending invalid character. */ { const int cwret = LIBCW_TEST_FUT(cw_send_character)(0); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "cw_send_character()"); } /* Test: sending all supported characters as single string. */ { char charlist[UCHAR_MAX + 1] = { 0 }; /* TODO: get size of this buffer through cw_get_character_count(). */ LIBCW_TEST_FUT(cw_list_characters)(charlist); /* Send the complete charlist as a single string. */ cte->log_info(cte, "cw_send_string():\n" " %s\n", charlist); const int cwret = LIBCW_TEST_FUT(cw_send_string)(charlist); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_send_string()"); while (cw_get_tone_queue_length() > 0) { cte->log_info(cte, "tone queue length %-6d\r", cw_get_tone_queue_length()); cte->flush_info(cte); cw_wait_for_tone(); } cte->log_info(cte, "tone queue length %-6d\n", cw_get_tone_queue_length()); } /* Test: sending invalid string. */ { const int cwret = LIBCW_TEST_FUT(cw_send_string)("%INVALID%"); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "cw_send_string()"); } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } /** Wrapper for common code used by three test functions. @reviewed on 2019-10-13 */ void legacy_api_test_iambic_key_paddles_common(cw_test_executor_t * cte, const int intended_dot_paddle, const int intended_dash_paddle, char character, int n_elements) { /* Test: keying alternate dit/dash. */ { /* It seems like this function calls means "keyer pressed until further notice".*/ const int cwret = LIBCW_TEST_FUT(cw_notify_keyer_paddle_event)(intended_dot_paddle, intended_dash_paddle); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_notify_keyer_paddle_event(%d, %d)", intended_dot_paddle, intended_dash_paddle); bool success = true; cte->flush_info(cte); for (int i = 0; i < n_elements; i++) { success = success && LIBCW_TEST_FUT(cw_wait_for_keyer_element)(); cte->log_info_cont(cte, "%c", character); cte->flush_info(cte); } cte->log_info_cont(cte, "\n"); cte->expect_op_int(cte, true, "==", success, "cw_wait_for_keyer_element() (%c)", character); } /* Test: preserving of paddle states. */ { /* State of paddles should be the same as after call to cw_notify_keyer_paddle_event() above. */ int read_back_dot_paddle; int read_back_dash_paddle; LIBCW_TEST_FUT(cw_get_keyer_paddles)(&read_back_dot_paddle, &read_back_dash_paddle); cte->expect_op_int(cte, intended_dot_paddle, "==", read_back_dot_paddle, "cw_get_keyer_paddles(): dot paddle"); cte->expect_op_int(cte, intended_dash_paddle, "==", read_back_dash_paddle, "cw_get_keyer_paddles(): dash paddle"); } cte->flush_info(cte); cw_wait_for_keyer(); return; } /** Perform some tests on the iambic keyer. The latch finer timing points are not tested here, just the basics - dots, dashes, and alternating dots and dashes. @reviewed on 2019-10-13 */ int legacy_api_test_iambic_key_dot(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); /* Test: keying dot. Since a "dot" paddle is pressed, get N "dot" events from the keyer. This is test of legacy API, so use true/false instead of CW_KEY_STATE_CLOSED (true)/CW_KEY_STATE_OPEN (false). */ const int intended_dot_paddle = true; const int intended_dash_paddle = false; const char character = CW_DOT_REPRESENTATION; const int n_elements = 30; legacy_api_test_iambic_key_paddles_common(cte, intended_dot_paddle, intended_dash_paddle, character, n_elements); #ifndef LIBCW_KEY_TESTS_WORKAROUND legacy_api_standalone_test_teardown(cte); #endif cte->print_test_footer(cte, __func__); return 0; } /** Perform some tests on the iambic keyer. The latch finer timing points are not tested here, just the basics - dots, dashes, and alternating dots and dashes. @reviewed on 2019-10-13 */ int legacy_api_test_iambic_key_dash(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); #ifndef LIBCW_KEY_TESTS_WORKAROUND legacy_api_standalone_test_setup(cte, true); #endif /* Test: keying dash. Since a "dash" paddle is pressed, get N "dash" events from the keyer. This is test of legacy API, so use true/false instead of CW_KEY_STATE_CLOSED (true)/CW_KEY_STATE_OPEN (false). */ const int intended_dot_paddle = false; const int intended_dash_paddle = true; const char character = CW_DASH_REPRESENTATION; const int n_elements = 30; legacy_api_test_iambic_key_paddles_common(cte, intended_dot_paddle, intended_dash_paddle, character, n_elements); #ifndef LIBCW_KEY_TESTS_WORKAROUND legacy_api_standalone_test_teardown(cte); #endif cte->print_test_footer(cte, __func__); return 0; } /** Perform some tests on the iambic keyer. The latch finer timing points are not tested here, just the basics - dots, dashes, and alternating dots and dashes. @reviewed on 2019-10-13 */ int legacy_api_test_iambic_key_alternating(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); #ifndef LIBCW_KEY_TESTS_WORKAROUND legacy_api_standalone_test_setup(cte, true); #endif /* Test: keying alternate dit/dash. Both arguments are true, so both paddles are pressed at the same time. This is test of legacy API, so use true/false instead of CW_KEY_STATE_CLOSED (true)/CW_KEY_STATE_OPEN (false). */ const int intended_dot_paddle = true; const int intended_dash_paddle = true; const char character = '#'; const int n_elements = 30; legacy_api_test_iambic_key_paddles_common(cte, intended_dot_paddle, intended_dash_paddle, character, n_elements); #ifndef LIBCW_KEY_TESTS_WORKAROUND legacy_api_standalone_test_teardown(cte); #endif cte->print_test_footer(cte, __func__); return 0; } /** Perform some tests on the iambic keyer. The latch finer timing points are not tested here, just the basics - dots, dashes, and alternating dots and dashes. @reviewed on 2019-10-13 */ int legacy_api_test_iambic_key_none(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); #ifndef LIBCW_KEY_TESTS_WORKAROUND legacy_api_standalone_test_setup(cte, true); #endif /* Test: set new state of paddles: no paddle pressed. This is test of legacy API, so use true/false instead of CW_KEY_STATE_CLOSED (true)/CW_KEY_STATE_OPEN (false). */ const int intended_dot_paddle = false; const int intended_dash_paddle = false; /* Test: depress paddles. */ { const int cwret = LIBCW_TEST_FUT(cw_notify_keyer_paddle_event)(intended_dot_paddle, intended_dot_paddle); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_notify_keyer_paddle_event(%d, %d)", intended_dot_paddle, intended_dash_paddle); } /* Test: preserving of paddle states. */ { /* State of paddles should be the same as after call to cw_notify_keyer_paddle_event() above. */ int read_back_dot_paddle; int read_back_dash_paddle; LIBCW_TEST_FUT(cw_get_keyer_paddles)(&read_back_dot_paddle, &read_back_dash_paddle); cte->expect_op_int(cte, intended_dot_paddle, "==", read_back_dot_paddle, "cw_get_keyer_paddles(): dot paddle"); cte->expect_op_int(cte, intended_dash_paddle, "==", read_back_dash_paddle, "cw_get_keyer_paddles(): dash paddle"); } cw_wait_for_keyer(); #ifndef LIBCW_KEY_TESTS_WORKAROUND legacy_api_standalone_test_teardown(cte); #endif cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2020-10-08 */ int legacy_api_test_straight_key(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); #ifndef LIBCW_KEY_TESTS_WORKAROUND legacy_api_standalone_test_setup(cte, true); #endif const int loops = 8; for (size_t i = 0; i < TEST_STRAIGHT_KEY_DATA_COUNT; i++) { g_test_straight_key_data[i].loops = loops; g_test_straight_key_data[i].legacy_set = LIBCW_TEST_FUT(cw_notify_straight_key_event); g_test_straight_key_data[i].legacy_get = LIBCW_TEST_FUT(cw_get_straight_key_state); g_test_straight_key_data[i].legacy_is_busy = LIBCW_TEST_FUT(cw_is_straight_key_busy); g_test_straight_key_data[i].modern_set = NULL; g_test_straight_key_data[i].modern_get = NULL; test_helper_test_straight_key(cte, &cw_key, &g_test_straight_key_data[i]); } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return 0; } # if 0 /* * cw_test_delayed_release() */ void cw_test_delayed_release(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); legacy_api_standalone_test_setup(cte, true); int failures = 0; struct timeval start, finish; int is_released, delay; /* This is slightly tricky to detect, but circumstantial evidence is provided by SIGALRM disposition returning to SIG_DFL. */ if (!cw_send_character_space()) { cte->log_error(cte, "cw_send_character_space()\n"); failures++; } if (gettimeofday(&start, NULL) != 0) { cte->log_error(cte, "gettimeofday failed, test incomplete\n"); return; } cte->log_info(cte, "waiting for cw_finalization delayed release"); cte->flush_info(cte); do { struct sigaction disposition; sleep(1); if (sigaction(SIGALRM, NULL, &disposition) != 0) { cte->log_error(cte, "sigaction failed, test incomplete\n"); return; } is_released = disposition.sa_handler == SIG_DFL; if (gettimeofday(&finish, NULL) != 0) { cte->log_error(cte, "gettimeofday failed, test incomplete\n"); return; } delay = (finish.tv_sec - start.tv_sec) * 1000000 + finish.tv_usec - start.tv_usec; cte->log_info_cont(cte, "."); cte->flush_info(cte); } while (!is_released && delay < 20000000) { ; } cte->log_info_cont(cte, "\n"); /* The release should be around 10 seconds after the end of the sent space. A timeout or two might leak in, reducing it by a bit; we'll be ecstatic with more than five seconds. */ if (is_released) { cte->log_info(cte, "cw_finalization delayed release after %d usecs\n", delay); if (delay < 5000000) { cte->log_error(cte, "cw_finalization release too quick\n"); failures++; } } else { cte->log_error(cte, "cw_finalization release wait timed out\n"); failures++; } legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return; } /* * cw_test_signal_handling_callback() * cw_test_signal_handling() */ static int cw_test_signal_handling_callback_called = false; void cw_test_signal_handling_callback(int signal_number) { signal_number = 0; cw_test_signal_handling_callback_called = true; } void cw_test_signal_handling(cw_test_executor_t * cte) { int failures = 0; struct sigaction action, disposition; /* Test registering, unregistering, and raising SIGUSR1. SIG_IGN and handlers are tested, but not SIG_DFL, because that stops the process. */ if (cw_unregister_signal_handler(SIGUSR1)) { cte->log_error(cte, "cw_unregister_signal_handler invalid\n"); failures++; } if (!cw_register_signal_handler(SIGUSR1, cw_test_signal_handling_callback)) { cte->log_error(cte, "cw_register_signal_handler failed\n"); failures++; } cw_test_signal_handling_callback_called = false; raise(SIGUSR1); sleep(1); if (!cw_test_signal_handling_callback_called) { cte->log_error(cte, "cw_test_signal_handling_callback missed\n"); failures++; } if (!cw_register_signal_handler(SIGUSR1, SIG_IGN)) { cte->log_error(cte, "cw_register_signal_handler (overwrite) failed\n"); failures++; } cw_test_signal_handling_callback_called = false; raise(SIGUSR1); sleep(1); if (cw_test_signal_handling_callback_called) { cte->log_error(cte, "cw_test_signal_handling_callback called\n"); failures++; } if (!cw_unregister_signal_handler(SIGUSR1)) { cte->log_error(cte, "cw_unregister_signal_handler failed\n"); failures++; } if (cw_unregister_signal_handler(SIGUSR1)) { cte->log_error(cte, "cw_unregister_signal_handler invalid\n"); failures++; } action.sa_handler = cw_test_signal_handling_callback; action.sa_flags = SA_RESTART; sigemptyset(&action.sa_mask); if (sigaction(SIGUSR1, &action, &disposition) != 0) { cte->log_error(cte, "sigaction failed, test incomplete\n"); return failures; } if (cw_register_signal_handler(SIGUSR1, SIG_IGN)) { cte->log_error(cte, "cw_register_signal_handler clobbered\n"); failures++; } if (sigaction(SIGUSR1, &disposition, NULL) != 0) { cte->log_error(cte, "WARNING: sigaction failed, test incomplete\n"); return failures; } cte->log_info(cte, "cw_[un]register_signal_handler tests complete\n"); return; } #endif /** @reviewed on 2019-10-13 */ int legacy_api_test_basic_gen_operations(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); #if 0 /* We don't call it here because generator is not created yet. Setup is handled by test code below.. */ legacy_api_standalone_test_setup(cte, true); #endif int cwret = CW_FAILURE; /* Test setting up generator. */ { cwret = LIBCW_TEST_FUT(cw_generator_new)(cte->current_gen_conf.sound_system, cte->current_gen_conf.sound_device); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_generator_new()"); if (cwret != CW_SUCCESS) { return -1; } cw_reset_send_receive_parameters(); cwret = LIBCW_TEST_FUT(cw_set_send_speed)(12); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_set_send_speed()"); cwret = LIBCW_TEST_FUT(cw_generator_start)(); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_generator_start()"); } /* Test using generator. */ { cwret = LIBCW_TEST_FUT(cw_send_string)("one "); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_send_string()"); cwret = LIBCW_TEST_FUT(cw_wait_for_tone_queue)(); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_wait_for_tone_queue()"); cwret = LIBCW_TEST_FUT(cw_send_string)("two"); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_send_string()"); cwret = LIBCW_TEST_FUT(cw_wait_for_tone_queue)(); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_wait_for_tone_queue()"); cwret = LIBCW_TEST_FUT(cw_send_string)("three"); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_send_string()"); cwret = LIBCW_TEST_FUT(cw_wait_for_tone_queue)(); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_wait_for_tone_queue()"); } /* Deconfigure generator. These functions don't return a value, so we can't verify anything. */ { LIBCW_TEST_FUT(cw_generator_stop)(); LIBCW_TEST_FUT(cw_generator_delete)(); } cte->print_test_footer(cte, __func__); return 0; } /** @brief Test removing a character from end of enqueued characters @reviewed on 2020-08-24 */ cwt_retv legacy_api_test_gen_remove_last_character(cw_test_executor_t * cte) { cte->print_test_header(cte, "%s", __func__); legacy_api_standalone_test_setup(cte, true); const int n = 4; bool failure = false; for (int to_remove = 0; to_remove <= n; to_remove++) { cte->log_info(cte, "You will now hear 'oooo' followed by %d 's' characters\n", n - to_remove); cw_send_string("oooo" "ssss"); /* Remove N characters from end. */ for (int i = 0; i < to_remove; i++) { cw_ret_t cwret = LIBCW_TEST_FUT(cw_generator_remove_last_character()); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "remove last %d characters, removing %d-th character", to_remove, i)) { failure = true; break; } } cw_wait_for_tone_queue(); /* TODO: this sleep should be randomized from 0 to 1000 * 1000 microseconds, to detect e.g. problems with ALSA underruns. I have noticed that when this sleep is small or zero, there is no underrun, but with 10^6 us delay an underrun may happen, because there is a long period in which we don't supply new frames to HW. Some cases of buffer underrun have been fixed with calls to snd_pcm_drain(), but perhaps not all of them. */ cw_usleep_internal(1000 * 1000); if (failure) { break; } } cte->expect_op_int(cte, false, "==", failure, "remove last character"); legacy_api_standalone_test_teardown(cte); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } unixcw-3.6.0/src/libcw/tests/libcw_legacy_api_tests_rec_poll.c0000644000175000017500000007744414000344554021550 00000000000000/* Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #if defined(HAVE_STRING_H) # include /* FreeBSD 12.1 */ #endif #if defined(HAVE_STRINGS_H) # include #endif #include #include #include "libcw.h" #include "libcw2.h" #include "libcw_gen.h" #include "libcw_key.h" #include "libcw_tq.h" #include "libcw_utils.h" #include "test_framework.h" #include "test_framework_tools.h" #include "libcw_legacy_api_tests_rec_poll.h" /** Test code for 'poll' method for libcw receiver used in xcwcp. xcwcp is implementing a receiver of Morse code keyed with Space or Enter keyboard key. Recently I have added a test code to xcwcp (from unixcw 3.5.1) that verifies how receive process is working and whether it is working correctly. Then I have extracted the core part of the xcwcp receiver code and the test code, and I have put it here. Now we have the test code embedded in xcwcp (so we can always have it as 'in vivo' test), and we have the test code here in libcw tests set. */ /* TODO: move this type to libcw_rec.h and use it to pass arguments to functions such as cw_rec_poll_representation_ics_internal(). */ typedef struct received_data { char character; char representation[20]; bool is_iws; /* Is receiver in 'found inter-word-space' state? */ bool is_error; } received_data; static cw_rec_tester_t g_tester; typedef struct Receiver { cw_test_executor_t * cte; /* Timer for measuring length of dots and dashes. Initial value of the timestamp is created by xcwcp's receiver on first "paddle down" event in a character. The timestamp is then updated by libcw on specific time intervals. The intervals are a function of keyboard key presses or mouse button presses recorded by xcwcp. */ struct timeval main_timer; /* Flag indicating if receive polling has received a character, and may need to augment it with a word space on a later poll. */ volatile bool is_pending_inter_word_space; /* Flag indicating possible receive errno detected in signal handler context and needing to be passed to the foreground. */ volatile int libcw_receive_errno; /* Safety flag to ensure that we keep the library in sync with keyer events. Without, there's a chance that of a on-off event, one half will go to one application instance, and the other to another instance. */ volatile bool tracked_key_state; bool c_r; } Receiver; static Receiver g_xcwcp_receiver; /* Callback. */ void xcwcp_handle_libcw_keying_event(void * timer, int key_state); void low_tone_queue_callback(void * arg); static cwt_retv legacy_api_test_rec_poll_inner(cw_test_executor_t * cte, bool c_r); void * receiver_input_generator_fn(void * arg); void receiver_sk_event(Receiver * xcwcp_receiver, bool is_down); /* Main poll function and its helpers. */ void receiver_poll_receiver(Receiver * xcwcp_receiver); void receiver_poll_report_error(Receiver * xcwcp_receiver); void receiver_poll_character_c_r(Receiver * xcwcp_receiver); void receiver_poll_character_r_c(Receiver * xcwcp_receiver); void receiver_poll_space_c_r(Receiver * xcwcp_receiver); void receiver_poll_space_r_c(Receiver * xcwcp_receiver); void test_callback_func(void * arg, int key_state); void tester_start_test_code(cw_rec_tester_t * tester); void tester_stop_test_code(cw_rec_tester_t * tester); /** \brief Poll the CW library receive buffer and handle anything found in the buffer */ void receiver_poll_receiver(Receiver * xcwcp_receiver) { if (xcwcp_receiver->libcw_receive_errno != 0) { receiver_poll_report_error(xcwcp_receiver); } if (xcwcp_receiver->is_pending_inter_word_space) { /* Check if receiver received the pending inter-word-space. */ if (xcwcp_receiver->c_r) { /* Poll character, then poll representation. */ receiver_poll_space_c_r(xcwcp_receiver); } else { /* First poll representation, then character. */ receiver_poll_space_r_c(xcwcp_receiver); } if (!xcwcp_receiver->is_pending_inter_word_space) { /* We received the pending space. After it the receiver may have received another character. Try to get it too. */ if (xcwcp_receiver->c_r) { /* Poll character, then poll representation. */ receiver_poll_character_c_r(xcwcp_receiver); } else { receiver_poll_character_r_c(xcwcp_receiver); } } } else { /* Not awaiting a possible space, so just poll the next possible received character. */ if (xcwcp_receiver->c_r) { /* Poll character, then poll representation. */ receiver_poll_character_c_r(xcwcp_receiver); } else { receiver_poll_character_r_c(xcwcp_receiver); } } return; } /** \brief Handler for the keying callback from the CW library indicating that the state of a key has changed. The "key" is libcw's internal key structure. It's state is updated by libcw when e.g. one iambic keyer paddle is constantly pressed. It is also updated in other situations. In any case: the function is called whenever state of this key changes. Notice that the description above talks about a key, not about a receiver. Key's states need to be interpreted by receiver, which is a separate task. Key and receiver are separate concepts. This function connects them. This function, called on key state changes, calls receiver functions to ensure that receiver does "receive" the key state changes. This function is called in signal handler context, and it takes care to call only functions that are safe within that context. In particular, it goes out of its way to deliver results by setting flags that are later handled by receive polling. */ void xcwcp_handle_libcw_keying_event(void * timer, int key_state) { struct timeval *t = (struct timeval *) timer; /* Ignore calls where the key state matches our tracked key state. This avoids possible problems where this event handler is redirected between application instances; we might receive an end of tone without seeing the start of tone. */ if (key_state == g_xcwcp_receiver.tracked_key_state) { //fprintf(stderr, "tracked key state == %d\n", g_xcwcp_receiver.tracked_key_state); return; } else { //fprintf(stderr, "tracked key state := %d\n", key_state); g_xcwcp_receiver.tracked_key_state = key_state; } /* If this is a tone start and we're awaiting an inter-word-space, cancel that wait and clear the receive buffer. */ if (key_state && g_xcwcp_receiver.is_pending_inter_word_space) { /* Tell receiver to prepare (to make space) for receiving new character. */ cw_clear_receive_buffer(); /* The tone start means that we're seeing the next incoming character within the same word, so no inter-word-space is possible at this point in time. The space that we were observing/waiting for, was just inter-character-space. */ g_xcwcp_receiver.is_pending_inter_word_space = false; } //fprintf(stderr, "calling callback, stage 2\n"); /* Pass tone state on to the library. For tone end, check to see if the library has registered any receive error. */ if (key_state) { /* Key down. */ //fprintf(stderr, "start receive tone: %10ld . %10ld\n", t->tv_sec, t->tv_usec); if (!cw_start_receive_tone(t)) { perror("cw_start_receive_tone"); // TODO: Perhaps this should be counted as test error return; } } else { /* Key up. */ //fprintf(stderr, "end receive tone: %10ld . %10ld\n", t->tv_sec, t->tv_usec); if (!cw_end_receive_tone(t)) { /* Handle receive error detected on tone end. For ENOMEM and ENOENT we set the error in a class flag, and display the appropriate message on the next receive poll. */ switch (errno) { case EAGAIN: /* libcw treated the tone as noise (it was shorter than noise threshold). No problem, not an error. */ break; case ENOMEM: case ERANGE: case EINVAL: case ENOENT: g_xcwcp_receiver.libcw_receive_errno = errno; cw_clear_receive_buffer(); break; default: perror("cw_end_receive_tone"); // TODO: Perhaps this should be counted as test error return; } } } return; } /** \brief Handle any error registered when handling a libcw keying event */ void receiver_poll_report_error(Receiver * xcwcp_receiver) { xcwcp_receiver->libcw_receive_errno = 0; return; } /** \brief Receive any new character from the CW library. The function is called c_r because primary function in production code polls character, and only then in test code a representation is polled. @reviewed 2020-08-27 */ void receiver_poll_character_c_r(Receiver * xcwcp_receiver) { /* Don't use xcwcp_receiver.main_timer - it is used exclusively for marking initial "key down" events. Use local throw-away local_timer. Additionally using xcwcp_receiver.main_timer here would mess up time intervals measured by xcwcp_receiver.main_timer, and that would interfere with recognizing dots and dashes. */ struct timeval local_timer; gettimeofday(&local_timer, NULL); //fprintf(stderr, "poll_receive_char: %10ld : %10ld\n", local_timer.tv_sec, local_timer.tv_usec); const bool debug_errnos = false; static int prev_errno = 0; received_data prod = { 0 }; cw_ret_t cwret = LIBCW_TEST_FUT(cw_receive_character)(&local_timer, &prod.character, &prod.is_iws, NULL); if (CW_SUCCESS == cwret) { prev_errno = 0; /* Receiver stores full, well formed character. Display it. */ fprintf(stderr, "[II] Polled character '%c'\n", prod.character); { /* Verification code. */ bool failure = false; received_data test = { 0 }; cw_test_executor_t * cte = xcwcp_receiver->cte; cwret = cw_receive_representation(&local_timer, test.representation, &test.is_iws, &test.is_error); if (!cte->expect_op_int_errors_only(cte, cwret, "==", CW_SUCCESS, "Test poll representation (c_r)")) { failure = true; } if (!cte->expect_op_int_errors_only(cte, test.is_iws, "==", prod.is_iws, "Comparing 'is inter-word-space' flags: %d, %d", test.is_iws, prod.is_iws)) { failure = true; } /* We are polling a character here, so we expect that receiver will set 'is inter-word-space' flag to false. */ if (!cte->expect_op_int_errors_only(cte, test.is_iws, "==", false, "Evaluating 'is inter-word-space' flag")) { failure = true; } test.character = cw_representation_to_character(test.representation); if (!cte->expect_op_int_errors_only(cte, 0, "!=", test.character, "Lookup character for representation")) { failure = true; } if (!cte->expect_op_int_errors_only(cte, prod.character, "==", test.character, "Compare polled and looked up character: %c, %c", prod.character, test.character)) { failure = true; } fprintf(stderr, "[II] Poll character: %c -> '%s' -> %c\n", prod.character, test.representation, test.character); cte->expect_op_int(cte, failure, "==", false, "Polling character"); g_tester.received_string[g_tester.received_string_i++] = prod.character; } /* A full character has been received. Directly after it comes a space. Either a short inter-character-space followed by another character (in this case we won't display the inter-character-space), or longer inter-word-space - this space we would like to catch and display. Set a flag indicating that next poll may result in inter-word-space. */ xcwcp_receiver->is_pending_inter_word_space = true; } else { /* Handle receive error detected on trying to read a character. */ switch (errno) { case EAGAIN: /* Call made too early, receiver hasn't received a full character yet. Try next time. */ if (debug_errnos && prev_errno != EAGAIN) { fprintf(stderr, "[NN] poll_receive_char: %d -> EAGAIN\n", prev_errno); prev_errno = EAGAIN; } break; case ERANGE: /* Call made not in time, or not in proper sequence. Receiver hasn't received any character (yet). Try harder. */ if (debug_errnos && prev_errno != ERANGE) { fprintf(stderr, "[NN] poll_receive_char: %d -> RANGE\n", prev_errno); prev_errno = ERANGE; } break; case ENOENT: /* Invalid character in receiver's buffer. */ if (debug_errnos && prev_errno != ENOENT) { fprintf(stderr, "[NN] poll_receive_char: %d -> ENONENT\n", prev_errno); prev_errno = ENOENT; } cw_clear_receive_buffer(); break; case EINVAL: /* Timestamp error. */ if (debug_errnos && prev_errno != EINVAL) { fprintf(stderr, "[NN] poll_receive_char: %d -> EINVAL\n", prev_errno); prev_errno = EINVAL; } cw_clear_receive_buffer(); break; default: perror("cw_receive_character"); // TODO: Perhaps this should be counted as test error return; } } return; } /** \brief Receive any new character from the CW library. The function is called r_c because primary function in production code polls representation, and only then in test code a character is polled. @reviewed 2020-08-27 */ void receiver_poll_character_r_c(Receiver * xcwcp_receiver) { /* Don't use xcwcp_receiver.main_timer - it is used exclusively for marking initial "key down" events. Use local throw-away local_timer. Additionally using xcwcp_receiver.main_timer here would mess up time intervals measured by xcwcp_receiver.main_timer, and that would interfere with recognizing dots and dashes. */ struct timeval local_timer; gettimeofday(&local_timer, NULL); //fprintf(stderr, "poll_receive_char: %10ld : %10ld\n", local_timer.tv_sec, local_timer.tv_usec); const bool debug_errnos = false; static int prev_errno = 0; received_data prod = { 0 }; cw_ret_t cwret = LIBCW_TEST_FUT(cw_receive_representation)(&local_timer, prod.representation, &prod.is_iws, &prod.is_error); if (CW_SUCCESS == cwret) { prev_errno = 0; /* Receiver stores full, well formed representation. Display it. */ fprintf(stderr, "[II] Polled representation '%s'\n", prod.representation); { /* Verification code. */ bool failure = false; received_data test = { 0 }; cw_test_executor_t * cte = xcwcp_receiver->cte; cwret = cw_receive_character(&local_timer, &test.character, &test.is_iws, &test.is_error); if (!cte->expect_op_int_errors_only(cte, cwret, "==", CW_SUCCESS, "Test poll character (in r_c)")) { failure = true; } if (!cte->expect_op_int_errors_only(cte, test.is_iws, "==", prod.is_iws, "Comparing 'is inter-word-space' flags: %d, %d", test.is_iws, prod.is_iws)) { failure = true; } /* We are polling a character here, so we expect that receiver will set 'is inter-word-space' flag to false. */ if (!cte->expect_op_int_errors_only(cte, test.is_iws, "==", false, "Evaluating 'is inter-word-space' flag")) { failure = true; } char * looked_up_representation = cw_character_to_representation(test.character); if (!cte->expect_valid_pointer_errors_only(cte, looked_up_representation, "Lookup representation of character")) { failure = true; } const int cmp = strcmp(prod.representation, looked_up_representation); if (!cte->expect_op_int_errors_only(cte, cmp, "==", 0, "Compare polled and looked up representation: '%s', '%s'", prod.representation, looked_up_representation)) { failure = true; } fprintf(stderr, "[II] Poll representation: '%s' -> %c -> '%s'\n", prod.representation, test.character, looked_up_representation); cte->expect_op_int(cte, failure, "==", false, "Poll representation"); g_tester.received_string[g_tester.received_string_i++] = test.character; free(looked_up_representation); } /* A full character has been received. Directly after it comes a space. Either a short inter-character-space followed by another character (in this case we won't display the inter-character-space), or longer inter-word-space - this space we would like to catch and display. Set a flag indicating that next poll may result in inter-word-space. */ xcwcp_receiver->is_pending_inter_word_space = true; } else { /* Handle receive error detected on trying to read a character. */ switch (errno) { case EAGAIN: /* Call made too early, receiver hasn't received a full character yet. Try next time. */ if (debug_errnos && prev_errno != EAGAIN) { fprintf(stderr, "[NN] poll_receive_representation: %d -> EAGAIN\n", prev_errno); prev_errno = EAGAIN; } break; case ERANGE: /* Call made not in time, or not in proper sequence. Receiver hasn't received any character (yet). Try harder. */ if (debug_errnos && prev_errno != ERANGE) { fprintf(stderr, "[NN] poll_receive_representation: %d -> RANGE\n", prev_errno); prev_errno = ERANGE; } break; case EINVAL: /* Invalid timestamp. */ if (debug_errnos && prev_errno != EINVAL) { fprintf(stderr, "[NN] poll_receive_representation: %d -> EINVAL\n", prev_errno); prev_errno = EINVAL; } cw_clear_receive_buffer(); break; default: perror("cw_receive_representation"); // TODO: Perhaps this should be counted as test error return; } } return; } /** If we received a character on an earlier poll, check again to see if we need to revise the decision about whether it is the end of a word too. The function is called c_r because primary function in production code polls character, and only then in test code a representation is polled. @reviewed 2020-08-27 */ void receiver_poll_space_c_r(Receiver * xcwcp_receiver) { /* Recheck the receive buffer for end of word. */ /* We expect the receiver to contain a character, but we don't ask for it this time. The receiver should also store information about an inter-character-space. If it is longer than a regular inter-character-space, then the receiver will treat it as inter-word-space, and communicate it over is_iws. Don't use xcwcp_receiver.main_timer - it is used eclusively for marking initial "key down" events. Use local throw-away local_timer. */ struct timeval local_timer; gettimeofday(&local_timer, NULL); //fprintf(stderr, "receiver_poll_space(): %10ld : %10ld\n", local_timer.tv_sec, local_timer.tv_usec); received_data prod = { 0 }; LIBCW_TEST_FUT(cw_receive_character)(&local_timer, NULL, &prod.is_iws, NULL); if (prod.is_iws) { fprintf(stderr, "[II] Polled inter-word-space\n"); { /* Verification code. */ bool failure = false; received_data test = { 0 }; cw_test_executor_t * cte = xcwcp_receiver->cte; #if 0 /* cw_receive_character() will return through 'c' variable the last character that was polled before space. Maybe this is good, maybe this is bad, but this is the legacy behaviour that we will keep supporting. */ if (!cte->expect_op_int_errors_only(cte, c, "!=", ' ', "returned character should not be space")) { failure = true; } #endif cw_ret_t cwret = cw_receive_representation(&local_timer, test.representation, &test.is_iws, &test.is_error); if (!cte->expect_op_int_errors_only(cte, cwret, "==", CW_SUCCESS, "Getting representation during space")) { failure = true; } if (!cte->expect_op_int_errors_only(cte, test.is_iws, "==", prod.is_iws, "Comparing 'is inter-word-space' flags: %d, %d", test.is_iws, prod.is_iws)) { failure = true; } /* We are polling ' ' space here, so we expect that receiver will set 'is inter-word-space' flag to true. */ if (!cte->expect_op_int_errors_only(cte, true, "==", test.is_iws, "Evaluating 'is inter-word-space' flag")) { failure = true; } cte->expect_op_int(cte, false, "==", failure, "Polling inter-word-space"); g_tester.received_string[g_tester.received_string_i++] = ' '; } cw_clear_receive_buffer(); xcwcp_receiver->is_pending_inter_word_space = false; } else { /* We don't reset is_pending_inter_word_space. The space that currently lasts, and isn't long enough to be considered inter-word-space, may grow to become the inter-word-space. Or not. This growing of inter-character-space into inter-word-space may be terminated by incoming next tone (key down event) - the tone will mark beginning of new character within the same word. And since a new character begins, the flag will be reset (elsewhere). */ } return; } /** If we received a character on an earlier poll, check again to see if we need to revise the decision about whether it is the end of a word too. The function is called r_c because primary function in production code polls representation, and only then in test code a character is polled. @reviewed 2020-08-27 */ void receiver_poll_space_r_c(Receiver * xcwcp_receiver) { /* Recheck the receive buffer for end of word. */ /* We expect the receiver to contain a character, but we don't ask for it this time. The receiver should also store information about an inter-character-space. If it is longer than a regular inter-character-space, then the receiver will treat it as inter-word-space, and communicate it over is_iws. Don't use xcwcp_receiver.main_timer - it is used eclusively for marking initial "key down" events. Use local throw-away local_timer. */ struct timeval local_timer; gettimeofday(&local_timer, NULL); //fprintf(stderr, "receiver_poll_space_r_c(): %10ld : %10ld\n", local_timer.tv_sec, local_timer.tv_usec); received_data prod = { 0 }; LIBCW_TEST_FUT(cw_receive_representation)(&local_timer, prod.representation, &prod.is_iws, NULL); if (prod.is_iws) { fprintf(stderr, "[II] Polled inter-word-space\n"); { /* Verification code. */ bool failure = false; received_data test = { 0 }; cw_test_executor_t * cte = xcwcp_receiver->cte; #if 0 /* cw_receive_character() will return through 'c' variable the last character that was polled before space. Maybe this is good, maybe this is bad, but this is the legacy behaviour that we will keep supporting. */ if (!cte->expect_op_int_errors_only(cte, c, "!=", ' ', "returned character should not be space")) { failure = true; } #endif cw_ret_t cwret = cw_receive_character(&local_timer, &test.character, &test.is_iws, &test.is_error); if (!cte->expect_op_int_errors_only(cte, cwret, "==", CW_SUCCESS, "Getting character during space")) { failure = true; } if (!cte->expect_op_int_errors_only(cte, test.is_iws, "==", prod.is_iws, "Comparing 'is inter-word-space' flags: %d, %d", test.is_iws, prod.is_iws)) { failure = true; } /* We are polling ' ' space here, so we expect that receiver will set 'is inter-word-space' flag to true. */ if (!cte->expect_op_int_errors_only(cte, true, "==", test.is_iws, "Evaluating 'is inter-word-space' flag")) { failure = true; } cte->expect_op_int(cte, false, "==", failure, "Polling inter-word-space"); g_tester.received_string[g_tester.received_string_i++] = ' '; } cw_clear_receive_buffer(); xcwcp_receiver->is_pending_inter_word_space = false; } else { /* We don't reset is_pending_inter_word_space. The space that currently lasts, and isn't long enough to be considered inter-word-space, may grow to become the inter-word-space. Or not. This growing of inter-character-space into inter-word-space may be terminated by incoming next tone (key down event) - the tone will mark beginning of new character within the same word. And since a new character begins, the flag will be reset (elsewhere). */ } return; } void receiver_sk_event(Receiver * xcwcp_receiver, bool is_down) { gettimeofday(&xcwcp_receiver->main_timer, NULL); //fprintf(stderr, "time on Skey down: %10ld : %10ld\n", xcwcp_receiver->main_timer.tv_sec, xcwcp_receiver->main_timer.tv_usec); cw_notify_straight_key_event(is_down); return; } /* Code that generates info about timing of input events for receiver. We could generate the info and the events using a big array of timestamps and a call to usleep(), but instead we are using a new generator that can inform us when marks/spaces start. */ void * receiver_input_generator_fn(void * arg) { cw_rec_tester_t * tester = arg; /* Start sending the test string. Registered callback will be called on every mark/space. Enqueue only initial part of string, just to start sending, the rest should be sent by 'low watermark' callback. */ cw_gen_start(tester->gen); for (int i = 0; i < 5; i++) { const char c = tester->input_string[tester->input_string_i]; if ('\0' == c) { /* A very short input string. */ break; } else { cw_gen_enqueue_character(tester->gen, c); tester->input_string_i++; } } /* Wait for all characters to be played out. */ cw_tq_wait_for_level_internal(tester->gen->tq, 0); cw_usleep_internal(1000 * 1000); cw_gen_delete(&tester->gen); tester->generating_in_progress = false; const int receive_correctness = cw_rec_tester_evaluate_receive_correctness(tester); if (g_xcwcp_receiver.cte->expect_op_int(g_xcwcp_receiver.cte, 0, "==", receive_correctness, "Final comparison of receive correctness")) { fprintf(stderr, "[II] Test result: success\n"); } else { fprintf(stderr, "[EE] Test result: failure\n"); } return NULL; } void tester_start_test_code(cw_rec_tester_t * tester) { tester->generating_in_progress = true; cw_rec_tester_init_text_buffers(tester, 1); /* Using Null sound system because this generator is only used to enqueue text and control key. Sound will be played by main generator used by xcwcp */ cw_gen_config_t gen_conf = { .sound_system = CW_AUDIO_NULL, .sound_device = { 0 } }; tester->gen = cw_gen_new(&gen_conf); cw_tq_register_low_level_callback_internal(tester->gen->tq, low_tone_queue_callback, tester, 5); cw_key_register_generator(&tester->key, tester->gen); cw_gen_register_value_tracking_callback_internal(tester->gen, test_callback_func, &g_xcwcp_receiver); /* TODO: use full range of allowed speeds. */ cwtest_param_ranger_init(&tester->speed_ranger, 6 /* CW_SPEED_MIN */, 40 /* CW_SPEED_MAX */, 1, cw_gen_get_speed(tester->gen)); cwtest_param_ranger_set_interval_sec(&tester->speed_ranger, 4); cwtest_param_ranger_set_plateau_length(&tester->speed_ranger, 6); pthread_create(&tester->receiver_test_code_thread_id, NULL, receiver_input_generator_fn, tester); } void tester_stop_test_code(cw_rec_tester_t * tester) { pthread_cancel(tester->receiver_test_code_thread_id); } void test_callback_func(void * arg, int key_state) { /* Inform libcw receiver about new state of straight key ("sk"). libcw receiver will process the new state and we will later try to poll a character or space from it. */ Receiver * xcwcp_receiver = (Receiver *) arg; //fprintf(stderr, "Callback function, key state = %d\n", key_state); receiver_sk_event(xcwcp_receiver, key_state); } /** @reviewed 2020-08-27 */ cwt_retv legacy_api_test_rec_poll(cw_test_executor_t * cte) { const cwt_retv retv1 = legacy_api_test_rec_poll_inner(cte, true); const cwt_retv retv2 = legacy_api_test_rec_poll_inner(cte, false); if (cwt_retv_ok == retv1 && cwt_retv_ok == retv2) { return cwt_retv_ok; } else { return cwt_retv_err; } } static cwt_retv legacy_api_test_rec_poll_inner(cw_test_executor_t * cte, bool c_r) { cte->print_test_header(cte, __func__); if (c_r) { cte->log_info(cte, "Test mode: poll character, verify by polling representation\n"); } else { cte->log_info(cte, "Test mode: poll representation, verify by polling character\n"); } if (CW_SUCCESS != cw_generator_new(cte->current_gen_conf.sound_system, cte->current_gen_conf.sound_device)) { fprintf(stderr, "failed to create generator\n"); return cwt_retv_err; } cw_rec_tester_init(&g_tester); cw_clear_receive_buffer(); cw_set_frequency(cte->config->frequency); cw_generator_start(); cw_enable_adaptive_receive(); /* Register handler as the CW library keying event callback. The handler called back by libcw is important because it's used to send to libcw information about timings of events (key down and key up events) through xcwcp_receiver.main_timer. Without the callback the library can play sounds as key or paddles are pressed, but (since it doesn't receive timing parameters) it won't be able to identify entered Morse code. */ cw_register_keying_callback(xcwcp_handle_libcw_keying_event, &g_xcwcp_receiver.main_timer); gettimeofday(&g_xcwcp_receiver.main_timer, NULL); //fprintf(stderr, "time on aux config: %10ld : %10ld\n", xcwcp_receiver.main_timer.tv_sec, xcwcp_receiver.main_timer.tv_usec); /* Prepare xcwcp_receiver object. */ memset(&g_xcwcp_receiver, 0, sizeof (g_xcwcp_receiver)); g_xcwcp_receiver.cte = cte; g_xcwcp_receiver.c_r = c_r; /* Start thread with test code. */ tester_start_test_code(&g_tester); while (g_tester.generating_in_progress) { /* At 60WPM, a dot is 20ms, so polling for the maximum speed library needs a 10ms timeout. */ usleep(10); receiver_poll_receiver(&g_xcwcp_receiver); #if 1 int new_speed = 0; if (cwtest_param_ranger_get_next(&g_tester.speed_ranger, &new_speed)) { cw_gen_set_speed(g_tester.gen, new_speed); } #endif } /* Stop thread with test code. TODO: Is this really needed? The thread should already be stopped if we get here. Calling this function leads to this problem in valgrind: ==2402== by 0x596DEDA: pthread_cancel_init (unwind-forcedunwind.c:52) ==2402== by 0x596A4EF: pthread_cancel (pthread_cancel.c:38) ==2402== by 0x1118BE: tester_stop_test_code (libcw_legacy_api_tests_rec_poll.c:958) ==2402== by 0x1118BE: legacy_api_test_rec_poll_inner (libcw_legacy_api_tests_rec_poll.c:1181) ==2402== by 0x111C6D: legacy_api_test_rec_poll (libcw_legacy_api_tests_rec_poll.c:1098) ==2402== by 0x11E1D5: iterate_over_test_objects (test_framework.c:1372) ==2402== by 0x11E1D5: iterate_over_sound_systems (test_framework.c:1329) ==2402== by 0x11E1D5: iterate_over_topics (test_framework.c:1306) ==2402== by 0x11E1D5: cw_test_main_test_loop (test_framework.c:1282) ==2402== by 0x10E703: main (test_main.c:130) */ /* TODO: remove this function altogether. */ //tester_stop_test_code(&g_tester); /* Tell legacy objects of libcw (those in production code) to stop working. */ cw_complete_reset(); cw_generator_stop(); cw_generator_delete(); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } void low_tone_queue_callback(void * arg) { cw_rec_tester_t * tester = (cw_rec_tester_t *) arg; for (int i = 0; i < tester->characters_to_enqueue; i++) { const char c = tester->input_string[tester->input_string_i]; if ('\0' == c) { /* Unregister ourselves. */ cw_tq_register_low_level_callback_internal(tester->gen->tq, NULL, NULL, 0); break; } else { cw_gen_enqueue_character(tester->gen, c); tester->input_string_i++; } } return; } unixcw-3.6.0/src/libcw/tests/test_framework.h0000644000175000017500000004043414000344554016220 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_TEST_FRAMEWORK_H_ #define _LIBCW_TEST_FRAMEWORK_H_ #include #include #include #include #include #include "test_framework_tools.h" /* Total width of test name + test status printed in console (without ending '\n'). Remember that some consoles have width = 80. Not everyone works in X. */ #define default_cw_test_print_n_chars 75 typedef struct { /* TODO: these should be unsigned. */ int successes; int failures; } cw_test_stats_t; /* Test framework function return values. The values indicate success/failure of test framework functions, not of tests themselves. Success/failure of tests is recorded in cw_test_stats_t variables in cw test executor. Failure of test framework function may happen e.g. because some malloc() fails, or because some helper function fails. */ typedef enum { cwt_retv_err = -1, cwt_retv_ok = 0, } cwt_retv; struct cw_test_set_t; struct cw_test_executor_t; typedef struct cw_test_executor_t { cw_config_t * config; char msg_prefix[32]; /* gcc on Alpine 3.12 doesn't like these variables to be called stdout/stderr. */ FILE * file_out; FILE * file_err; resource_meas resource_meas; bool use_resource_meas; #ifndef __FreeBSD__ /* TODO: add calculation of test duration on BSD. */ /* Uptime at begin and end of tests. Used to measure duration of tests. */ long uptime_begin; long uptime_end; #endif /* Type suitable for srand48(). */ long int random_seed; /* Sound system and test topic currently tested. Should be set right before calling a specific test function. 'current' word highlights the fact that this is configuration for current test. */ cw_gen_config_t current_gen_conf; int current_topic; /* Limit of characters that can be printed to console in one row. */ int console_n_cols; /* This array holds stats for all distinct sound systems, and is indexed by cw_audio_systems enum. This means that first row will not be used (because CW_AUDIO_NONE==0 is not a distinct sound system). */ cw_test_stats_t all_stats[CW_SOUND_SYSTEM_LAST + 1][LIBCW_TEST_TOPIC_MAX]; cw_test_stats_t * stats; /* Pointer to current stats (one of fields of ::all_stats[][]). */ /** Verify that operator @param operator is satisfied for @param received_value and @param expected_value Use the function to verify that a result of libcw function or of other operation (@param received_value) is in proper relation to expected value (@param expected_value). expect_op_X(): print log specified by @param fmt and following args regardless of results of the verification. Include test result (success or failure) of the test in statistics. expect_op_X_errors_only(): print log specified by @param fmt and following args only if the application of operator has shown that the values DON'T satisfy the operator, which is regarded as failure of expectation, i.e. an error. Include test result in statistics only if the result was an error. You shouldn't put newline character at end of formatting string. The messages printed by these functions will be followed by "[ OK ]" or "[FAIL]" tags, and the newline would put these tags in new line, decreasing readability of test results. @return true if this comparison shows that the values satisfy the operator @return false otherwise */ bool (* expect_op_int)(struct cw_test_executor_t * self, int expected_value, const char * operator, int received_value, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); bool (* expect_op_int_errors_only)(struct cw_test_executor_t * self, int expected_value, const char * operator, int received_value, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); bool (* expect_op_float)(struct cw_test_executor_t * self, float expected_value, const char * operator, float received_value, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); bool (* expect_op_float_errors_only)(struct cw_test_executor_t * self, float expected_value, const char * operator, float received_value, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); /** Verify that @param received_value is between @param expected_lower and @param expect_higher (inclusive) Use the function to verify that a result of libcw function or of other operation (@param received_value) is larger than or equal to @param expected_lower, and at the same time is smaller than or equal to @param expected_higher. expect_op_X(): print log specified by @param fmt and following args regardless of results of the verification. Include test result (success or failure) of the test in statistics. expect_op_X_errors_only(): print log specified by @param fmt and following args only if the application of operator has shown that the values DON'T satisfy the operator, which is regarded as failure of expectation, i.e. an error. Include test result in statistics only if the result was an error. @return true if this comparison shows that the given value is within specified range @return false otherwise */ bool (* expect_between_int)(struct cw_test_executor_t * self, int expected_lower, int received_value, int expected_higher, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); bool (* expect_between_int_errors_only)(struct cw_test_executor_t * self, int expected_lower, int received_value, int expected_higher, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); /** Verify that @param pointer is NULL pointer expect_op_X(): print log specified by @param fmt and following args regardless of results of the verification. Include test result (success or failure) of the test in statistics. expect_op_X_errors_only(): print log specified by @param fmt and following args only if verification was negative. Include test result in statistics only if the result was an error. @return true if this comparison shows that the pointer is truly a NULL pointer @return false otherwise */ bool (* expect_null_pointer)(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); bool (* expect_null_pointer_errors_only)(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); /** Verify that @param pointer is valid (non-NULL) pointer Print log specified by @param fmt and following args regardless of results of the verification or (in case of _errors_only() variant) only if the check has shown that pointer *IS* NULL pointer, which is regarded as failure of expectation, i.e. an error. Right now there are no additional checks of validity of the pointer. Function only checks if it is non-NULL pointer. @return true if this comparison shows that the pointer is a non-NULL pointer @return false otherwise */ bool (* expect_valid_pointer)(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); bool (* expect_valid_pointer_errors_only)(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); /** An assert - not much to explain */ void (* assert2)(struct cw_test_executor_t * self, bool condition, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); /** @brief Print an informative header with information about current test Call this function on top of a test function to display some basic information about test: current test topic, current sound system and name of test function. This should get a basic overview of what is about to be tested now. Name of the function is usually passed through @param fmt argument. @param fmt can be also printf()-like format string, followed by additional arguments */ void (* print_test_header)(struct cw_test_executor_t * self, const char * fmt, ...) __attribute__ ((format (printf, 2, 3))); /** @brief Print a not-so-informative test footer Call the function at the very end of test function to display indication that test function with name @param function_name has completed its work. */ void (* print_test_footer)(struct cw_test_executor_t * self, const char * function_name); /** \brief Process command line arguments of test executable @return 0 on success: program can continue after arguments have been processes (or no arguments were provided), Function exits with EXIT_FAILURE status when function encountered errors during processing of command line args. Help text is printed before the exit. Function exits with EXIT_SUCCESS status when "help" option was requested. Help text is printed before the exit. @return cwt_retv_ok on success @return cwt_retv_err otherwise */ cwt_retv (* process_args)(struct cw_test_executor_t * self, int argc, char * const argv[]); /** \brief Get count of loops of a test Get a loop limit for some repeating tests. It may be a small value for quick tests, or it may be a large value for long-term tests. */ int (* get_loops_count)(struct cw_test_executor_t * self); /** @brief Print summary of program's arguments and options that are in effect Print names of test topics that will be tested and of sound systems that will be used in tests. Call this function after processing command line arguments with ::process_args(). */ void (* print_test_options)(struct cw_test_executor_t * self); /** @brief Print a table with summary of test statistics The statistics are presented as a table. There are columns with test topics and rows with sound modules. Each table cell contains a total number of tests in given category and number of errors (failed test functions) in that category. */ void (* print_test_stats)(struct cw_test_executor_t * self); /** @brief Get label of currently tested test topic Return pointer to static string buffer with label of currently tested test topic. Notice: if the topic is not set, function returns "unknown" text label. */ const char * (* get_current_topic_label)(struct cw_test_executor_t * self); /** @brief Get label of currently used sound system Return pointer to static string buffer with label of currently used sound system Notice: if the sound system is not set, function returns "None" text label. */ const char * (* get_current_sound_system_label)(struct cw_test_executor_t * self); /** @brief Get name of current sound device */ const char * (* get_current_sound_device)(struct cw_test_executor_t * self); /** Log information to cw_test_executor_t::stdout file (if it is set). Add "[II]" mark at the beginning. Add message prefix at the beginning. Don't add newline character at the end. @return number of characters printed */ int (* log_info)(struct cw_test_executor_t * self, const char * fmt, ...) __attribute__ ((format (printf, 2, 3))); /** Log text to cw_test_executor_t::stdout file (if it is set). Don't add "[II]" mark at the beginning. Don't add message prefix at the beginning. Don't add newline character at the end. */ void (* log_info_cont)(struct cw_test_executor_t * self, const char * fmt, ...) __attribute__ ((format (printf, 2, 3))); /** Flush file descriptor used to log info messages */ void (* flush_info)(struct cw_test_executor_t * self); /** Log error to cw_test_executor_t::stdout file (if it is set). Add "[EE]" mark at the beginning. Add message prefix at the beginning. Don't add newline character at the end. */ void (* log_error)(struct cw_test_executor_t * self, const char * fmt, ...) __attribute__ ((format (printf, 2, 3))); /** See whether or not a given test topic was requested from command line. By default, if not specified in command line, all test topics are requested. */ bool (* test_topic_was_requested)(struct cw_test_executor_t * self, int libcw_test_topic); /** See whether or not a given sound system was requested from command line. By default, if not specified in command line, all sound systems are requested. However, if a host machine does not support some sound system (e.g. because a library is missing), such sound system is excluded from list of requested sound systems. */ bool (* sound_system_was_requested)(struct cw_test_executor_t * self, cw_sound_system sound_system); /** @brief Main test loop that walks through given @param @test_sets and executes all test function specified in @param test_sets @return cwt_retv_ok if test framework worked correctly @return cwt_retv_err if test framework failed at some point */ cwt_retv (* main_test_loop)(struct cw_test_executor_t * cte, struct cw_test_set_t * test_sets); /** @brief Get total count of errors after main_test_loop() has returned The function can be used to check if there were any errors reported by tested functions. @return 0 if no errors were reported by test functions @return some positive value otherwise */ unsigned int (* get_total_errors_count)(struct cw_test_executor_t * cte); } cw_test_executor_t; /** @brief Initialize cw_text_executor_t object @param cte Some messages printed by logger function of cw_text_executor_t object will be prefixed with @param msg_prefix. No resources are allocated in @param cte during the call, so there is no "deinit" function. */ void cw_test_init(cw_test_executor_t * self, FILE * stdout, FILE * stderr, const char * msg_prefix); /** @brief Deinitialize cw_test_executor_t object @param cte */ void cw_test_deinit(cw_test_executor_t * self); /** @return cwt_retv_ok if test framework managed to successfully run the test (regardless of results of the test itself) @return cwt_retv_err if test framework failed to run the test (the test didn't manage to test production code) */ typedef cwt_retv (* cw_test_function_t)(cw_test_executor_t * cte); typedef enum cw_test_set_valid { LIBCW_TEST_SET_INVALID, LIBCW_TEST_SET_VALID, } cw_test_set_valid; typedef enum cw_test_api_tested { LIBCW_TEST_API_LEGACY, /* Tests of functions from libcw.h. Legacy API that does not allow using multiple gen/key/rec objects. */ LIBCW_TEST_API_MODERN, /* Tests of internal functions that operate on explicit gen/key/rec objects (functions that accept such objects as arguments). */ } cw_test_api_tested; typedef struct cw_test_object_t { cw_test_function_t test_function; const char * name; /* Unique label/name of test function, used to execute only one test function from whole set. Can be empty/NULL. */ bool is_quick; /* Is the execution of the function quick? Is the time short enough to execute the function during "make check" target during builds? */ } cw_test_object_t; typedef struct cw_test_set_t { cw_test_set_valid set_valid; /* Invalid test set is a guard element in array of test sets. */ cw_test_api_tested api_tested; /* libcw areas tested by given test set. */ int tested_areas[LIBCW_TEST_TOPIC_MAX]; /* Distinct sound systems that need to be configured to test given test set. This array is indexed from zero. End of this array is marked by guard element CW_AUDIO_NONE. */ cw_sound_system tested_sound_systems[CW_SOUND_SYSTEM_LAST + 1]; cw_test_object_t test_objects[100]; /* Right now my test sets have only a few test functions. For now 100 is a safe limit. */ } cw_test_set_t; #define LIBCW_TEST_STRINGIFY(x) #x #define LIBCW_TEST_TOSTRING(x) LIBCW_TEST_STRINGIFY(x) #define LIBCW_TEST_FUNCTION_INSERT(function_pointer, _is_quick_) { .test_function = (function_pointer), .name = LIBCW_TEST_TOSTRING(function_pointer), .is_quick = (_is_quick_) } /* FUT = "Function under test". A function from libcw library that is the subject of a test. */ #define LIBCW_TEST_FUT(x) x /* At what intervals the CPU usage will be measured. */ #define LIBCW_TEST_MEAS_CPU_MEAS_INTERVAL_MSECS 200 /* What is the top CPU usage threshold during test function's execution. */ #define LIBCW_TEST_MEAS_CPU_OK_THRESHOLD_PERCENT 4.0F #endif /* #ifndef _LIBCW_TEST_FRAMEWORK_H_ */ unixcw-3.6.0/src/libcw/tests/Makefile.in0000644000175000017500000021340514001105754015056 00000000000000# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) # Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ check_PROGRAMS = libcw_tests$(EXEEXT) subdir = src/libcw/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/src/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__objects_1 = libcw_tests-libcw_legacy_api_tests.$(OBJEXT) \ libcw_tests-libcw_legacy_api_tests_rec_poll.$(OBJEXT) am__objects_2 = libcw_tests-libcw_data_tests.$(OBJEXT) \ libcw_tests-libcw_gen_tests.$(OBJEXT) \ libcw_tests-libcw_gen_tests_state_callback.$(OBJEXT) \ libcw_tests-libcw_rec_tests.$(OBJEXT) \ libcw_tests-libcw_utils_tests.$(OBJEXT) \ libcw_tests-libcw_key_tests.$(OBJEXT) \ libcw_tests-libcw_debug_tests.$(OBJEXT) \ libcw_tests-libcw_tq_tests.$(OBJEXT) am__objects_3 = libcw_tests-libcw_test_tq_short_space.$(OBJEXT) am__objects_4 = libcw_tests-test_framework.$(OBJEXT) \ libcw_tests-test_framework_tools.$(OBJEXT) am_libcw_tests_OBJECTS = $(am__objects_1) $(am__objects_2) \ $(am__objects_3) $(am__objects_4) \ libcw_tests-test_data.$(OBJEXT) \ libcw_tests-test_sets.$(OBJEXT) \ libcw_tests-test_main.$(OBJEXT) libcw_tests_OBJECTS = $(am_libcw_tests_OBJECTS) am__DEPENDENCIES_1 = libcw_tests_DEPENDENCIES = \ $(top_builddir)/src/cwutils/lib_libcw_tests.a \ $(top_builddir)/src/cwutils/lib_rec_tests.a \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libcw_tests_SOURCES) DIST_SOURCES = $(libcw_tests_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` RECHECK_LOGS = $(TEST_LOGS) AM_RECURSIVE_TARGETS = check recheck TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ $(top_srcdir)/test-driver README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CC_LINKS_SO = @CC_LINKS_SO@ CFLAGS = @CFLAGS@ CFLAG_PIC = @CFLAG_PIC@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DL_LIB = @DL_LIB@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ GZIP = @GZIP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTL_LIB = @INTL_LIB@ LD = @LD@ LDCONFIG = @LDCONFIG@ LDFLAGS = @LDFLAGS@ LD_LINKS_SO = @LD_LINKS_SO@ LIBCW_NDEBUG = @LIBCW_NDEBUG@ LIBCW_VERSION = @LIBCW_VERSION@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MOC = @MOC@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OSS_LIB = @OSS_LIB@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ QT5_CFLAGS = @QT5_CFLAGS@ QT5_LIBS = @QT5_LIBS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SRC_SUBDIRS = @SRC_SUBDIRS@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ TESTS = $(check_SCRIPTS) # run test programs check_SCRIPTS = libcw_test_quick.sh # empty # List of files that implement tests of specific bug fixes. bug_test_files = \ libcw_test_tq_short_space.c \ libcw_test_tq_short_space.h test_framework_files = \ test_framework.c \ test_framework.h \ test_framework_tools.c \ test_framework_tools.h modern_api_test_files = \ libcw_data_tests.c \ libcw_data_tests.h \ libcw_gen_tests.c \ libcw_gen_tests.h \ libcw_gen_tests_state_callback.c \ libcw_gen_tests_state_callback.h \ libcw_rec_tests.c \ libcw_rec_tests.h \ libcw_utils_tests.c \ libcw_utils_tests.h \ libcw_key_tests.c \ libcw_key_tests.h \ libcw_debug_tests.c \ libcw_debug_tests.h \ libcw_tq_tests.c \ libcw_tq_tests.h legacy_api_test_files = \ libcw_legacy_api_tests.c \ libcw_legacy_api_tests.h \ libcw_legacy_api_tests_rec_poll.c \ libcw_legacy_api_tests_rec_poll.h libcw_tests_SOURCES = \ $(legacy_api_test_files) \ $(modern_api_test_files) \ $(bug_test_files) \ $(test_framework_files) \ test_data.c \ test_sets.c \ test_main.c libcw_tests_CPPFLAGS = $(AM_CPPFLAGS) -DLIBCW_UNIT_TESTS #libcw_tests_CFLAGS = -I../../ # Target-specific linker flags (objects to link). Order is important: # first static libraries then dynamic. Otherwise linker may not find # symbols from the dynamic library. libcw_tests_LDADD = $(top_builddir)/src/cwutils/lib_libcw_tests.a \ $(top_builddir)/src/cwutils/lib_rec_tests.a $(INTL_LIB) -lm \ -lpthread $(DL_LIB) -L../.libs -lcw_test EXTRA_DIST = \ $(check_SCRIPTS) \ count_functions_under_test.py all: all-am .SUFFIXES: .SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/libcw/tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/libcw/tests/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-checkPROGRAMS: @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list libcw_tests$(EXEEXT): $(libcw_tests_OBJECTS) $(libcw_tests_DEPENDENCIES) $(EXTRA_libcw_tests_DEPENDENCIES) @rm -f libcw_tests$(EXEEXT) $(AM_V_CCLD)$(LINK) $(libcw_tests_OBJECTS) $(libcw_tests_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_data_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_debug_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_gen_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_gen_tests_state_callback.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_key_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_legacy_api_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_legacy_api_tests_rec_poll.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_rec_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_test_tq_short_space.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_tq_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-libcw_utils_tests.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-test_data.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-test_framework.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-test_framework_tools.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-test_main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcw_tests-test_sets.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< libcw_tests-libcw_legacy_api_tests.o: libcw_legacy_api_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_legacy_api_tests.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_legacy_api_tests.Tpo -c -o libcw_tests-libcw_legacy_api_tests.o `test -f 'libcw_legacy_api_tests.c' || echo '$(srcdir)/'`libcw_legacy_api_tests.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_legacy_api_tests.Tpo $(DEPDIR)/libcw_tests-libcw_legacy_api_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_legacy_api_tests.c' object='libcw_tests-libcw_legacy_api_tests.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_legacy_api_tests.o `test -f 'libcw_legacy_api_tests.c' || echo '$(srcdir)/'`libcw_legacy_api_tests.c libcw_tests-libcw_legacy_api_tests.obj: libcw_legacy_api_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_legacy_api_tests.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_legacy_api_tests.Tpo -c -o libcw_tests-libcw_legacy_api_tests.obj `if test -f 'libcw_legacy_api_tests.c'; then $(CYGPATH_W) 'libcw_legacy_api_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_legacy_api_tests.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_legacy_api_tests.Tpo $(DEPDIR)/libcw_tests-libcw_legacy_api_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_legacy_api_tests.c' object='libcw_tests-libcw_legacy_api_tests.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_legacy_api_tests.obj `if test -f 'libcw_legacy_api_tests.c'; then $(CYGPATH_W) 'libcw_legacy_api_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_legacy_api_tests.c'; fi` libcw_tests-libcw_legacy_api_tests_rec_poll.o: libcw_legacy_api_tests_rec_poll.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_legacy_api_tests_rec_poll.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_legacy_api_tests_rec_poll.Tpo -c -o libcw_tests-libcw_legacy_api_tests_rec_poll.o `test -f 'libcw_legacy_api_tests_rec_poll.c' || echo '$(srcdir)/'`libcw_legacy_api_tests_rec_poll.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_legacy_api_tests_rec_poll.Tpo $(DEPDIR)/libcw_tests-libcw_legacy_api_tests_rec_poll.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_legacy_api_tests_rec_poll.c' object='libcw_tests-libcw_legacy_api_tests_rec_poll.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_legacy_api_tests_rec_poll.o `test -f 'libcw_legacy_api_tests_rec_poll.c' || echo '$(srcdir)/'`libcw_legacy_api_tests_rec_poll.c libcw_tests-libcw_legacy_api_tests_rec_poll.obj: libcw_legacy_api_tests_rec_poll.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_legacy_api_tests_rec_poll.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_legacy_api_tests_rec_poll.Tpo -c -o libcw_tests-libcw_legacy_api_tests_rec_poll.obj `if test -f 'libcw_legacy_api_tests_rec_poll.c'; then $(CYGPATH_W) 'libcw_legacy_api_tests_rec_poll.c'; else $(CYGPATH_W) '$(srcdir)/libcw_legacy_api_tests_rec_poll.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_legacy_api_tests_rec_poll.Tpo $(DEPDIR)/libcw_tests-libcw_legacy_api_tests_rec_poll.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_legacy_api_tests_rec_poll.c' object='libcw_tests-libcw_legacy_api_tests_rec_poll.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_legacy_api_tests_rec_poll.obj `if test -f 'libcw_legacy_api_tests_rec_poll.c'; then $(CYGPATH_W) 'libcw_legacy_api_tests_rec_poll.c'; else $(CYGPATH_W) '$(srcdir)/libcw_legacy_api_tests_rec_poll.c'; fi` libcw_tests-libcw_data_tests.o: libcw_data_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_data_tests.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_data_tests.Tpo -c -o libcw_tests-libcw_data_tests.o `test -f 'libcw_data_tests.c' || echo '$(srcdir)/'`libcw_data_tests.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_data_tests.Tpo $(DEPDIR)/libcw_tests-libcw_data_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_data_tests.c' object='libcw_tests-libcw_data_tests.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_data_tests.o `test -f 'libcw_data_tests.c' || echo '$(srcdir)/'`libcw_data_tests.c libcw_tests-libcw_data_tests.obj: libcw_data_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_data_tests.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_data_tests.Tpo -c -o libcw_tests-libcw_data_tests.obj `if test -f 'libcw_data_tests.c'; then $(CYGPATH_W) 'libcw_data_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_data_tests.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_data_tests.Tpo $(DEPDIR)/libcw_tests-libcw_data_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_data_tests.c' object='libcw_tests-libcw_data_tests.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_data_tests.obj `if test -f 'libcw_data_tests.c'; then $(CYGPATH_W) 'libcw_data_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_data_tests.c'; fi` libcw_tests-libcw_gen_tests.o: libcw_gen_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_gen_tests.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_gen_tests.Tpo -c -o libcw_tests-libcw_gen_tests.o `test -f 'libcw_gen_tests.c' || echo '$(srcdir)/'`libcw_gen_tests.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_gen_tests.Tpo $(DEPDIR)/libcw_tests-libcw_gen_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_gen_tests.c' object='libcw_tests-libcw_gen_tests.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_gen_tests.o `test -f 'libcw_gen_tests.c' || echo '$(srcdir)/'`libcw_gen_tests.c libcw_tests-libcw_gen_tests.obj: libcw_gen_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_gen_tests.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_gen_tests.Tpo -c -o libcw_tests-libcw_gen_tests.obj `if test -f 'libcw_gen_tests.c'; then $(CYGPATH_W) 'libcw_gen_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_gen_tests.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_gen_tests.Tpo $(DEPDIR)/libcw_tests-libcw_gen_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_gen_tests.c' object='libcw_tests-libcw_gen_tests.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_gen_tests.obj `if test -f 'libcw_gen_tests.c'; then $(CYGPATH_W) 'libcw_gen_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_gen_tests.c'; fi` libcw_tests-libcw_gen_tests_state_callback.o: libcw_gen_tests_state_callback.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_gen_tests_state_callback.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_gen_tests_state_callback.Tpo -c -o libcw_tests-libcw_gen_tests_state_callback.o `test -f 'libcw_gen_tests_state_callback.c' || echo '$(srcdir)/'`libcw_gen_tests_state_callback.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_gen_tests_state_callback.Tpo $(DEPDIR)/libcw_tests-libcw_gen_tests_state_callback.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_gen_tests_state_callback.c' object='libcw_tests-libcw_gen_tests_state_callback.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_gen_tests_state_callback.o `test -f 'libcw_gen_tests_state_callback.c' || echo '$(srcdir)/'`libcw_gen_tests_state_callback.c libcw_tests-libcw_gen_tests_state_callback.obj: libcw_gen_tests_state_callback.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_gen_tests_state_callback.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_gen_tests_state_callback.Tpo -c -o libcw_tests-libcw_gen_tests_state_callback.obj `if test -f 'libcw_gen_tests_state_callback.c'; then $(CYGPATH_W) 'libcw_gen_tests_state_callback.c'; else $(CYGPATH_W) '$(srcdir)/libcw_gen_tests_state_callback.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_gen_tests_state_callback.Tpo $(DEPDIR)/libcw_tests-libcw_gen_tests_state_callback.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_gen_tests_state_callback.c' object='libcw_tests-libcw_gen_tests_state_callback.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_gen_tests_state_callback.obj `if test -f 'libcw_gen_tests_state_callback.c'; then $(CYGPATH_W) 'libcw_gen_tests_state_callback.c'; else $(CYGPATH_W) '$(srcdir)/libcw_gen_tests_state_callback.c'; fi` libcw_tests-libcw_rec_tests.o: libcw_rec_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_rec_tests.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_rec_tests.Tpo -c -o libcw_tests-libcw_rec_tests.o `test -f 'libcw_rec_tests.c' || echo '$(srcdir)/'`libcw_rec_tests.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_rec_tests.Tpo $(DEPDIR)/libcw_tests-libcw_rec_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_rec_tests.c' object='libcw_tests-libcw_rec_tests.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_rec_tests.o `test -f 'libcw_rec_tests.c' || echo '$(srcdir)/'`libcw_rec_tests.c libcw_tests-libcw_rec_tests.obj: libcw_rec_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_rec_tests.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_rec_tests.Tpo -c -o libcw_tests-libcw_rec_tests.obj `if test -f 'libcw_rec_tests.c'; then $(CYGPATH_W) 'libcw_rec_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_rec_tests.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_rec_tests.Tpo $(DEPDIR)/libcw_tests-libcw_rec_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_rec_tests.c' object='libcw_tests-libcw_rec_tests.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_rec_tests.obj `if test -f 'libcw_rec_tests.c'; then $(CYGPATH_W) 'libcw_rec_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_rec_tests.c'; fi` libcw_tests-libcw_utils_tests.o: libcw_utils_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_utils_tests.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_utils_tests.Tpo -c -o libcw_tests-libcw_utils_tests.o `test -f 'libcw_utils_tests.c' || echo '$(srcdir)/'`libcw_utils_tests.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_utils_tests.Tpo $(DEPDIR)/libcw_tests-libcw_utils_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_utils_tests.c' object='libcw_tests-libcw_utils_tests.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_utils_tests.o `test -f 'libcw_utils_tests.c' || echo '$(srcdir)/'`libcw_utils_tests.c libcw_tests-libcw_utils_tests.obj: libcw_utils_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_utils_tests.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_utils_tests.Tpo -c -o libcw_tests-libcw_utils_tests.obj `if test -f 'libcw_utils_tests.c'; then $(CYGPATH_W) 'libcw_utils_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_utils_tests.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_utils_tests.Tpo $(DEPDIR)/libcw_tests-libcw_utils_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_utils_tests.c' object='libcw_tests-libcw_utils_tests.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_utils_tests.obj `if test -f 'libcw_utils_tests.c'; then $(CYGPATH_W) 'libcw_utils_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_utils_tests.c'; fi` libcw_tests-libcw_key_tests.o: libcw_key_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_key_tests.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_key_tests.Tpo -c -o libcw_tests-libcw_key_tests.o `test -f 'libcw_key_tests.c' || echo '$(srcdir)/'`libcw_key_tests.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_key_tests.Tpo $(DEPDIR)/libcw_tests-libcw_key_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_key_tests.c' object='libcw_tests-libcw_key_tests.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_key_tests.o `test -f 'libcw_key_tests.c' || echo '$(srcdir)/'`libcw_key_tests.c libcw_tests-libcw_key_tests.obj: libcw_key_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_key_tests.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_key_tests.Tpo -c -o libcw_tests-libcw_key_tests.obj `if test -f 'libcw_key_tests.c'; then $(CYGPATH_W) 'libcw_key_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_key_tests.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_key_tests.Tpo $(DEPDIR)/libcw_tests-libcw_key_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_key_tests.c' object='libcw_tests-libcw_key_tests.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_key_tests.obj `if test -f 'libcw_key_tests.c'; then $(CYGPATH_W) 'libcw_key_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_key_tests.c'; fi` libcw_tests-libcw_debug_tests.o: libcw_debug_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_debug_tests.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_debug_tests.Tpo -c -o libcw_tests-libcw_debug_tests.o `test -f 'libcw_debug_tests.c' || echo '$(srcdir)/'`libcw_debug_tests.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_debug_tests.Tpo $(DEPDIR)/libcw_tests-libcw_debug_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_debug_tests.c' object='libcw_tests-libcw_debug_tests.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_debug_tests.o `test -f 'libcw_debug_tests.c' || echo '$(srcdir)/'`libcw_debug_tests.c libcw_tests-libcw_debug_tests.obj: libcw_debug_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_debug_tests.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_debug_tests.Tpo -c -o libcw_tests-libcw_debug_tests.obj `if test -f 'libcw_debug_tests.c'; then $(CYGPATH_W) 'libcw_debug_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_debug_tests.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_debug_tests.Tpo $(DEPDIR)/libcw_tests-libcw_debug_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_debug_tests.c' object='libcw_tests-libcw_debug_tests.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_debug_tests.obj `if test -f 'libcw_debug_tests.c'; then $(CYGPATH_W) 'libcw_debug_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_debug_tests.c'; fi` libcw_tests-libcw_tq_tests.o: libcw_tq_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_tq_tests.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_tq_tests.Tpo -c -o libcw_tests-libcw_tq_tests.o `test -f 'libcw_tq_tests.c' || echo '$(srcdir)/'`libcw_tq_tests.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_tq_tests.Tpo $(DEPDIR)/libcw_tests-libcw_tq_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_tq_tests.c' object='libcw_tests-libcw_tq_tests.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_tq_tests.o `test -f 'libcw_tq_tests.c' || echo '$(srcdir)/'`libcw_tq_tests.c libcw_tests-libcw_tq_tests.obj: libcw_tq_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_tq_tests.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_tq_tests.Tpo -c -o libcw_tests-libcw_tq_tests.obj `if test -f 'libcw_tq_tests.c'; then $(CYGPATH_W) 'libcw_tq_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_tq_tests.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_tq_tests.Tpo $(DEPDIR)/libcw_tests-libcw_tq_tests.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_tq_tests.c' object='libcw_tests-libcw_tq_tests.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_tq_tests.obj `if test -f 'libcw_tq_tests.c'; then $(CYGPATH_W) 'libcw_tq_tests.c'; else $(CYGPATH_W) '$(srcdir)/libcw_tq_tests.c'; fi` libcw_tests-libcw_test_tq_short_space.o: libcw_test_tq_short_space.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_test_tq_short_space.o -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_test_tq_short_space.Tpo -c -o libcw_tests-libcw_test_tq_short_space.o `test -f 'libcw_test_tq_short_space.c' || echo '$(srcdir)/'`libcw_test_tq_short_space.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_test_tq_short_space.Tpo $(DEPDIR)/libcw_tests-libcw_test_tq_short_space.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_test_tq_short_space.c' object='libcw_tests-libcw_test_tq_short_space.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_test_tq_short_space.o `test -f 'libcw_test_tq_short_space.c' || echo '$(srcdir)/'`libcw_test_tq_short_space.c libcw_tests-libcw_test_tq_short_space.obj: libcw_test_tq_short_space.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-libcw_test_tq_short_space.obj -MD -MP -MF $(DEPDIR)/libcw_tests-libcw_test_tq_short_space.Tpo -c -o libcw_tests-libcw_test_tq_short_space.obj `if test -f 'libcw_test_tq_short_space.c'; then $(CYGPATH_W) 'libcw_test_tq_short_space.c'; else $(CYGPATH_W) '$(srcdir)/libcw_test_tq_short_space.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-libcw_test_tq_short_space.Tpo $(DEPDIR)/libcw_tests-libcw_test_tq_short_space.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libcw_test_tq_short_space.c' object='libcw_tests-libcw_test_tq_short_space.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-libcw_test_tq_short_space.obj `if test -f 'libcw_test_tq_short_space.c'; then $(CYGPATH_W) 'libcw_test_tq_short_space.c'; else $(CYGPATH_W) '$(srcdir)/libcw_test_tq_short_space.c'; fi` libcw_tests-test_framework.o: test_framework.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_framework.o -MD -MP -MF $(DEPDIR)/libcw_tests-test_framework.Tpo -c -o libcw_tests-test_framework.o `test -f 'test_framework.c' || echo '$(srcdir)/'`test_framework.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_framework.Tpo $(DEPDIR)/libcw_tests-test_framework.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_framework.c' object='libcw_tests-test_framework.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_framework.o `test -f 'test_framework.c' || echo '$(srcdir)/'`test_framework.c libcw_tests-test_framework.obj: test_framework.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_framework.obj -MD -MP -MF $(DEPDIR)/libcw_tests-test_framework.Tpo -c -o libcw_tests-test_framework.obj `if test -f 'test_framework.c'; then $(CYGPATH_W) 'test_framework.c'; else $(CYGPATH_W) '$(srcdir)/test_framework.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_framework.Tpo $(DEPDIR)/libcw_tests-test_framework.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_framework.c' object='libcw_tests-test_framework.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_framework.obj `if test -f 'test_framework.c'; then $(CYGPATH_W) 'test_framework.c'; else $(CYGPATH_W) '$(srcdir)/test_framework.c'; fi` libcw_tests-test_framework_tools.o: test_framework_tools.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_framework_tools.o -MD -MP -MF $(DEPDIR)/libcw_tests-test_framework_tools.Tpo -c -o libcw_tests-test_framework_tools.o `test -f 'test_framework_tools.c' || echo '$(srcdir)/'`test_framework_tools.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_framework_tools.Tpo $(DEPDIR)/libcw_tests-test_framework_tools.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_framework_tools.c' object='libcw_tests-test_framework_tools.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_framework_tools.o `test -f 'test_framework_tools.c' || echo '$(srcdir)/'`test_framework_tools.c libcw_tests-test_framework_tools.obj: test_framework_tools.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_framework_tools.obj -MD -MP -MF $(DEPDIR)/libcw_tests-test_framework_tools.Tpo -c -o libcw_tests-test_framework_tools.obj `if test -f 'test_framework_tools.c'; then $(CYGPATH_W) 'test_framework_tools.c'; else $(CYGPATH_W) '$(srcdir)/test_framework_tools.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_framework_tools.Tpo $(DEPDIR)/libcw_tests-test_framework_tools.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_framework_tools.c' object='libcw_tests-test_framework_tools.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_framework_tools.obj `if test -f 'test_framework_tools.c'; then $(CYGPATH_W) 'test_framework_tools.c'; else $(CYGPATH_W) '$(srcdir)/test_framework_tools.c'; fi` libcw_tests-test_data.o: test_data.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_data.o -MD -MP -MF $(DEPDIR)/libcw_tests-test_data.Tpo -c -o libcw_tests-test_data.o `test -f 'test_data.c' || echo '$(srcdir)/'`test_data.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_data.Tpo $(DEPDIR)/libcw_tests-test_data.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_data.c' object='libcw_tests-test_data.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_data.o `test -f 'test_data.c' || echo '$(srcdir)/'`test_data.c libcw_tests-test_data.obj: test_data.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_data.obj -MD -MP -MF $(DEPDIR)/libcw_tests-test_data.Tpo -c -o libcw_tests-test_data.obj `if test -f 'test_data.c'; then $(CYGPATH_W) 'test_data.c'; else $(CYGPATH_W) '$(srcdir)/test_data.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_data.Tpo $(DEPDIR)/libcw_tests-test_data.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_data.c' object='libcw_tests-test_data.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_data.obj `if test -f 'test_data.c'; then $(CYGPATH_W) 'test_data.c'; else $(CYGPATH_W) '$(srcdir)/test_data.c'; fi` libcw_tests-test_sets.o: test_sets.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_sets.o -MD -MP -MF $(DEPDIR)/libcw_tests-test_sets.Tpo -c -o libcw_tests-test_sets.o `test -f 'test_sets.c' || echo '$(srcdir)/'`test_sets.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_sets.Tpo $(DEPDIR)/libcw_tests-test_sets.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_sets.c' object='libcw_tests-test_sets.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_sets.o `test -f 'test_sets.c' || echo '$(srcdir)/'`test_sets.c libcw_tests-test_sets.obj: test_sets.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_sets.obj -MD -MP -MF $(DEPDIR)/libcw_tests-test_sets.Tpo -c -o libcw_tests-test_sets.obj `if test -f 'test_sets.c'; then $(CYGPATH_W) 'test_sets.c'; else $(CYGPATH_W) '$(srcdir)/test_sets.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_sets.Tpo $(DEPDIR)/libcw_tests-test_sets.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_sets.c' object='libcw_tests-test_sets.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_sets.obj `if test -f 'test_sets.c'; then $(CYGPATH_W) 'test_sets.c'; else $(CYGPATH_W) '$(srcdir)/test_sets.c'; fi` libcw_tests-test_main.o: test_main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_main.o -MD -MP -MF $(DEPDIR)/libcw_tests-test_main.Tpo -c -o libcw_tests-test_main.o `test -f 'test_main.c' || echo '$(srcdir)/'`test_main.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_main.Tpo $(DEPDIR)/libcw_tests-test_main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_main.c' object='libcw_tests-test_main.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_main.o `test -f 'test_main.c' || echo '$(srcdir)/'`test_main.c libcw_tests-test_main.obj: test_main.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcw_tests-test_main.obj -MD -MP -MF $(DEPDIR)/libcw_tests-test_main.Tpo -c -o libcw_tests-test_main.obj `if test -f 'test_main.c'; then $(CYGPATH_W) 'test_main.c'; else $(CYGPATH_W) '$(srcdir)/test_main.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcw_tests-test_main.Tpo $(DEPDIR)/libcw_tests-test_main.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_main.c' object='libcw_tests-test_main.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcw_tests_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcw_tests-test_main.obj `if test -f 'test_main.c'; then $(CYGPATH_W) 'test_main.c'; else $(CYGPATH_W) '$(srcdir)/test_main.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ elif test -n "$$redo_logs"; then \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ if test -n "$(PACKAGE_BUGREPORT)"; then \ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all $(check_PROGRAMS) $(check_SCRIPTS) @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? libcw_test_quick.sh.log: libcw_test_quick.sh @p='libcw_test_quick.sh'; \ b='libcw_test_quick.sh'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(check_SCRIPTS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \ clean-checkPROGRAMS clean-generic clean-libtool cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile -include $(top_builddir)/Makefile.inc libcw_test_quick.sh: # sources, references # # source of snippet related to "check_SCRIPTS" and related sh script: # http://www.freesoftwaremagazine.com/books/agaal/automatically_writing_makefiles_with_autotools # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: unixcw-3.6.0/src/libcw/tests/libcw_rec_tests.c0000644000175000017500000015372014000344554016335 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include "libcw.h" #include "libcw2.h" #include "libcw_debug.h" #include "libcw_key.h" #include "libcw_rec.h" #include "libcw_rec_internal.h" #include "libcw_rec_tests.h" #include "libcw_tq.h" #include "libcw_utils.h" #include "test_framework.h" /* TODO: this should go to some 'utils' header. */ #define cw_min(a, b) ((a) < (b) ? (a) : (b)) #define cw_max(a, b) ((a) > (b) ? (a) : (b)) typedef struct cw_variation_params { /* For functions generating constant send speeds. */ int speed; /* For functions generating varying send speeds. */ int speed_min; int speed_max; /* For... something. */ int fuzz_percent; } cw_variation_params; /* Data type describing sending speeds, at which test characters will be sent to receiver. */ typedef struct cw_send_speeds { float * values; size_t n_speeds; } cw_send_speeds; static cw_send_speeds * cw_send_speeds_new_constant(cw_test_executor_t * cte, size_t n, const cw_variation_params * variation_params); static cw_send_speeds * cw_send_speeds_new_varying_sine(cw_test_executor_t * cte, size_t n, const cw_variation_params * variation_params); static void cw_send_speeds_delete(cw_send_speeds ** speeds); typedef cw_send_speeds * (* send_speeds_maker_t)(cw_test_executor_t *, size_t n, const cw_variation_params * variation_params); /* Set of characters that will be sent to receiver. */ typedef struct cw_characters_list { char * values; size_t n_characters; /* Does not include terminating NUL. */ } cw_characters_list; static cw_characters_list * cw_characters_list_new_basic(cw_test_executor_t * cte); static cw_characters_list * cw_characters_list_new_random(cw_test_executor_t * cte); static void cw_characters_list_delete(cw_characters_list ** characters_list); typedef cw_characters_list * (* characters_list_maker_t)(cw_test_executor_t *); #define TEST_CW_REC_DATA_LEN_MAX 30 /* There is no character that would have that many time points corresponding to a representation. */ typedef struct cw_rec_test_point { char character; /* Character that is being sent to receiver. */ char * representation; /* Character's representation (dots and dashes). */ int tone_durations[TEST_CW_REC_DATA_LEN_MAX]; /* Character's representation's times - time information for marks and spaces. */ size_t n_tone_durations; /* Number of duration values encoding given representation of given character. */ float send_speed; /* Send speed (speed at which the character is incoming). */ bool is_last_in_word; /* Is this character a last character in a word? (is it followed by inter-word-space?) */ } cw_rec_test_point; static cw_rec_test_point * cw_rec_test_point_new(cw_test_executor_t * cte); static void cw_rec_test_point_delete(cw_rec_test_point ** point); typedef struct cw_rec_test_vector { cw_rec_test_point ** points; /* Because of how we treat space characters from list of test characters (we don't put them in cw_rec_test_vector vector), not all points allocated in this object will be valid (will represent valid characters). In order to be able to deallocate both valid and invalid points, we have to have two separate variables: one for total count of allocated points, and the other for count of valid points. */ size_t n_points_allocated; /* This is how many point objects were allocated and are in cw_rec_test_vector::points. */ size_t n_points_valid; /* This is how many valid points (with valid character and durations) are in cw_rec_test_vector::points. */ } cw_rec_test_vector; static cw_rec_test_vector * cw_rec_test_vector_new(cw_test_executor_t * cte, size_t n); static void cw_rec_test_vector_delete(cw_rec_test_vector ** vec); static cw_rec_test_vector * cw_rec_test_vector_factory(cw_test_executor_t * cte, characters_list_maker_t characters_list_maker, send_speeds_maker_t send_speeds_maker, const cw_variation_params * variation_params); __attribute__((unused)) static void cw_rec_test_vector_print(cw_test_executor_t * cte, cw_rec_test_vector * vec); static bool test_cw_rec_test_begin_end(cw_test_executor_t * cte, cw_rec_t * rec, cw_rec_test_vector * vec); /** Test if function correctly recognizes dots and dashes for a range of receive speeds. This test function also checks if marks of durations longer or shorter than certain limits (dictated by receiver) are handled properly (i.e. if they are recognized as invalid marks). Currently the function only works for non-adaptive receiving. @reviewed on 2019-10-26 */ int test_cw_rec_identify_mark_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); cw_rec_t * rec = cw_rec_new(); cte->assert2(cte, rec, "identify mark: failed to create new receiver\n"); cw_rec_disable_adaptive_mode(rec); for (int speed = CW_SPEED_MIN; speed <= CW_SPEED_MAX; speed++) { cw_ret_t cwret = cw_rec_set_speed(rec, speed); cte->assert2(cte, CW_SUCCESS == cwret, "identify mark @ %02d [wpm]: failed to set receive speed\n", speed); bool failure = false; char representation; /* Test marks that have duration appropriate for a dot. */ int duration_step = (rec->dot_duration_max - rec->dot_duration_min) / 10; for (int duration = rec->dot_duration_min; duration <= rec->dot_duration_max; duration += duration_step) { cwret = LIBCW_TEST_FUT(cw_rec_identify_mark_internal)(rec, duration, &representation); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "identify valid dot @ %02d [wpm], duration %d [us]", speed, duration)) { failure = true; break; } if (!cte->expect_op_int_errors_only(cte, CW_DOT_REPRESENTATION, "==", representation, "identify valid dot @ %02d [wpm]: getting dot representation for duration %d [us]", speed, duration)) { failure = true; break; } } cte->expect_op_int(cte, false, "==", failure, "identify dot @ %02d [wpm]: mark valid", speed); /* Test mark shorter than minimal duration of dot. */ cwret = LIBCW_TEST_FUT(cw_rec_identify_mark_internal)(rec, rec->dot_duration_min - 1, &representation); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "identify dot @ %02d [wpm]: mark shorter than min dot", speed); /* Test mark longer than maximal duration of dot (but shorter than minimal duration of dash). */ cwret = LIBCW_TEST_FUT(cw_rec_identify_mark_internal)(rec, rec->dot_duration_max + 1, &representation); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "identify dot @ %02d [wpm]: mark longer than max dot", speed); /* Test marks that have duration appropriate for a dash. */ duration_step = (rec->dash_duration_max - rec->dash_duration_min) / 10; for (int duration = rec->dash_duration_min; duration <= rec->dash_duration_max; duration += duration_step) { cwret = LIBCW_TEST_FUT(cw_rec_identify_mark_internal)(rec, duration, &representation); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "identify valid dash @ %02d [wpm], duration %d [us]", speed, duration)) { failure = true; break; } if (!cte->expect_op_int_errors_only(cte, CW_DASH_REPRESENTATION, "==", representation, "identify valid dash @ %02d [wpm]: getting dash representation for duration = %d [us]", speed, duration)) { failure = true; break; } } cte->expect_op_int(cte, false, "==", failure, "identify dash @ %02d [wpm]: mark valid", speed); /* Test mark shorter than minimal duration of dash (but longer than maximal duration of dot). */ cwret = LIBCW_TEST_FUT(cw_rec_identify_mark_internal)(rec, rec->dash_duration_min - 1, &representation); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "identify dash @ %02d [wpm]: mark shorter than min dash", speed); /* Test mark longer than maximal duration of dash. */ cwret = LIBCW_TEST_FUT(cw_rec_identify_mark_internal)(rec, rec->dash_duration_max + 1, &representation); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "identify dash @ %02d [wpm]: mark longer than max dash", speed); } cw_rec_delete(&rec); cte->print_test_footer(cte, __func__); return 0; } /** @brief Test a receiver with characters sent at constant speed @reviewed on 2019-10-23 */ int test_cw_rec_test_with_constant_speeds(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); const char * this_test_name = "constant speeds"; struct { const char * name; characters_list_maker_t char_list_maker; send_speeds_maker_t send_speeds_maker; } test_data[] = { { /* All characters supported by libcw. Don't use get_characters_random(): for this test get a small table of all characters supported by libcw. This should be a quick test, and it should give 100% guarantee that it covers all characters. Fixed speed receive mode: speed is constant for all characters. */ "basic chars/const speed", cw_characters_list_new_basic, cw_send_speeds_new_constant }, { /* Fixed speed receive mode: speed is constant for all characters. */ "random chars/const speed", cw_characters_list_new_random, cw_send_speeds_new_constant }, { NULL, NULL, NULL } }; cw_rec_t * rec = cw_rec_new(); cte->assert2(cte, rec, "%s: failed to create new receiver\n", this_test_name); int i = 0; while (test_data[i].name) { for (int speed = CW_SPEED_MIN; speed <= CW_SPEED_MAX; speed++) { const cw_variation_params variation_params = { .speed = speed, .speed_min = 0, .speed_max = 0, .fuzz_percent = 0 }; /* Generate duration data for given list of characters, each character is sent with speed calculated by "speeds maker". */ cw_rec_test_vector * vec = cw_rec_test_vector_factory(cte, test_data[i].char_list_maker, test_data[i].send_speeds_maker, &variation_params); cte->assert2(cte, vec, "%s: failed to generate test vector for test %s\n", this_test_name, test_data[i].name); // cw_rec_test_vector_print(cte, vec); /* Prepare receiver (by resetting it to fresh state). */ cw_rec_reset_statistics(rec); cw_rec_reset_state(rec); cw_rec_set_speed(rec, speed); cw_rec_disable_adaptive_mode(rec); /* Verify that the test speed has been set correctly. */ const float rec_speed = cw_rec_get_speed(rec); const float diff = rec_speed - speed; cte->assert2(cte, diff < 0.1f, "%s: setting speed for test %s failed: %.3f != %.3f\n", this_test_name, test_data[i].name, /* Casting to double to avoid compiler warning about implicit conversion from float to double. */ (double) rec_speed, (double) speed); /* Actual tests of receiver functions are here. */ bool failure = test_cw_rec_test_begin_end(cte, rec, vec); cte->expect_op_int(cte, false, "==", failure, "%s: %s @ %02d wpm", this_test_name, test_data[i].name, speed); cw_rec_test_vector_delete(&vec); } i++; } cw_rec_delete(&rec); cte->print_test_footer(cte, __func__); return 0; } /** @brief Test a receiver with characters sent at varying speeds @reviewed on 2019-10-24 */ int test_cw_rec_test_with_varying_speeds(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); const char * this_test_name = "varying speeds"; struct { const char * name; characters_list_maker_t char_list_maker; send_speeds_maker_t send_speeds_maker; } test_data[] = { { /* All characters supported by libcw. Don't use get_characters_random(): for this test get a small table of all characters supported by libcw. This should be a quick test, and it should give 100% guarantee that it covers all characters. */ "basic chars/var speed", cw_characters_list_new_basic, cw_send_speeds_new_varying_sine }, { "random chars/var speed", cw_characters_list_new_random, cw_send_speeds_new_varying_sine }, { NULL, NULL, NULL } }; cw_rec_t * rec = cw_rec_new(); cte->assert2(cte, rec, "%s: failed to create new receiver\n", this_test_name); int i = 0; while (test_data[i].name) { cw_variation_params variation_params = { .speed = 0, .speed_min = CW_SPEED_MIN, .speed_max = CW_SPEED_MAX, .fuzz_percent = 0 }; /* Generate duration data for given set of characters, each character is sent with varying speed from range speed_min-speed_max. */ cw_rec_test_vector * vec = cw_rec_test_vector_factory(cte, test_data[i].char_list_maker, test_data[i].send_speeds_maker, &variation_params); cte->assert2(cte, vec, "%s: failed to generate random/varying test data\n", this_test_name); // cw_rec_test_vector_print(cte, vec); /* Prepare receiver (by resetting it to fresh state). */ cw_rec_reset_statistics(rec); cw_rec_reset_state(rec); cw_rec_set_speed(rec, CW_SPEED_MAX); cw_rec_enable_adaptive_mode(rec); /* Verify that initial test speed has been set correctly. */ const float rec_speed = cw_rec_get_speed(rec); const float diff = rec_speed - CW_SPEED_MAX; cte->assert2(cte, diff < 0.1f, "%s: incorrect receive speed: %.3f != %.3f\n", this_test_name, /* Casting to double to avoid compiler warning about implicit conversion from float to double. */ (double) rec_speed, (double) CW_SPEED_MAX); /* Actual tests of receiver functions are here. */ const bool failure = test_cw_rec_test_begin_end(cte, rec, vec); cte->expect_op_int(cte, false, "==", failure, "%s", this_test_name); cw_rec_test_vector_delete(&vec); i++; } cw_rec_delete(&rec); cte->print_test_footer(cte, __func__); return 0; } /** \brief The core test function, testing receiver's "begin" and "end" functions As mentioned in file's top-level comment, there are two main methods to add data to receiver. This function tests first method: using cw_rec_mark_begin() and cw_rec_mark_end(). Other helper functions are used/tested here as well, because adding marks and spaces to receiver is just half of the job necessary to receive Morse code. You have to interpret the marks and spaces, too. @reviewed on 2019-10-26 \param rec - receiver variable used during tests \param data - table with tone_durations, used to test the receiver */ bool test_cw_rec_test_begin_end(cw_test_executor_t * cte, cw_rec_t * rec, cw_rec_test_vector * vec) { const char * this_test_name = "begin/end"; struct timeval tv = { 0, 0 }; bool begin_end_failure = false; bool buffer_length_failure = false; bool poll_representation_failure = false; bool match_representation_failure = false; bool error_representation_failure = false; bool word_representation_failure = false; bool poll_character_failure = false; bool match_character_failure = false; bool empty_failure = false; for (size_t i = 0; i < vec->n_points_valid; i++) { cw_rec_test_point * point = vec->points[i]; #ifdef LIBCW_UNIT_TESTS_VERBOSE cte->log_info(cte, "%s: input test data #%zd%zd: '%c' / '%s' @ %.2f [wpm]\n", this_test_name, i, point->n_tone_durations, point->character, point->representation, point->send_speed); #endif /* This loop simulates "key down" and "key up" events in specific moments, in specific time intervals, and for specific durations. key down -> call to cw_rec_mark_begin() key up -> call to cw_rec_mark_end(). First "key down" event is at X seconds Y microseconds (see initialization of 'tv'). Time of every following event is calculated by iterating over tone durations specified in data table. */ int tone; for (tone = 0; point->tone_durations[tone] > 0; tone++) { begin_end_failure = false; if (tone % 2) { const cw_ret_t cwret = LIBCW_TEST_FUT(cw_rec_mark_end)(rec, &tv); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "%s: cw_rec_mark_end(): tone = %d, time = %d.%d", this_test_name, tone, (int) tv.tv_sec, (int) tv.tv_usec)) { begin_end_failure = true; break; } } else { const cw_ret_t cwret = LIBCW_TEST_FUT(cw_rec_mark_begin)(rec, &tv); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "%s: cw_rec_mark_begin(): tone = %d, time = %d.%d", this_test_name, tone, (int) tv.tv_sec, (int) tv.tv_usec)) { begin_end_failure = true; break; } } /* TODO: add wrapper for adding milliseconds to timeval. */ tv.tv_usec += point->tone_durations[tone]; if (tv.tv_usec >= CW_USECS_PER_SEC) { /* Moving event to next second. */ tv.tv_sec += tv.tv_usec / CW_USECS_PER_SEC; tv.tv_usec %= CW_USECS_PER_SEC; } /* If we exit the loop at this point, the last 'tv' with duration of inter-character-space will be used below in cw_rec_poll_representation(). */ } if (begin_end_failure) { break; } cte->assert2(cte, tone, "%s: test executed zero times\n", this_test_name); /* Test: length of receiver's buffer (only marks!) after adding a representation of a single character to receiver's buffer. */ { const int readback_len = LIBCW_TEST_FUT(cw_rec_get_buffer_length_internal)(rec); const int expected_len = (int) strlen(point->representation); if (!cte->expect_op_int_errors_only(cte, expected_len, "==", readback_len, "%s: cw_rec_get_buffer_length_internal()", this_test_name)) { buffer_length_failure = true; break; } } /* Test: getting representation from receiver's buffer. */ { /* Get representation (dots and dashes) accumulated by receiver. Check for errors. */ char polled_representation[CW_REC_REPRESENTATION_CAPACITY + 1] = { 0 }; bool is_word = false; bool is_error = false; /* Notice that we call the function with last timestamp (tv) from input data. The last timestamp in the input data represents end of final inter-character-space. With this final passing of "end of space" timestamp to libcw the test code informs receiver that inter-character-space has occurred, i.e. a full character has been passed to receiver. The space duration in input data is (3 x dot + jitter). In libcw maximum recognizable duration of "end of character" space is 5 x dot. */ cw_ret_t cwret = LIBCW_TEST_FUT(cw_rec_poll_representation)(rec, &tv, polled_representation, &is_word, &is_error); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "%s: poll representation (cwret)", this_test_name)) { poll_representation_failure = true; break; } const int strcmp_result = strcmp(polled_representation, point->representation); if (!cte->expect_op_int_errors_only(cte, 0, "==", strcmp_result, "%s: polled representation vs. test representation: \"%s\" vs. \"%s\"", this_test_name, polled_representation, point->representation)) { match_representation_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, false, "==", is_error, "%s: poll representation is_error flag", this_test_name)) { error_representation_failure = true; break; } /* If the last space in character's data is inter-word-space (which is indicated by is_last_in_word flag in test point), then is_word should be set by poll() to true. Otherwise both values should be false. */ if (!cte->expect_op_int_errors_only(cte, point->is_last_in_word, "==", is_word, "%s: poll representation: is word", this_test_name)) { word_representation_failure = true; cte->log_info(cte, "%s: poll representation: 'is_word' flag error: function returns '%d', data is tagged with '%d'\n", this_test_name, is_word, point->is_last_in_word); for (size_t p = 0; p < vec->n_points_valid; p++) { if (cw_max(p, i) - cw_min(p, i) < 4) { cte->log_info_cont(cte, "character #%zd = '%c'\n", p, vec->points[p]->character); } } break; } #if 0 /* Debug code. Print times of character with inter-word-space to verify duration of the space. */ if (point->is_last_in_word) { cte->log_info(cte, "Character '%c' is last in word:\n", point->character); for (size_t m = 0; m < point->n_tone_durations; m++) { cte->log_info(cte, " tone #%zd: duration = %d [us]\n", m, point->tone_durations[m]); } } #endif } char polled_character = '@'; /* Test: getting character from receiver's buffer. */ { bool is_word = false; bool is_error = false; /* The representation is still held in receiver. Ask receiver for converting the representation to character. */ const cw_ret_t cwret = LIBCW_TEST_FUT(cw_rec_poll_character)(rec, &tv, &polled_character, &is_word, &is_error); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "%s: poll character (cwret)", this_test_name)) { poll_character_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, point->character, "==", polled_character, "%s: polled character vs. test character: '%c' vs. '%c'", this_test_name, polled_character, point->character)) { match_character_failure = true; break; } } /* Test: getting length of receiver's representation buffer after cleaning the buffer. */ { /* We have a copy of received representation, we have a copy of character. The receiver no longer needs to store the representation. If I understand this correctly, the call to reset_state() is necessary to prepare the receiver for receiving next character. */ cw_rec_reset_state(rec); const int length = LIBCW_TEST_FUT(cw_rec_get_buffer_length_internal)(rec); if (!cte->expect_op_int_errors_only(cte, 0, "==", length, "%s: get buffer length: length of cleared buffer", this_test_name)) { empty_failure = true; break; } } #ifdef LIBCW_UNIT_TESTS_VERBOSE const float rec_speed = cw_rec_get_speed(rec); cte->log_info(cte, "%s: received data #%d: <%c> / <%s> @ %.2f [wpm]\n", this_test_name, i, polled_character, polled_representation, rec_speed); #endif } cte->expect_op_int_errors_only(cte, false, "==", begin_end_failure, "Signalling begin and end of mark"); cte->expect_op_int_errors_only(cte, false, "==", buffer_length_failure, "Getting length of representation buffer"); cte->expect_op_int_errors_only(cte, false, "==", poll_representation_failure, "Polling representation"); cte->expect_op_int_errors_only(cte, false, "==", match_representation_failure, "Representation match"); cte->expect_op_int_errors_only(cte, false, "==", error_representation_failure, "Representation 'is error'"); cte->expect_op_int_errors_only(cte, false, "==", word_representation_failure, "Representation 'is word'"); cte->expect_op_int_errors_only(cte, false, "==", poll_character_failure, "Polling character"); cte->expect_op_int_errors_only(cte, false, "==", match_character_failure, "Character match"); cte->expect_op_int_errors_only(cte, false, "==", empty_failure, "Empty representation buffer"); return begin_end_failure || buffer_length_failure || poll_representation_failure || match_representation_failure || error_representation_failure || word_representation_failure || poll_character_failure || match_character_failure || empty_failure; } /** \brief Get a string with all characters supported by libcw Function allocates and returns a string with all characters that are supported/recognized by libcw. @reviewed on 2019-10-24 \return wrapper object for allocated string */ cw_characters_list * cw_characters_list_new_basic(cw_test_executor_t * cte) { const int n = cw_get_character_count(); cw_characters_list * characters_list = (cw_characters_list *) calloc(sizeof (cw_characters_list), 1); cte->assert2(cte, characters_list, "%s: first calloc() failed\n", __func__); characters_list->values = (char *) calloc(sizeof (char), n + 1); /* This will be a C string, so +1 for terminating NUL. */ cte->assert2(cte, characters_list, "%s: second calloc() failed\n", __func__); characters_list->n_characters = n; cw_list_characters(characters_list->values); return characters_list; } /** \brief Generate a set of characters of size \p n. Function allocates and returns a string of \p n characters. The characters are randomly drawn from set of all characters supported by libcw. Spaces are added to the string in random places to mimic a regular text. Function makes sure that there are no consecutive spaces (two or more) in the string. @reviewed on 2019-10-24 \return wrapper object for string of random characters (including spaces) */ cw_characters_list * cw_characters_list_new_random(cw_test_executor_t * cte) { const size_t n_random_characters = cw_get_character_count() * ((lrand48() % 50) + 30); /* We will use basic characters list (all characters supported by libcw) as an input for generating random characters list. */ cw_characters_list * basic_characters_list = cw_characters_list_new_basic(cte); const size_t n_basic_characters = basic_characters_list->n_characters; cw_characters_list * random_characters_list = (cw_characters_list *) calloc(sizeof (cw_characters_list), 1); cte->assert2(cte, random_characters_list, "first calloc() failed\n"); random_characters_list->values = (char *) calloc(sizeof (char), n_random_characters + 1); /* This will be a C string, so +1 for terminating NUL. */ cte->assert2(cte, random_characters_list->values, "second calloc() failed\n"); size_t space_randomizer = 3; for (size_t i = 0; i < n_random_characters; i++) { int basic_idx = lrand48() % n_basic_characters; if (0 == (basic_idx % space_randomizer)) { /* Insert space at random places. */ space_randomizer = (lrand48() % (n_basic_characters / 2)) + 3; /* Pick new value for next round. */ random_characters_list->values[i] = ' '; /* Also fill next cell, but with non-space char, to prevent two consecutive spaces in result string. TODO: why we want to avoid two consecutive spaces? Does it break test algorithm? */ if ((i + 1) < n_random_characters) { i++; random_characters_list->values[i] = basic_characters_list->values[basic_idx]; } } else { random_characters_list->values[i] = basic_characters_list->values[basic_idx]; } } /* First character in input data can't be a space. Two reasons: 1. we can't start a receiver's state machine with space. 2. when a inter-word-space appears in test string, it is added as last duration value at the end of duration values table for "previous char". We couldn't do this (i.e. modify table of duration of "previous char") for 1st char in test string. */ random_characters_list->values[0] = 'K'; /* Use capital letter. libcw uses capital letters internally. */ random_characters_list->values[n_random_characters] = '\0'; random_characters_list->n_characters = n_random_characters; // fprintf(stderr, "\n%s\n\n", random_characters_list->values); cw_characters_list_delete(&basic_characters_list); return random_characters_list; } /** @reviewed on 2019-10-25 */ void cw_characters_list_delete(cw_characters_list ** characters_list) { if (NULL == characters_list) { return; } if (NULL == *characters_list) { return; } if (NULL != (*characters_list)->values) { free((*characters_list)->values); (*characters_list)->values = NULL; } free(*characters_list); *characters_list = NULL; } /** \brief Generate a table of constant speeds Function allocates and returns a table of speeds of constant value specified by cw_variation_params::speed. There will be \p n valid (non-negative and within valid range) values in the table. @reviewed on 2019-10-25 \param n - size of speeds table \param cw_variation_params::speed - a constant value to be used as initializer of the table \return wrapper object for table of speeds of constant value */ cw_send_speeds * cw_send_speeds_new_constant(cw_test_executor_t * cte, size_t n, const cw_variation_params * variation_params) { cte->assert2(cte, variation_params->speed >= CW_SPEED_MIN, "%s: speed must be at least %d\n", __func__, CW_SPEED_MIN); cte->assert2(cte, variation_params->speed <= CW_SPEED_MAX, "%s: speed must be no larger than %d\n", __func__, CW_SPEED_MAX); cw_send_speeds * speeds = (cw_send_speeds *) calloc(sizeof (cw_send_speeds), 1); cte->assert2(cte, speeds, "%s: first calloc() failed\n", __func__); speeds->values = (float *) calloc(sizeof (float), n); cte->assert2(cte, speeds, "%s: second calloc() failed\n", __func__); speeds->n_speeds = n; for (size_t i = 0; i < speeds->n_speeds; i++) { /* Constant speeds. */ speeds->values[i] = (float) variation_params->speed; } return speeds; } /** \brief Generate a table of varying speeds Function allocates and returns a table of speeds of varying values, changing between \p variation_params::speed_min and \p variation_params::speed_max. There will be \p n valid (non-negative and within the specified range) values in the table. @reviewed on 2019-10-25 \param n - size of speeds table \param variation_params::speed_min - minimal speed \param variation_params::speed_max - maximal speed \return wrapper object for table of speeds */ cw_send_speeds * cw_send_speeds_new_varying_sine(cw_test_executor_t * cte, size_t n, const cw_variation_params * variation_params) { cte->assert2(cte, variation_params->speed_min >= CW_SPEED_MIN, "%s: speed_min must be at least %d\n", __func__, CW_SPEED_MIN); cte->assert2(cte, variation_params->speed_max >= CW_SPEED_MIN, "%s: speed_max must be at least %d\n", __func__, CW_SPEED_MIN); cte->assert2(cte, variation_params->speed_min <= CW_SPEED_MAX, "%s: speed_min must be no larger than %d\n", __func__, CW_SPEED_MAX); cte->assert2(cte, variation_params->speed_max <= CW_SPEED_MAX, "%s: speed_max must be no larger than %d\n", __func__, CW_SPEED_MAX); cte->assert2(cte, variation_params->speed_min <= variation_params->speed_max, "%s: speed_min can't be larger than speed_max\n", __func__); cw_send_speeds * speeds = (cw_send_speeds *) calloc(sizeof (cw_send_speeds), 1); cte->assert2(cte, speeds, "%s: first calloc() failed\n", __func__); speeds->values = (float *) calloc(sizeof (float), n); cte->assert2(cte, speeds, "%s: second calloc() failed\n", __func__); speeds->n_speeds = n; for (size_t i = 0; i < n; i++) { /* Varying speeds. */ const float t = (1.0 * i) / n; speeds->values[i] = (1 + cosf(2 * 3.1415f * t)) / 2.0f; /* 0.0 - 1.0 */ speeds->values[i] *= (variation_params->speed_max - variation_params->speed_min); /* 0.0 - 56.0 */ speeds->values[i] += variation_params->speed_min; /* 4.0 - 60.0 */ // fprintf(stderr, "%f\n", speeds->values[i]); } return speeds; } /** @reviewed on 2019-10-25 */ void cw_send_speeds_delete(cw_send_speeds ** speeds) { if (NULL == speeds) { return; } if (NULL == *speeds) { return; } if (NULL != (*speeds)->values) { free((*speeds)->values); (*speeds)->values = NULL; } free(*speeds); *speeds = NULL; } /** \brief Create durations data used for testing a receiver This is a generic function that can generate different sets of data depending on input parameters. It is to be used by wrapper functions that first specify parameters of test data, and then pass the parameters to this function. The function allocates a table with durations data (and some other data as well) that can be used to test receiver's functions that accept timestamp argument. @param character_list_maker generates list of (valid) characters that will be represented by durations. @param send_speeds_maker generates list of speeds (wpm) at which the characters will be sent to receiver. The data returned by the function is valid and represents valid Morse representations (durations describe a series of dots and dashes that in turn correspond to list of characters). If you want to generate invalid data or to generate data based on invalid representations, you have to use some other function. For each character the last duration parameter represents inter-character-space or inter-word-space. The next duration parameter after that space is zero. For character 'A' that would look like this: .- == 40000 (dot mark); 40000 (inter-mark-space); 120000 (dash mark); 240000 (inter-word-space); 0 (guard, zero duration) Last element in the created table (a guard "pseudo-character") has 'representation' field set to NULL. Use cw_rec_test_vector_delete() to deallocate the duration data table. @reviewed on 2019-10-25 \return wrapper object around table of duration data sets */ cw_rec_test_vector * cw_rec_test_vector_factory(cw_test_executor_t * cte, characters_list_maker_t characters_list_maker, send_speeds_maker_t send_speeds_maker, const cw_variation_params * variation_params) { cw_characters_list * characters_list = characters_list_maker(cte); cw_send_speeds * send_speeds = send_speeds_maker(cte, characters_list->n_characters, variation_params); const size_t n_characters = characters_list->n_characters; cw_rec_test_vector * vec = cw_rec_test_vector_new(cte, n_characters); size_t out_idx = 0; for (size_t in_idx = 0; in_idx < n_characters; in_idx++) { /* TODO: in this loop we are using some formulas from somewhere to calculate durations. Where do these formulas come from? How do we know that they are valid? */ const int dot_duration = CW_DOT_CALIBRATION / send_speeds->values[in_idx]; /* [us]; used as basis for other elements. */ //fprintf(stderr, "dot_duration = %d [us] for speed = %05.2f [wpm]\n", dot_duration, send_speeds->values[in_idx]); /* First handle a special case: inter-word-space. This long space will be put at the end of table of time values for previous representation. The space in character list is never transformed into separate point in vector. When generating list of characters, we make sure to put non-space character at index 0, so when we index points[] with 'out_idx-1' we are safe. */ if (characters_list->values[in_idx] == ' ') { /* We don't want to affect *current* output point (we don't create a vector point for space). We want to turn inter-character-space of previous point into inter-word-space, hence 'out_idx - 1'. */ cw_rec_test_point * prev_point = vec->points[out_idx - 1]; const int space_idx = prev_point->n_tone_durations - 1; /* Index of last space (inter-character-space, to become inter-word-space). */ prev_point->tone_durations[space_idx] = dot_duration * 6; /* dot_duration * 5 is the minimal inter-word-space. */ prev_point->is_last_in_word = true; continue; } else { /* A regular character, handled below. */ } cw_rec_test_point * point = vec->points[out_idx]; point->character = characters_list->values[in_idx]; point->representation = cw_character_to_representation(point->character); cte->assert2(cte, point->representation, "%s: cw_character_to_representation() failed for input char #%zu: '%c'\n", __func__, in_idx, characters_list->values[in_idx]); point->send_speed = send_speeds->values[in_idx]; /* Build table of durations 'tone_durations[]' for given representation 'point->representation'. */ size_t n_tone_durations = 0; /* Number of durations in durations table. */ size_t rep_length = strlen(point->representation); for (size_t k = 0; k < rep_length; k++) { /* Duration of mark. */ if (point->representation[k] == CW_DOT_REPRESENTATION) { point->tone_durations[n_tone_durations] = dot_duration; } else if (point->representation[k] == CW_DASH_REPRESENTATION) { point->tone_durations[n_tone_durations] = dot_duration * 3; } else { cte->assert2(cte, 0, "%s: unknown char in representation: '%c'\n", __func__, point->representation[k]); } n_tone_durations++; /* Duration of space (inter-mark space). Mark and space always go in pair. */ point->tone_durations[n_tone_durations] = dot_duration; n_tone_durations++; } /* Every character has non-zero marks and spaces. */ cte->assert2(cte, n_tone_durations > 0, "%s: number of data points is %zu for representation '%s'\n", __func__, n_tone_durations, point->representation); /* Mark and space always go in pair, so nd should be even. */ cte->assert2(cte, ! (n_tone_durations % 2), "%s: number of times is not even\n", __func__); /* Mark/space pair per each dot or dash. */ cte->assert2(cte, n_tone_durations == 2 * rep_length, "%s: number of times incorrect: %zu != 2 * %zu\n", __func__, n_tone_durations, rep_length); /* Graduate that last space (inter-mark space) into inter-character-space. */ point->tone_durations[n_tone_durations - 1] = (dot_duration * 3) + (dot_duration / 2); /* Guard. */ point->tone_durations[n_tone_durations] = 0; point->n_tone_durations = n_tone_durations; /* This may be overwritten by this function when a space character (' ') is encountered in next cell of input string. */ point->is_last_in_word = false; out_idx++; } /* The count of valid points in vector (smaller than n_characters because we have skipped all space (' ') characters). */ vec->n_points_valid = out_idx; cw_characters_list_delete(&characters_list); cw_send_speeds_delete(&send_speeds); return vec; } /** @reviewed on 2019-10-26 */ cw_rec_test_point * cw_rec_test_point_new(__attribute__((unused)) cw_test_executor_t * cte) { return (cw_rec_test_point *) calloc(sizeof (cw_rec_test_point), 1); } /** @reviewed on 2019-10-26 */ void cw_rec_test_point_delete(cw_rec_test_point ** point) { if (NULL == point) { return; } if (NULL == *point) { return; } if ((*point)->representation) { free((*point)->representation); (*point)->representation = NULL; } free(*point); *point = NULL; return; } /** @reviewed on 2019-10-26 */ cw_rec_test_vector * cw_rec_test_vector_new(cw_test_executor_t * cte, size_t n) { cw_rec_test_vector * vec = (cw_rec_test_vector *) calloc(sizeof (cw_rec_test_vector), 1); cte->assert2(cte, vec, "%s: first calloc() failed\n", __func__); vec->points = (cw_rec_test_point **) calloc(sizeof (cw_rec_test_point *), n); cte->assert2(cte, vec->points, "%s: second calloc() failed\n", __func__); vec->n_points_allocated = n; /* This will be overwritten later, once we know the real number of valid points generated from non-space characters. */ vec->n_points_valid = n; for (size_t i = 0; i < vec->n_points_allocated; i++) { vec->points[i] = cw_rec_test_point_new(cte); } return vec; } /** \brief Deallocate vector of duration data used for testing a receiver @reviewed on 2019-10-26 \param vec - pointer to vector to be deallocated */ void cw_rec_test_vector_delete(cw_rec_test_vector ** vec) { if (NULL == vec) { return; } if (NULL == *vec) { return; } for (size_t i = 0; i < (*vec)->n_points_allocated; i++) { cw_rec_test_point_delete(&(*vec)->points[i]); } free((*vec)->points); (*vec)->points = NULL; free(*vec); *vec = NULL; return; } /** \brief Pretty-print duration data used for testing a receiver @reviewed on 2019-10-26 \param vec - vector with duration data to be printed */ void cw_rec_test_vector_print(cw_test_executor_t * cte, cw_rec_test_vector * vec) { cte->log_info_cont(cte, "---------------------------------------------------------------------------------------------------------------------------------------------------------\n"); for (size_t i = 0; i < vec->n_points_valid; i++) { cw_rec_test_point * point = vec->points[i]; /* Print header. */ if (!(i % 10)) { cte->log_info_cont(cte, "ch repr [wpm] mark space mark space mark space mark space mark space mark space mark space\n"); } /* Print data. */ cte->log_info_cont(cte, "%c %-7s %6.2f", point->character, point->representation, (double) point->send_speed); /* Casting to double to avoid compiler warning about implicit conversion from float to double. */ for (size_t t = 0; t < point->n_tone_durations; t++) { cte->log_info_cont(cte, "%9d ", point->tone_durations[t]); } cte->log_info_cont(cte, "\n"); } return; } /** Parameter getters are independent of sound system, so they can be tested just with CW_AUDIO_NULL. @reviewed on 2019-10-26 */ int test_cw_rec_get_receive_parameters(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); const char * this_test_name = "get params"; int dot_len_ideal = 0; int dash_len_ideal = 0; int dot_len_min = 0; int dot_len_max = 0; int dash_len_min = 0; int dash_len_max = 0; int ims_len_min = 0; int ims_len_max = 0; int ims_len_ideal = 0; int ics_len_min = 0; int ics_len_max = 0; int ics_len_ideal = 0; int adaptive_speed_threshold = 0; cw_rec_t * rec = cw_rec_new(); cte->assert2(cte, rec, "%s: failed to create new receiver\n", this_test_name); cw_rec_reset_parameters_internal(rec); cw_rec_sync_parameters_internal(rec); LIBCW_TEST_FUT(cw_rec_get_parameters_internal)(rec, &dot_len_ideal, &dash_len_ideal, &dot_len_min, &dot_len_max, &dash_len_min, &dash_len_max, &ims_len_min, &ims_len_max, &ims_len_ideal, &ics_len_min, &ics_len_max, &ics_len_ideal, &adaptive_speed_threshold); cw_rec_delete(&rec); cte->log_info(cte, "%s: dot/dash: %d, %d, %d, %d, %d, %d\n", this_test_name, dot_len_ideal, dash_len_ideal, dot_len_min, dot_len_max, dash_len_min, dash_len_max); cte->log_info(cte, "%s: ims: %d, %d, %d\n", this_test_name, ims_len_min, ims_len_max, ims_len_ideal); cte->log_info(cte, "%s: ics: %d, %d, %d\n", this_test_name, ics_len_min, ics_len_max, ics_len_ideal); cte->log_info(cte, "%s: adaptive threshold: %d\n", this_test_name, adaptive_speed_threshold); bool failure = (dot_len_ideal <= 0 || dash_len_ideal <= 0 || dot_len_min <= 0 || dot_len_max <= 0 || dash_len_min <= 0 || dash_len_max <= 0 || ims_len_min <= 0 || ims_len_max <= 0 || ims_len_ideal <= 0 || ics_len_min <= 0 || ics_len_max <= 0 || ics_len_ideal <= 0 || adaptive_speed_threshold <= 0); cte->expect_op_int(cte, false, "==", failure, "cw_rec_get_parameters_internal()"); cte->expect_op_int(cte, dot_len_max, "<", dash_len_min, "%s: max dot len < min dash len (%d/%d)", this_test_name, dot_len_max, dash_len_min); cte->expect_op_int(cte, dot_len_min, "<", dot_len_max, "%s: dot len consistency A (%d/%d)", this_test_name, dot_len_min, dot_len_max); cte->expect_op_int(cte, dot_len_min, "<", dot_len_ideal, "%s: dot len consistency B (%d/%d/%d)", this_test_name, dot_len_min, dot_len_ideal, dot_len_max); cte->expect_op_int(cte, dot_len_max, ">", dot_len_ideal, "%s: dot len consistency C (%d/%d/%d)", this_test_name, dot_len_min, dot_len_ideal, dot_len_max); cte->expect_op_int(cte, dash_len_min, "<", dash_len_max, "%s: dash len consistency A (%d/%d)", this_test_name, dash_len_min, dash_len_max); cte->expect_op_int(cte, dash_len_min, "<", dash_len_ideal, "%s: dash len consistency B (%d/%d/%d)", this_test_name, dash_len_min, dash_len_ideal, dash_len_max); cte->expect_op_int(cte, dash_len_max, ">", dash_len_ideal, "%s: dash len consistency c (%d/%d/%d)", this_test_name, dash_len_min, dash_len_ideal, dash_len_max); cte->expect_op_int(cte, ims_len_max, "<", ics_len_min, "%s: max ims len < min ics len (%d/%d)", this_test_name, ims_len_max, ics_len_min); cte->expect_op_int(cte, ims_len_min, "<", ims_len_max, "%s: ims len consistency A (%d/%d)", this_test_name, ims_len_min, ims_len_max); cte->expect_op_int(cte, ims_len_min, "<", ims_len_ideal, "%s: ims len consistency B (%d/%d/%d)", this_test_name, ims_len_min, ims_len_ideal, ims_len_max); cte->expect_op_int(cte, ims_len_max, ">", ims_len_ideal, "%s: ims len consistency C (%d/%d/%d)", this_test_name, ims_len_min, ims_len_ideal, ims_len_max); cte->expect_op_int(cte, ics_len_min, "<", ics_len_max, "%s: ics len consistency A (%d/%d)", this_test_name, ics_len_min, ics_len_max); cte->expect_op_int(cte, ics_len_min, "<", ics_len_ideal, "%s: ics len consistency B (%d/%d/%d)", this_test_name, ics_len_min, ics_len_ideal, ics_len_max); cte->expect_op_int(cte, ics_len_max, ">", ics_len_ideal, "%s: ics len consistency C (%d/%d/%d)", this_test_name, ics_len_min, ics_len_ideal, ics_len_max); cte->print_test_footer(cte, __func__); return 0; } /** Parameter getters and setters are independent of sound system, so they can be tested just with CW_AUDIO_NULL. This function tests a single set of functions. This set is "special" because "get_value()" function returns float. Most of other "get_value()" functions in libcw return int. @reviewed on 2019-10-26 */ int test_cw_rec_parameter_getters_setters_1(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); const char * this_test_name = "get/set param 1"; cw_rec_t * rec = cw_rec_new(); cte->assert2(cte, rec, "%s: failed to create new receiver\n", this_test_name); /* Test setting and getting of some basic parameters. */ int off_limits = 10000; struct { /* There are tree functions that take part in the test: first gets range of acceptable values, seconds sets a new value of parameter, and third reads back the value. */ void (* get_limits)(int * min, int * max); cw_ret_t (* set_new_value)(cw_rec_t * rec, int new_value); float (* get_value)(const cw_rec_t * rec); const int expected_min; /* Expected value of minimum. */ const int expected_max; /* Expected value of maximum. */ int readback_min; /* Value returned by 'get_limits()' function. */ int readback_max; /* Value returned by 'get_limits()' function. */ const char *name; } test_data[] = { { LIBCW_TEST_FUT(cw_get_speed_limits), LIBCW_TEST_FUT(cw_rec_set_speed), LIBCW_TEST_FUT(cw_rec_get_speed), CW_SPEED_MIN, CW_SPEED_MAX, off_limits, -off_limits, "rec speed" }, { NULL, NULL, NULL, 0, 0, 0, 0, NULL } }; bool get_failure = false; bool set_min_failure = false; bool set_max_failure = false; bool set_ok_failure = false; for (int i = 0; test_data[i].get_limits; i++) { int cwret = CW_FAILURE; int value = 0; /* Get limits of values to be tested. */ test_data[i].get_limits(&test_data[i].readback_min, &test_data[i].readback_max); if (!cte->expect_op_int_errors_only(cte, test_data[i].readback_min, "==", test_data[i].expected_min, "%s: get min %s", this_test_name, test_data[i].name)) { get_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, test_data[i].readback_max, "==", test_data[i].expected_max, "%s: get max %s", this_test_name, test_data[i].name)) { get_failure = true; break; } /* Test out-of-range value lower than minimum. */ errno = 0; value = test_data[i].readback_min - 1; cwret = test_data[i].set_new_value(rec, value); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "%s: setting %s value below minimum (cwret)", this_test_name, test_data[i].name)) { set_min_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, EINVAL, "==", errno, "%s: setting %s value below minimum (errno)", this_test_name, test_data[i].name)) { set_min_failure = true; break; } /* Test out-of-range value higher than maximum. */ errno = 0; value = test_data[i].readback_max + 1; cwret = test_data[i].set_new_value(rec, value); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "%s: setting %s value above maximum (cwret)", this_test_name, test_data[i].name)) { set_max_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, EINVAL, "==", errno, "%s: setting %s value above maximum (errno)", this_test_name, test_data[i].name)) { set_max_failure = true; break; } /* Test in-range values. Set with setter and then read back with getter. */ errno = 0; for (int new_val = test_data[i].expected_min; new_val <= test_data[i].expected_max; new_val++) { test_data[i].set_new_value(rec, new_val); const float readback_value = test_data[i].get_value(rec); const float diff = readback_value - new_val; if (!cte->expect_op_float_errors_only(cte, 0.01, ">", diff, "%s: setting %s value in-range: %d (val)", this_test_name, test_data[i].name, new_val)) { set_ok_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, 0, "==", errno, "%s: setting %s value in-range: %d (errno)", this_test_name, test_data[i].name, new_val)) { set_ok_failure = true; break; } } if (set_ok_failure) { break; } } cw_rec_delete(&rec); cte->expect_op_int(cte, false, "==", get_failure, "%s: get", this_test_name); cte->expect_op_int(cte, false, "==", set_min_failure, "%s: set value below min", this_test_name); cte->expect_op_int(cte, false, "==", set_max_failure, "%s: set value above max", this_test_name); cte->expect_op_int(cte, false, "==", set_ok_failure, "%s: set value in range", this_test_name); cte->print_test_footer(cte, __func__); return 0; } /** Parameter getters and setters are independent of sound system, so they can be tested just with CW_AUDIO_NULL. This function tests a single set of functions. This set is "special" because "get_value()" function returns float. Most of other "get_value()" functions in libcw return int. @reviewed on 2019-10-26 */ int test_cw_rec_parameter_getters_setters_2(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); const char * this_test_name = "get/set param 2"; cw_rec_t * rec = cw_rec_new(); cte->assert2(cte, rec, "%s: failed to create new receiver\n", this_test_name); /* Test setting and getting of some basic parameters. */ int off_limits = 10000; struct { /* There are tree functions that take part in the test: first gets range of acceptable values, seconds sets a new value of parameter, and third reads back the value. */ void (* get_limits)(int * min, int * max); cw_ret_t (* set_new_value)(cw_rec_t * rec, int new_value); int (* get_value)(const cw_rec_t * rec); const int expected_min; /* Expected value of minimum. */ const int expected_max; /* Expected value of maximum. */ int readback_min; /* Value returned by 'get_limits()' function. */ int readback_max; /* Value returned by 'get_limits()' function. */ const char *name; } test_data[] = { { LIBCW_TEST_FUT(cw_get_tolerance_limits), LIBCW_TEST_FUT(cw_rec_set_tolerance), LIBCW_TEST_FUT(cw_rec_get_tolerance), CW_TOLERANCE_MIN, CW_TOLERANCE_MAX, off_limits, -off_limits, "tolerance" }, { NULL, NULL, NULL, 0, 0, 0, 0, NULL } }; bool get_failure = false; bool set_min_failure = false; bool set_max_failure = false; bool set_ok_failure = false; for (int i = 0; test_data[i].get_limits; i++) { int cwret = CW_FAILURE; int value = 0; /* Get limits of values to be tested. */ test_data[i].get_limits(&test_data[i].readback_min, &test_data[i].readback_max); if (!cte->expect_op_int_errors_only(cte, test_data[i].readback_min, "==", test_data[i].expected_min, "%s: get min %s", this_test_name, test_data[i].name)) { get_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, test_data[i].readback_max, "==", test_data[i].expected_max, "%s: get max %s", this_test_name, test_data[i].name)) { get_failure = true; break; } /* Test out-of-range value lower than minimum. */ errno = 0; value = test_data[i].readback_min - 1; cwret = test_data[i].set_new_value(rec, value); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "%s: setting %s value below minimum (cwret)", this_test_name, test_data[i].name)) { set_min_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, EINVAL, "==", errno, "%s: setting %s value below minimum (errno)", this_test_name, test_data[i].name)) { set_min_failure = true; break; } /* Test out-of-range value higher than maximum. */ errno = 0; value = test_data[i].readback_max + 1; cwret = test_data[i].set_new_value(rec, value); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "%s: setting %s value above maximum (cwret)", this_test_name, test_data[i].name)) { set_max_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, EINVAL, "==", errno, "%s: setting %s value above maximum (errno)", this_test_name, test_data[i].name)) { set_max_failure = true; break; } /* Test in-range values. Set with setter and then read back with getter. */ errno = 0; for (int new_val = test_data[i].readback_min; new_val <= test_data[i].readback_max; new_val++) { test_data[i].set_new_value(rec, new_val); const int readback_value = test_data[i].get_value(rec); const int diff = readback_value - new_val; if (!cte->expect_op_int_errors_only(cte, 1, ">", diff, "%s: setting %s value in-range: %d (val)", this_test_name, test_data[i].name, new_val)) { set_ok_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, 0, "==", errno, "%s: setting %s value in-range: %d (errno)", this_test_name, test_data[i].name, new_val)) { set_ok_failure = true; break; } } if (set_ok_failure) { break; } } cw_rec_delete(&rec); cte->expect_op_int(cte, false, "==", get_failure, "%s: get", this_test_name); cte->expect_op_int(cte, false, "==", set_min_failure, "%s: set value below min", this_test_name); cte->expect_op_int(cte, false, "==", set_max_failure, "%s: set value above max", this_test_name); cte->expect_op_int(cte, false, "==", set_ok_failure, "%s: set value in range", this_test_name); cte->print_test_footer(cte, __func__); return 0; } unixcw-3.6.0/src/libcw/tests/libcw_debug_tests.c0000644000175000017500000000476514000344554016656 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include /* "PRIu32" */ #include "libcw.h" #include "libcw2.h" #include "libcw_debug.h" #include "libcw_debug_tests.h" #include "libcw_key.h" #include "libcw_utils.h" #include "test_framework.h" extern cw_debug_t cw_debug_object; extern cw_debug_t cw_debug_object_ev; extern cw_debug_t cw_debug_object_dev; /** \brief Test getting and setting of debug flags. @reviewed on 2019-10-12 */ int test_cw_debug_flags_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Store current flags for period of tests. */ uint32_t flags_backup = cw_debug_get_flags(&cw_debug_object); bool set_failure = false; bool get_failure = false; cw_debug_set_flags(&cw_debug_object, 0x00); for (uint32_t flags = 1; flags <= CW_DEBUG_MASK; flags++) { /* All combinations of all bits that form libcw debug mask. */ LIBCW_TEST_FUT(cw_debug_set_flags)(&cw_debug_object, flags); if (!cte->expect_op_int_errors_only(cte, flags, "==", cw_debug_object.flags, "set debug flag %"PRIu32"", flags)) { set_failure = true; break; } uint32_t readback_flags = LIBCW_TEST_FUT(cw_debug_get_flags)(&cw_debug_object); if (!cte->expect_op_int_errors_only(cte, flags, "==", readback_flags, "get debug flag %"PRIu32"", flags)) { get_failure = true; break; } } cte->expect_op_int(cte, false, "==", set_failure, "set debug flags"); cte->expect_op_int(cte, false, "==", get_failure, "get debug flags"); /* Restore original flags. */ cw_debug_set_flags(&cw_debug_object, flags_backup); cte->print_test_footer(cte, __func__); return 0; } unixcw-3.6.0/src/libcw/tests/libcw_gen_tests.c0000644000175000017500000012441614000344554016335 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include /* UCHAR_MAX */ #include #include #include "libcw.h" #include "libcw2.h" #include "libcw_gen.h" #include "libcw_gen_tests.h" #include "libcw_debug.h" #include "libcw_utils.h" #include "test_framework.h" extern const char * test_valid_representations[]; extern const char * test_invalid_representations[]; extern const char * test_invalid_strings[]; static cwt_retv gen_setup(cw_test_executor_t * cte, cw_gen_t ** gen); static void gen_destroy(cw_gen_t ** gen); static cwt_retv test_cw_gen_new_start_stop_delete_sub(cw_test_executor_t * cte, const char * function_name, bool do_new, bool do_start, bool do_stop, bool do_delete); static cwt_retv test_cw_gen_forever_sub(cw_test_executor_t * cte, int seconds); /** @brief Prepare new generator, possibly with parameter values passed through command line Test helper function. @reviewed on 2020-05-07 @return cwt_retv_ok on success @return cwt_retv_err on failure */ static cwt_retv gen_setup(cw_test_executor_t * cte, cw_gen_t ** gen) { *gen = cw_gen_new(&cte->current_gen_conf); if (!*gen) { cte->log_error(cte, "Can't create generator, stopping the test\n"); return cwt_retv_err; } cw_gen_reset_parameters_internal(*gen); cw_gen_sync_parameters_internal(*gen); cw_gen_set_speed(*gen, cte->config->send_speed); cw_gen_set_frequency(*gen, cte->config->frequency); return cwt_retv_ok; } /** @brief Delete @param gen, set the pointer to NULL Test helper function. @reviewed on 2020-05-07 */ void gen_destroy(cw_gen_t ** gen) { if (NULL != gen) { if (NULL != *gen) { cw_gen_delete(gen); } } } /** @brief Test creating and deleting a generator (without trying to start or stop it) @reviewed on 2020-05-07 */ cwt_retv test_cw_gen_new_delete(cw_test_executor_t * cte) { return test_cw_gen_new_start_stop_delete_sub(cte, __func__, true, false, false, true); } /** @brief Test creating, starting and deleting a generator (without trying to stop it) @reviewed on 2020-05-07 */ cwt_retv test_cw_gen_new_start_delete(cw_test_executor_t * cte) { return test_cw_gen_new_start_stop_delete_sub(cte, __func__, true, true, false, true); } /** @brief Test creating, stopping and deleting a generator (without trying to start it) @reviewed on 2020-05-07 */ cwt_retv test_cw_gen_new_stop_delete(cw_test_executor_t * cte) { return test_cw_gen_new_start_stop_delete_sub(cte, __func__, true, false, true, true); } /** @brief Test creating, starting, stopping and deleting a generator @reviewed on 2020-05-07 */ cwt_retv test_cw_gen_new_start_stop_delete(cw_test_executor_t * cte) { return test_cw_gen_new_start_stop_delete_sub(cte, __func__, true, true, true, true); } /** @brief Test creating, starting, stopping and deleting a generator - test helper function The operations (creating, starting, stopping and deleting) are executed depending on value of corresponding argument of the function. @param function_name is name of function that called this subroutine @reviewed on 2020-05-07 */ static cwt_retv test_cw_gen_new_start_stop_delete_sub(cw_test_executor_t * cte, const char * function_name, bool do_new, bool do_start, bool do_stop, bool do_delete) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", function_name, loops); bool new_failure = false; bool start_failure = false; bool stop_failure = false; bool delete_failure = false; cw_gen_t * gen = NULL; for (int i = 0; i < loops; i++) { cte->log_info(cte, "%s", ""); if (do_new) { cte->log_info_cont(cte, "new "); gen = LIBCW_TEST_FUT(cw_gen_new)(&cte->current_gen_conf); if (!cte->expect_valid_pointer_errors_only(cte, gen, "new() (loop #%d/%d)", i + 1, loops)) { new_failure = true; break; } /* Try to access some fields in cw_gen_t just to be sure that the gen has been allocated properly. */ if (!cte->expect_op_int_errors_only(cte, 0, "==", gen->buffer_sub_start, "buffer_sub_start in new generator is not at zero")) { new_failure = true; break; } gen->buffer_sub_stop = gen->buffer_sub_start + 10; if (!cte->expect_op_int_errors_only(cte, 10, "==", gen->buffer_sub_stop, "buffer_sub_stop didn't store correct new value")) { new_failure = true; break; } if (!cte->expect_null_pointer_errors_only(cte, gen->library_client.name, "initial value of generator's client name is not NULL")) { new_failure = true; break; } if (!cte->expect_valid_pointer_errors_only(cte, gen->tq, "tone queue is NULL")) { new_failure = true; break; } } if (do_start || do_stop) { if (do_start) { cte->log_info_cont(cte, "start "); } if (do_stop) { cte->log_info_cont(cte, "stop "); } /* I expect that a common pattern will be that generator will be created once, then started/stopped multiple times, and then deleted once. So do start/stop in inner loop here. */ int loops_inner = loops; if (do_start && (!do_stop)) { /* FIXME: there is a problem: if client code will call cw_gen_start() multiple times, without calling cw_gen_stop() after each start(), then we will have a problem: the final delete() call in this test function (which internally calls stop()) will have problem with stopping a generator thread with "pthread_join(gen->thread.id, NULL);" because there were several start() calls and several threads were created in single generator. So for now, if this subroutine was called to do several starts but no stops, limit number of inner loops. The solution can be that start() function looks at state of thread, and doesn't create new one if a generator thread is already running. */ loops_inner = 1; } for (int j = 0; j < loops_inner; j++) { if (do_start) { const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_start)(gen); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "start() (loop #%d/%d - %d/%d)", i + 1, loops, j + 1, loops_inner)) { start_failure = true; break; } } if (do_stop) { const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_stop)(gen); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "stop() (loop #%d/%d - %d/%d)", i + 1, loops_inner, j + 1, loops_inner)) { stop_failure = true; break; } } } if (start_failure || stop_failure) { break; } } if (do_delete) { cte->log_info_cont(cte, "delete "); LIBCW_TEST_FUT(cw_gen_delete)(&gen); if (!cte->expect_null_pointer_errors_only(cte, gen, "delete() (loop #%d/%d)", i + 1, loops)) { delete_failure = true; break; } } cte->log_info_cont(cte, "\n"); } if (do_new) { cte->expect_op_int(cte, false, "==", new_failure, "%s(): new()", function_name); } if (do_start) { cte->expect_op_int(cte, false, "==", start_failure, "%s(): start()", function_name); } if (do_stop) { cte->expect_op_int(cte, false, "==", stop_failure, "%s(): stop()", function_name); } if (do_delete) { cte->expect_op_int(cte, false, "==", delete_failure, "%s(): delete()", function_name); } /* If test fails to delete generator, do it here. */ if (gen) { cw_gen_delete(&gen); } cte->print_test_footer(cte, function_name); return cwt_retv_ok; } /** @brief Test setting tone slope shape and duration @reviewed on 2020-05-08 */ cwt_retv test_cw_gen_set_tone_slope(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Test 0: test property of newly created generator. */ { cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); cte->assert2(cte, gen, "test 0: failed to create generator"); cte->expect_op_int(cte, CW_TONE_SLOPE_SHAPE_RAISED_COSINE, "==", gen->tone_slope.shape, "test 0: initial slope shape (%d)", gen->tone_slope.shape); cte->expect_op_int(cte, CW_AUDIO_SLOPE_DURATION, "==", gen->tone_slope.duration, "test 0: initial slope duration (%d)", gen->tone_slope.duration); cw_gen_delete(&gen); } /* Test A: pass conflicting arguments. "A: If you pass to function conflicting values of \p slope_shape and \p slope_duration, the function will return CW_FAILURE. These conflicting values are rectangular slope shape and larger than zero slope duration. You just can't have rectangular slopes that have non-zero duration." */ { cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); cte->assert2(cte, gen, "test A: failed to create generator"); const int slope_duration = 10; const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, CW_TONE_SLOPE_SHAPE_RECTANGULAR, slope_duration); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "test A: conflicting arguments"); cw_gen_delete(&gen); } /* Test B: pass '-1' as both arguments. "B: If you pass to function '-1' as value of both \p slope_shape and \p slope_duration, the function won't change any of the related two generator's parameters." TODO: add to function description an explicit information that -1/-1 is not an error, and that CW_SUCCESS will be returned. */ { cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); cte->assert2(cte, gen, "test B: failed to create generator"); const int shape_before = gen->tone_slope.shape; const int duration_before = gen->tone_slope.duration; const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, -1, -1); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "test B: set tone slope <-1 -1> (cwret) "); cte->expect_op_int(cte, shape_before, "==", gen->tone_slope.shape, "test B: <-1 -1> (slope shape)"); cte->expect_op_int(cte, duration_before, "==", gen->tone_slope.duration, "test B: <-1 -1> (slope duration)"); cw_gen_delete(&gen); } /* Test C1 "C1: If you pass to function '-1' as value of either \p slope_shape or \p slope_duration, the function will attempt to set only this generator's parameter that is different than '-1'." */ { cw_ret_t cwret; cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); cte->assert2(cte, gen, "test C1: failed to create generator"); /* At the beginning of test these values are generator's initial values. As test progresses, some other values will be expected after successful calls to tested function. */ int expected_shape = CW_TONE_SLOPE_SHAPE_RAISED_COSINE; int expected_duration = CW_AUDIO_SLOPE_DURATION; /* At this point generator should have initial values of its parameters (yes, that's test zero again). */ cte->expect_op_int(cte, expected_shape, "==", gen->tone_slope.shape, "test C1: : initial slope shape"); cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test C1: : initial slope duration"); /* Set only new slope shape. */ expected_shape = CW_TONE_SLOPE_SHAPE_LINEAR; cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, expected_shape, -1); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "test C1: : set"); /* At this point only slope shape should be updated. */ cte->expect_op_int(cte, expected_shape, "==", gen->tone_slope.shape, "test C1: : get"); cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test C1: : preserved slope duration"); /* Set only new slope duration. */ expected_duration = 30; cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, -1, expected_duration); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "set slope: C1: <-1 x>: set"); /* At this point only slope duration should be updated (compared to previous function call). */ cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test C1: <-1 x>: get"); cte->expect_op_int(cte, expected_shape, "==", gen->tone_slope.shape, "test C1: <-1 x>: preserved slope shape"); cw_gen_delete(&gen); } /* Test C2 "C2: However, if selected slope shape is rectangular, function will set generator's slope duration to zero, even if value of \p slope_duration is '-1'." */ { cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); cte->assert2(cte, gen, "test C2: failed to create generator"); cw_ret_t cwret; /* At the beginning of test these values are generator's initial values. As test progresses, some other values will be expected after successful calls to tested function. */ int expected_shape = CW_TONE_SLOPE_SHAPE_RAISED_COSINE; int expected_duration = CW_AUDIO_SLOPE_DURATION; /* At this point generator should have initial values of its parameters (yes, that's test zero again). */ cte->expect_op_int(cte, expected_shape, "==", gen->tone_slope.shape, "test C2: initial slope shape"); cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test C2: initial slope duration"); /* Set only new slope shape. */ expected_shape = CW_TONE_SLOPE_SHAPE_RECTANGULAR; expected_duration = 0; /* Even though we won't pass this to function, this is what we expect to get after this call: we request rectangular slope, which by its nature has zero duration. */ cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, expected_shape, -1); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "test: C2: set rectangular"); /* At this point slope shape AND slope duration should be updated (slope duration is updated only because of requested rectangular slope shape). */ cte->expect_op_int(cte, expected_shape, "==", gen->tone_slope.shape, "test C2: set rectangular: slope shape"); cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test C2: set rectangular: slope duration"); cw_gen_delete(&gen); } /* Test D "D: Notice that the function allows non-rectangular slope shape with zero duration of the slopes. The slopes will be non-rectangular, but just unusually short." */ { cw_ret_t cwret; cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); cte->assert2(cte, gen, "test D: failed to create generator"); const int expected_duration = 0; cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, CW_TONE_SLOPE_SHAPE_LINEAR, expected_duration); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "test D: : cwret"); cte->expect_op_int(cte, CW_TONE_SLOPE_SHAPE_LINEAR, "==", gen->tone_slope.shape, "test D: : slope shape"); cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test D: : slope duration"); cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, CW_TONE_SLOPE_SHAPE_RAISED_COSINE, 0); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "test D: : cwret"); cte->expect_op_int(cte, CW_TONE_SLOPE_SHAPE_RAISED_COSINE, "==", gen->tone_slope.shape, "test D: : slope shape"); cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test D: : slope duration"); cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, CW_TONE_SLOPE_SHAPE_SINE, 0); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "test D: : cwret"); cte->expect_op_int(cte, CW_TONE_SLOPE_SHAPE_SINE, "==", gen->tone_slope.shape, "test D: : slope shape"); cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test D: : slope duration"); cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, CW_TONE_SLOPE_SHAPE_RECTANGULAR, 0); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "test D: : cwret"); cte->expect_op_int(cte, CW_TONE_SLOPE_SHAPE_RECTANGULAR, "==", gen->tone_slope.shape, "test D: : slope shape"); cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test D: : slope duration"); cwret = LIBCW_TEST_FUT(cw_gen_set_tone_slope)(gen, CW_TONE_SLOPE_SHAPE_LINEAR, 0); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "test D: : cwret"); cte->expect_op_int(cte, CW_TONE_SLOPE_SHAPE_LINEAR, "==", gen->tone_slope.shape, "test D: : slope shape"); cte->expect_op_int(cte, expected_duration, "==", gen->tone_slope.duration, "test D: : slope duration"); cw_gen_delete(&gen); } cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Test some assertions about CW_TONE_SLOPE_SHAPE_* Test code in this file depends on the fact that these values are different than -1. I'm testing these values to be sure that when I get a silly idea to modify them, the test will catch this modification. @reviewed on 2020-05-07 */ cwt_retv test_cw_gen_tone_slope_shape_enums(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); const bool failure = CW_TONE_SLOPE_SHAPE_LINEAR < 0 || CW_TONE_SLOPE_SHAPE_RAISED_COSINE < 0 || CW_TONE_SLOPE_SHAPE_SINE < 0 || CW_TONE_SLOPE_SHAPE_RECTANGULAR < 0; cte->expect_op_int(cte, false, "==", failure, "slope shape enums"); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** It's not a test of a "forever" function, but of "forever" functionality. Pay attention to CPU usage during execution of this function. I have been experiencing 100% CPU usage on one of cores with ALSA sound system when HW configuration of the ALSA sound system was incorrect. @reviewed on 2020-10-15 */ cwt_retv test_cw_gen_forever_internal(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); const int seconds = 7; cte->print_test_header(cte, "%s (%d loops, %d seconds)", __func__, loops, seconds); for (int i = 0; i < loops; i++) { const cwt_retv rv = test_cw_gen_forever_sub(cte, seconds); cte->expect_op_int(cte, cwt_retv_ok, "==", rv, "'forever' test"); } cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @reviewed on 2020-05-10 */ static cwt_retv test_cw_gen_forever_sub(cw_test_executor_t * cte, __attribute__((unused)) int seconds) { cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); if (NULL == gen) { cte->log_error(cte, "failed to create generator\n"); return cwt_retv_err; } cw_gen_start(gen); //sleep(1); const int slope_duration = 10000; /* [us] duration of slope that should be 'pleasing' to ear. */ const int freq = cte->config->frequency; cw_tone_t tone; CW_TONE_INIT(&tone, freq, slope_duration, CW_SLOPE_MODE_RISING_SLOPE); const cw_ret_t cwret1 = cw_tq_enqueue_internal(gen->tq, &tone); cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret1, "enqueue first tone"); /* Use "errors only" here because this is not a core part of test. */ CW_TONE_INIT(&tone, freq, gen->quantum_duration, CW_SLOPE_MODE_NO_SLOPES); tone.is_forever = true; tone.debug_id = 'f'; const cw_ret_t cwret2 = cw_tq_enqueue_internal(gen->tq, &tone); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret2, "enqueue forever tone"); #ifdef __FreeBSD__ /* Tested on FreeBSD 10. */ /* Separate path for FreeBSD because for some reason signals badly interfere with value returned through second arg to nanolseep(). Try to run the section in #else under FreeBSD to see what happens - value returned by nanosleep() through "rem" will be increasing. TODO: see if the problem still persists after moving from signals to conditional variables. */ fprintf(stderr, "enter any character to end \"forever\" tone\n"); char c; scanf("%c", &c); #else cw_usleep_internal(seconds * CW_USECS_PER_SEC); #endif /* Silence the generator. */ CW_TONE_INIT(&tone, freq, slope_duration, CW_SLOPE_MODE_FALLING_SLOPE); tone.debug_id = 'e'; const cw_ret_t cwret3 = cw_tq_enqueue_internal(gen->tq, &tone); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret3, "silence the generator"); cw_gen_wait_for_queue_level(gen, 0); cw_gen_wait_for_end_of_current_tone(gen); cw_gen_stop(gen); cw_gen_delete(&gen); return cwt_retv_ok; } /** @reviewed on 2020-05-07 */ cwt_retv test_cw_gen_get_timing_parameters_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); int initial = -5; int dot_duration = initial; int dash_duration = initial; int ims_duration = initial; int ics_duration = initial; int iws_duration = initial; int additional_space_duration = initial; int adjustment_space_duration = initial; cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); cw_gen_start(gen); cw_gen_reset_parameters_internal(gen); /* Reset requires resynchronization. */ cw_gen_sync_parameters_internal(gen); LIBCW_TEST_FUT(cw_gen_get_timing_parameters_internal)(gen, &dot_duration, &dash_duration, &ims_duration, &ics_duration, &iws_duration, &additional_space_duration, &adjustment_space_duration); bool failure = (dot_duration == initial) || (dash_duration == initial) || (ims_duration == initial) || (ics_duration == initial) || (iws_duration == initial) || (additional_space_duration == initial) || (adjustment_space_duration == initial); cte->expect_op_int(cte, false, "==", failure, "get timing parameters"); cw_gen_delete(&gen); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Test setting and getting of some basic parameters @reviewed on 2020-05-07 */ cwt_retv test_cw_gen_parameter_getters_setters(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* No parameter will have value that is larger (for "max" params) or smaller (for "min" params) than this, so this is a good initial value. */ int off_limits = 10000; cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); /* It shouldn't matter for functions tested here if generator is started or not. */ cw_gen_start(gen); struct { /* There are tree functions that take part in the test: first gets range of acceptable values, seconds sets a new value of parameter, and third reads back the value. */ void (* get_limits)(int * min, int * max); cw_ret_t (* set_new_value)(cw_gen_t * gen, int new_value); int (* get_value)(cw_gen_t const * gen); const int expected_min; /* Expected value of minimum. */ const int expected_max; /* Expected value of maximum. */ int readback_min; /* Value returned by 'get_limits()' function. */ int readback_max; /* Value returned by 'get_limits()' function. */ const char * name; } test_data[] = { { LIBCW_TEST_FUT(cw_get_speed_limits), LIBCW_TEST_FUT(cw_gen_set_speed), LIBCW_TEST_FUT(cw_gen_get_speed), CW_SPEED_MIN, CW_SPEED_MAX, off_limits, -off_limits, "speed" }, { LIBCW_TEST_FUT(cw_get_frequency_limits), LIBCW_TEST_FUT(cw_gen_set_frequency), LIBCW_TEST_FUT(cw_gen_get_frequency), CW_FREQUENCY_MIN, CW_FREQUENCY_MAX, off_limits, -off_limits, "frequency" }, { LIBCW_TEST_FUT(cw_get_volume_limits), LIBCW_TEST_FUT(cw_gen_set_volume), LIBCW_TEST_FUT(cw_gen_get_volume), CW_VOLUME_MIN, CW_VOLUME_MAX, off_limits, -off_limits, "volume" }, { LIBCW_TEST_FUT(cw_get_gap_limits), LIBCW_TEST_FUT(cw_gen_set_gap), LIBCW_TEST_FUT(cw_gen_get_gap), CW_GAP_MIN, CW_GAP_MAX, off_limits, -off_limits, "gap" }, { LIBCW_TEST_FUT(cw_get_weighting_limits), LIBCW_TEST_FUT(cw_gen_set_weighting), LIBCW_TEST_FUT(cw_gen_get_weighting), CW_WEIGHTING_MIN, CW_WEIGHTING_MAX, off_limits, -off_limits, "weighting" }, { NULL, NULL, NULL, 0, 0, 0, 0, NULL } }; for (int i = 0; test_data[i].get_limits; i++) { int value = 0; int cwret = CW_FAILURE; /* Test getting limits of values to be tested. */ test_data[i].get_limits(&test_data[i].readback_min, &test_data[i].readback_max); if (!cte->expect_op_int(cte, test_data[i].expected_min, "==", test_data[i].readback_min, "get %s limits: min", test_data[i].name)) { break; } if (!cte->expect_op_int(cte, test_data[i].expected_max, "==", test_data[i].readback_max, "get %s limits: max", test_data[i].name)) { break; } /* Test setting out-of-range value lower than minimum. */ errno = 0; value = test_data[i].readback_min - 1; cwret = test_data[i].set_new_value(gen, value); if (!cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "set %s below limit (cwret)", test_data[i].name)) { break; } if (!cte->expect_op_int(cte, EINVAL, "==", errno, "set %s below limit (errno)", test_data[i].name)) { break; } /* Test setting out-of-range value higher than maximum. */ errno = 0; value = test_data[i].readback_max + 1; cwret = test_data[i].set_new_value(gen, value); if (!cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "set %s above limit (cwret)", test_data[i].name)) { break; } if (!cte->expect_op_int(cte, EINVAL, "==", errno, "set %s above limit (errno)", test_data[i].name)) { break; } /* Test setting in-range values. Set with setter and then read back with getter. */ bool set_within_range_cwret_failure = false; bool set_within_range_errno_failure = false; bool set_within_range_readback_failure = false; for (int value_to_set = test_data[i].readback_min; value_to_set <= test_data[i].readback_max; value_to_set++) { errno = 0; cwret = test_data[i].set_new_value(gen, value_to_set); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "set %s within limits (cwret) (value to set = %d)", test_data[i].name, value_to_set)) { set_within_range_cwret_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, 0, "==", errno, "set %s within limits (errno) (value to set = %d)", test_data[i].name, value_to_set)) { set_within_range_errno_failure = true; break; } const int readback_value = test_data[i].get_value(gen); if (!cte->expect_op_int_errors_only(cte, readback_value, "==", value_to_set, "readback %s within limits (value to set = %d)", test_data[i].name, value_to_set)) { set_within_range_readback_failure = true; break; } } cte->expect_op_int(cte, false, "==", set_within_range_cwret_failure, "set %s within range (cwret)", test_data[i].name); cte->expect_op_int(cte, false, "==", set_within_range_errno_failure, "set %s within range (errno)", test_data[i].name); cte->expect_op_int(cte, false, "==", set_within_range_readback_failure, "set %s within range (readback)", test_data[i].name); } cw_gen_delete(&gen); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Test control of generator's volume Check that we can set the volume through its entire range. @reviewed on 2020-05-05 */ cwt_retv test_cw_gen_volume_functions(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); const int volume_delta = 1; /* [%] volume change in percent. */ const int tone_freq = cte->config->frequency; const int tone_duration = 70000; /* [microseconds] Duration can't be too short, because the loops will run too fast. */ cw_gen_t * gen = cw_gen_new(&cte->current_gen_conf); /* Test: get range of allowed volumes. */ int volume_min = -1; int volume_max = -1; { LIBCW_TEST_FUT(cw_get_volume_limits)(&volume_min, &volume_max); const bool failure = volume_min != CW_VOLUME_MIN || volume_max != CW_VOLUME_MAX; cte->expect_op_int(cte, false, "==", failure, "get volume limits: %d, %d", volume_min, volume_max); } /* Test: decrease volume from max to min. */ { /* Few initial tones to make tone queue non-empty. */ for (int i = 0; i < 3; i++) { cw_tone_t tone; const int slope_mode = i == 0 ? CW_SLOPE_MODE_RISING_SLOPE : CW_SLOPE_MODE_NO_SLOPES; /* Rising slope only in first tone in series of tones. */ CW_TONE_INIT(&tone, tone_freq, tone_duration, slope_mode); cw_tq_enqueue_internal(gen->tq, &tone); } bool set_failure = false; bool get_failure = false; cw_gen_start(gen); for (int vol = volume_max; vol >= volume_min; vol -= volume_delta) { cw_tone_t tone; const int slope_mode = vol == volume_min ? CW_SLOPE_MODE_FALLING_SLOPE : CW_SLOPE_MODE_NO_SLOPES; /* No slopes in the middle - keep each level of tone constant at the beginning and end. */ CW_TONE_INIT(&tone, tone_freq, tone_duration, slope_mode); cw_tq_enqueue_internal(gen->tq, &tone); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_set_volume)(gen, vol); if (!cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "set volume (down, vol = %d)", vol)) { set_failure = true; break; } const int readback_value = LIBCW_TEST_FUT(cw_gen_get_volume)(gen); if (!cte->expect_op_int(cte, readback_value, "==", vol, "get volume (down, vol = %d)", vol)) { get_failure = true; break; } //fprintf(stderr, "len = %zd\n", cw_gen_get_queue_length(gen)); cw_gen_wait_for_queue_level(gen, 1); } cw_gen_wait_for_queue_level(gen, 0); usleep(2 * tone_duration); cw_gen_stop(gen); cte->expect_op_int(cte, false, "==", set_failure, "set volume (down)"); cte->expect_op_int(cte, false, "==", get_failure, "get volume (down)"); } /* Test: increase volume from min to max. */ { /* Few initial tones to make tone queue non-empty. */ for (int i = 0; i < 3; i++) { cw_tone_t tone; const int slope_mode = i == 0 ? CW_SLOPE_MODE_RISING_SLOPE : CW_SLOPE_MODE_NO_SLOPES; /* Rising slope only in first tone in series of tones. */ CW_TONE_INIT(&tone, tone_freq, tone_duration, slope_mode); cw_tq_enqueue_internal(gen->tq, &tone); } bool set_failure = false; bool get_failure = false; cw_gen_start(gen); for (int vol = volume_min; vol <= volume_max; vol += volume_delta) { cw_tone_t tone; const int slope_mode = vol == volume_max ? CW_SLOPE_MODE_FALLING_SLOPE : CW_SLOPE_MODE_NO_SLOPES; /* No slopes in the middle - keep each level of tone constant at the beginning and end. */ CW_TONE_INIT(&tone, tone_freq, tone_duration, slope_mode); cw_tq_enqueue_internal(gen->tq, &tone); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_set_volume)(gen, vol); if (!cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "set volume (up, vol = %d)", vol)) { set_failure = true; break; } const int readback_value = LIBCW_TEST_FUT(cw_gen_get_volume)(gen); if (!cte->expect_op_int(cte, readback_value, "==", vol, "get volume (up, vol = %d)", vol)) { get_failure = true; break; } //fprintf(stderr, "len = %zd\n", cw_gen_get_queue_length(gen)); cw_gen_wait_for_queue_level(gen, 1); } cw_gen_wait_for_queue_level(gen, 0); usleep(2 * tone_duration); cw_gen_stop(gen); cte->expect_op_int(cte, false, "==", set_failure, "set volume (up)"); cte->expect_op_int(cte, false, "==", get_failure, "get volume (up)"); } #if 0 /* FIXME: check behaviour of these function for empty tone queue, particularly if each of the functions is called one after another, like this: cw_gen_wait_for_end_of_current_tone(gen); cw_gen_wait_for_end_of_current_tone(gen); or this: cw_gen_wait_for_queue_level(gen, 0); cw_gen_wait_for_queue_level(gen, 0); */ cw_gen_wait_for_end_of_current_tone(gen); cw_gen_wait_for_queue_level(gen, 0); #endif cw_gen_delete(&gen); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Test enqueueing and playing most basic elements of Morse code @reviewed on 2020-05-09 */ cwt_retv test_cw_gen_enqueue_primitives(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); cw_gen_t * gen = NULL; if (cwt_retv_ok != gen_setup(cte, &gen)) { cte->log_error(cte, "%s:%d: Failed to create generator\n", __func__, __LINE__); return cwt_retv_err; } cw_gen_start(gen); /* Test: playing dots. */ { bool failure = false; for (int i = 0; i < loops; i++) { const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_mark_internal)(gen, CW_DOT_REPRESENTATION, false); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueue mark internal(CW_DOT_REPRESENTATION) (i = %d)", i)) { failure = true; break; } } cw_gen_wait_for_queue_level(gen, 0); cte->expect_op_int(cte, false, "==", failure, "enqueue mark internal(CW_DOT_REPRESENTATION)"); } /* Test: playing dashes. */ { bool failure = false; for (int i = 0; i < loops; i++) { const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_mark_internal)(gen, CW_DASH_REPRESENTATION, false); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueue mark internal(CW_DASH_REPRESENTATION) (i = %d)", i)) { failure = true; break; } } cw_gen_wait_for_queue_level(gen, 0); cte->expect_op_int(cte, false, "==", failure, "enqueue mark internal(CW_DASH_REPRESENTATION)"); } /* Test: playing inter-character-spaces. */ { bool failure = false; for (int i = 0; i < loops; i++) { /* TODO: this function adds 2-Unit space, not a regular 3-Unit inter-character-space. Be aware of this fact. */ const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_2u_ics_internal)(gen); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueue ics internal() (i = %d)", i)) { failure = true; break; } } cw_gen_wait_for_queue_level(gen, 0); cte->expect_op_int(cte, false, "==", failure, "enqueue ics internal()"); } /* Test: playing inter-word-spaces. */ { bool failure = false; for (int i = 0; i < loops; i++) { const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_iws_internal)(gen); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueue iws internal() (i = %d)", i)) { failure = true; break; } } cw_gen_wait_for_queue_level(gen, 0); cte->expect_op_int(cte, false, "==", failure, "enqueue iws internal()"); } gen_destroy(&gen); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Test playing representations of characters @reviewed on 2020-05-09 */ cwt_retv test_cw_gen_enqueue_representations(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); /* Representation is valid when it contains dots and dashes only. cw_gen_enqueue_representation*() doesn't check if given representation represents a supported character. */ cw_gen_t * gen = NULL; if (cwt_retv_ok != gen_setup(cte, &gen)) { cte->log_error(cte, "%s:%d: Failed to create generator\n", __func__, __LINE__); return cwt_retv_err; } cw_gen_start(gen); /* Test: playing valid representations. */ { bool failure = false; for (int rep = 0; rep < loops; rep++) { int i = 0; while (NULL != test_valid_representations[i]) { cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_representation)(gen, test_valid_representations[i]); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueue representation() (%d)", i)) { failure = true; break; } cwret = LIBCW_TEST_FUT(cw_gen_enqueue_representation_no_ics)(gen, test_valid_representations[i]); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueue representation, no ics() (%d)", i)) { failure = true; break; } i++; } if (failure) { break; } } cw_gen_wait_for_queue_level(gen, 0); cte->expect_op_int(cte, false, "==", failure, "enqueue representation internal()"); } /* Test: trying to play invalid representations. */ { bool failure = false; for (int rep = 0; rep < loops; rep++) { int i = 0; while (NULL != test_invalid_representations[i]) { cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_representation)(gen, test_invalid_representations[i]); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "enqueue representation() (%d)", i)) { failure = true; break; } cwret = LIBCW_TEST_FUT(cw_gen_enqueue_representation_no_ics)(gen, test_invalid_representations[i]); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "enqueue representation, no ics() (%d)", i)) { failure = true; break; } i++; } if (failure) { break; } } cw_gen_wait_for_queue_level(gen, 0); cte->expect_op_int(cte, false, "==", failure, "enqueue representation internal()"); } #if 0 /* TODO: remove this. We wait here for generator's queue to drain completely, and this should be done in some other way. */ cw_usleep_internal(1 * CW_USECS_PER_SEC); #endif gen_destroy(&gen); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Play all supported characters as individual characters @reviewed on 2020-05-09 */ cwt_retv test_cw_gen_enqueue_character(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); cw_gen_t * gen = NULL; if (cwt_retv_ok != gen_setup(cte, &gen)) { cte->log_error(cte, "%s:%d: Failed to create generator\n", __func__, __LINE__); return cwt_retv_err; } cw_gen_start(gen); /* Test: play all supported characters as individual characters. */ { char charlist[UCHAR_MAX + 1] = { 0 }; bool failure = false; /* Enqueue all the characters from the charlist individually. */ cw_list_characters(charlist); cte->log_info(cte, "enqueue character():\n" " "); for (int i = 0; charlist[i] != '\0'; i++) { cte->log_info_cont(cte, "%c", charlist[i]); cte->flush_info(cte); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_character)(gen, charlist[i]); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueue character() (i = %d, char = '%c')", i, charlist[i])) { failure = true; break; } cw_gen_wait_for_queue_level(gen, 0); /* TODO: the queue level should be randomized. */ } cte->log_info_cont(cte, "\n"); cte->flush_info(cte); cte->expect_op_int(cte, false, "==", failure, "enqueue character()"); } /* Test: trying to play invalid characters. */ { bool failure = false; for (int rep = 0; rep < loops; rep++) { const char invalid_characters[] = { 0x00, 0x01 }; /* List of invalid characters to be enqueued. */ const int n = sizeof (invalid_characters) / sizeof (invalid_characters[0]); for (int i = 0; i < n; i++) { const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_character)(gen, invalid_characters[i]); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "enqueue character() (i = %d)", i)) { failure = true; break; } } if (failure) { break; } } cte->expect_op_int(cte, false, "==", failure, "enqueue character()"); } gen_destroy(&gen); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Enqueue all supported characters as a single string @reviewed on 2020-05-09 */ cwt_retv test_cw_gen_enqueue_string(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); cw_gen_t * gen = NULL; if (cwt_retv_ok != gen_setup(cte, &gen)) { cte->log_error(cte, "%s:%d: Failed to create generator\n", __func__, __LINE__); return cwt_retv_err; } cw_gen_start(gen); /* Test: playing all supported characters as single string. */ { char charlist[UCHAR_MAX + 1] = { 0 }; cw_list_characters(charlist); /* Enqueue the complete charlist as a single string. */ cte->log_info(cte, "enqueue string():\n" " %s\n", charlist); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_string)(gen, charlist); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "enqueue string()"); while (cw_gen_get_queue_length(gen) > 0) { cte->log_info(cte, "tone queue length %-6zu\r", cw_gen_get_queue_length(gen)); cte->flush_info(cte); cw_gen_wait_for_end_of_current_tone(gen); } cte->log_info(cte, "tone queue length %-6zu\n", cw_gen_get_queue_length(gen)); cte->flush_info(cte); cw_gen_wait_for_queue_level(gen, 0); } /* Test: trying to enqueue invalid strings. */ { bool failure = false; for (int rep = 0; rep < loops; rep++) { int i = 0; while (NULL != test_invalid_strings[i]) { const char * test_string = test_invalid_strings[i]; const cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_enqueue_string)(gen, test_string); if (!cte->expect_op_int_errors_only(cte, CW_FAILURE, "==", cwret, "enqueue string() (i = %d, string = '%s')", i, test_string)) { failure = true; break; } i++; } if (failure) { break; } } cte->expect_op_int(cte, false, "==", failure, "enqueue string()"); } gen_destroy(&gen); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Test removing a character from end of enqueued characters @reviewed on 2020-08-24 */ cwt_retv test_cw_gen_remove_last_character(cw_test_executor_t * cte) { cte->print_test_header(cte, "%s", __func__); cw_gen_t * gen = NULL; if (cwt_retv_ok != gen_setup(cte, &gen)) { cte->log_error(cte, "%s:%d: Failed to create generator\n", __func__, __LINE__); return cwt_retv_err; } const int n = 4; bool failure = false; for (int to_remove = 0; to_remove <= n; to_remove++) { cw_gen_start(gen); cte->log_info(cte, "You will now hear 'oooo' followed by %d 's' characters\n", n - to_remove); cw_gen_enqueue_string(gen, "oooo" "ssss"); /* Remove N characters from end. */ for (int i = 0; i < to_remove; i++) { cw_ret_t cwret = LIBCW_TEST_FUT(cw_gen_remove_last_character(gen)); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "remove last %d characters, removing %d-th character", to_remove, i)) { failure = true; break; } } cw_gen_wait_for_queue_level(gen, 0); cw_usleep_internal(1000 * 1000); cw_gen_stop(gen); if (failure) { break; } } gen_destroy(&gen); cte->expect_op_int(cte, false, "==", failure, "remove last character"); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } unixcw-3.6.0/src/libcw/tests/libcw_rec_tests.h0000644000175000017500000000146014000344554016333 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_REC_TESTS_H_ #define _LIBCW_REC_TESTS_H_ #include "test_framework.h" int test_cw_rec_with_base_data_fixed(void); int test_cw_rec_with_random_data_fixed(void); int test_cw_rec_with_random_data_adaptive(void); int test_cw_rec_identify_mark_internal(cw_test_executor_t * cte); int test_cw_rec_test_with_constant_speeds(cw_test_executor_t * cte); int test_cw_rec_test_with_varying_speeds(cw_test_executor_t * cte); int test_cw_rec_get_receive_parameters(cw_test_executor_t * cte); int test_cw_rec_parameter_getters_setters_1(cw_test_executor_t * cte); int test_cw_rec_parameter_getters_setters_2(cw_test_executor_t * cte); #endif /* #ifndef _LIBCW_REC_TESTS_H_ */ unixcw-3.6.0/src/libcw/tests/libcw_legacy_api_tests.h0000644000175000017500000000324514000344554017662 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_LEGACY_TESTS_H_ #define _LIBCW_LEGACY_TESTS_H_ #include "test_framework.h" /* "Tone queue" topic. */ int legacy_api_test_cw_wait_for_tone(cw_test_executor_t * cte); int legacy_api_test_cw_wait_for_tone_queue(cw_test_executor_t * cte); int legacy_api_test_cw_queue_tone(cw_test_executor_t * cte); int legacy_api_test_empty_tone_queue(cw_test_executor_t * cte); int legacy_api_test_full_tone_queue(cw_test_executor_t * cte); int legacy_api_test_tone_queue_callback(cw_test_executor_t * cte); /* "Generator" topic. */ int legacy_api_test_volume_functions(cw_test_executor_t * cte); int legacy_api_test_send_primitives(cw_test_executor_t * cte); int legacy_api_test_send_character_and_string(cw_test_executor_t * cte); int legacy_api_test_representations(cw_test_executor_t * cte); int legacy_api_test_basic_gen_operations(cw_test_executor_t * cte); cwt_retv legacy_api_test_gen_remove_last_character(cw_test_executor_t * cte); /* "Morse key" topic. */ int legacy_api_test_iambic_key_dot(cw_test_executor_t * cte); int legacy_api_test_iambic_key_dash(cw_test_executor_t * cte); int legacy_api_test_iambic_key_alternating(cw_test_executor_t * cte); int legacy_api_test_iambic_key_none(cw_test_executor_t * cte); int legacy_api_test_straight_key(cw_test_executor_t * cte); /* Other functions. */ int legacy_api_test_low_level_gen_parameters(cw_test_executor_t * cte); int legacy_api_test_parameter_ranges(cw_test_executor_t * cte); // int legacy_api_cw_test_delayed_release(cw_test_executor_t * cte); #endif /* #ifndef _LIBCW_LEGACY_TESTS_H_ */ unixcw-3.6.0/src/libcw/tests/libcw_gen_tests_state_callback.h0000644000175000017500000000055014000344554021346 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_GEN_TESTS_STATE_CALLBACK_H_ #define _LIBCW_GEN_TESTS_STATE_CALLBACK_H_ #include "test_framework.h" int test_cw_gen_state_callback(cw_test_executor_t * cte); #endif /* _LIBCW_GEN_TESTS_STATE_CALLBACK_H_ */ unixcw-3.6.0/src/libcw/tests/count_functions_under_test.py0000644000175000017500000001672614000344554021050 00000000000000#------------------------------------------------------------------------------- # Converted from elftools example: elf_low_high_api.py # # Eli Bendersky (eliben@gmail.com) # This code is in the public domain #------------------------------------------------------------------------------- # # Code in this file counts libcw function symbols in libcw's .so file # (function's whose names start with "cw_"). The .so file must be # generated by "make check" command to have more library functions # visible to the tool. # The code also discovers and counts functions in libcw's test code, # marked with "LIBCW_TEST_FUT()" macro - those functions are tested by # the test code. # Then the code counts percentage of tested functions in relation to # all functions from library. Thanks to the calculated value we have # additional information about test coverage. # # python3 ./this_script --so --code # # Kamil Ignacak (not a python master, obviously), 2020 # import sys import os # If pyelftools is not installed, the example can also run from the root or # examples/ dir of the source distribution. sys.path[0:0] = ['.', '..'] from elftools.elf.elffile import ELFFile from elftools.elf.sections import SymbolTableSection def find_libcw_functions_in_so(filename): result = set() with open(filename, 'rb') as f: result = section_info_highlevel(f) return result def section_info_highlevel(stream): result = set() elffile = ELFFile(stream) # Just use the public methods of ELFFile to get what we need # Note that section names are strings. print(" {} sections".format(elffile.num_sections())) section = elffile.get_section_by_name('.symtab') if not section: print(' No symbol table found. Perhaps this ELF has been stripped?') return result # A section type is in its header, but the name was decoded and placed in # a public attribute. print(' Section name: %s, type: %s' %(section.name, section['sh_type'])) # But there's more... If this section is a symbol table section (which is # the case in the sample ELF file that comes with the examples), we can # get some more information about it. if not isinstance(section, SymbolTableSection): return result num_symbols = section.num_symbols() print(" It's a symbol section with %s symbols" % num_symbols) for i in range(0, num_symbols): symbol_type = section.get_symbol(i)['st_info']['type']; if symbol_type != 'STT_FUNC': continue symbol_name = section.get_symbol(i).name if symbol_name[0:3] != "cw_": continue if "localalias" in symbol_name: # e.g. cw_send_representation_partial.localalias.2; TODO: why do we have symbols like this one? continue result.add(symbol_name) return result def find_tested_functions_in_file(file_handle, fut_tag): result = set() while True: line = file_handle.readline() if not line: break idx_start = line.find(fut_tag) if -1 == idx_start: continue idx_start += len(fut_tag + "(") # tag + opening paren: "LIBCW_TEST_FUT(" # Find closing paren of FUT macro; should cover both usages of # FUT macro: on a name of called function, and on a name of # function pointer variable. idx_end = line.find(")", idx_start) if -1 == idx_end: continue function = line[idx_start:idx_end] if not function: continue result.add(function) return result def find_tested_functions_in_dir(directory, fut_tag): result = set() for name in os.listdir(directory): if not name.endswith(".c"): continue with open(directory + "/" + name) as file: result |= find_tested_functions_in_file(file, fut_tag) # merge two sets return result # Print a set with function names: global functions first, internal # functions second. def print_functions_set(header, functions): print("\n" + header) for fun in sorted(functions): if not fun.endswith("_internal"): print(" " + fun) print("") for fun in sorted(functions): if fun.endswith("_internal"): print(" " + fun) print("") def print_function_row(function_counter, function_name, is_in_so, is_in_fut): print('{:>3} {:<50}'.format(function_counter, function_name), end='') if is_in_so: print(" ", end='') else: print(" so-", end='') if is_in_fut: print(" ") else: print(" fut-") def print_stats(n_tested, n_all): print("Functions under test: {} out of {} ({:.2f}%)".format(n_tested, n_all, 100.0 * n_tested/n_all)) def print_help(script_name): print("Count how many libcw functions are tested") print("Call it like this:\n") print("python3 {}\n" " or\n" "python3 {} --so --code ".format(script_name, script_name)) if __name__ == '__main__': so_symbols = set() tested_functions = set() so_location = "" test_code_location = "" if len(sys.argv) == 1: so_location = "../.libs/libcw.so" test_code_location = "./" elif len(sys.argv) == 5: if sys.argv[1] == '--so' and sys.argv[3] == '--code': so_location = sys.argv[2] test_code_location = sys.argv[4] else: print_help(sys.argv[0]) sys.exit(1) else: print_help(sys.argv[0]) sys.exit(1) so_symbols = find_libcw_functions_in_so(so_location) tested_functions = find_tested_functions_in_dir(test_code_location, "LIBCW_TEST_FUT") #print_functions_set("All libcw.so function symbols:", so_symbols) #print_functions_set("\nlibcw FUT functions (functions under test):", tested_functions) #print_functions_set("libcw functions not tested yet:", so_symbols - tested_functions) #print("\n") all_functions = so_symbols | tested_functions n_tested = len(tested_functions) n_all = len(all_functions) # Check if all tested symbols were found of list of symbols in # library: #if not tested_functions.issubset(so_symbols): # print("Error: set of FUT functions is not a subset of all so symbols") # # First build set of functions that are not common, and then preserve only tested functions. # not_in_so = (so_symbols ^ tested_functions) & tested_functions # print_functions_set("FUT functions not in .so:", not_in_so) print_stats(n_tested, n_all) print("") print("'fut-' - function not tested (not marked by 'function under test' macro") print("'so-' - function not found in .so library"); print("") fun_counter = 1 for fun in sorted(all_functions): if not fun.endswith("_internal"): is_in_so = fun in so_symbols is_in_fut = fun in tested_functions print_function_row(fun_counter, fun, is_in_so, is_in_fut) fun_counter += 1 print("") for fun in sorted(all_functions): if fun.endswith("_internal"): is_in_so = fun in so_symbols is_in_fut = fun in tested_functions print_function_row(fun_counter, fun, is_in_so, is_in_fut) fun_counter += 1 # Print the stats again after a long long list of functions has been # printed. print("") print_stats(n_tested, n_all) unixcw-3.6.0/src/libcw/tests/libcw_gen_tests_state_callback.c0000644000175000017500000006417714000344554021360 00000000000000#include /* fabsf() */ #include "libcw2.h" #include "libcw_gen_tests_state_callback.h" #include "libcw_gen.h" #include "libcw_utils.h" /* This test is verifying if callback called on each change of state of generator is called at proper intervals. Client code can use cw_gen_register_value_tracking_callback_internal() to register a callback. The callback will be called each time the generator changes it's state between mark/space. The changes of state should occur at time intervals specified by length of marks (dots, dashes) and spaces. libcw is using sound card's (sound system's) blocking write property to measure how often and for how long a generator should stay in particular state. Generator will write e.g. mark to sound system, the blocking write will block the generator for specified time, and then the generator will be able to get from tone queue the next element (mark or space) and do another blocking write to sound system. Calls to the callback are made between each blocking write. The calls to the callback will be made with smaller or larger precision, depending on: 1. the exact mechanism used by libcw. For now libcw uses blocking write, but e.g. ALSA is offering other mechanisms that are right now unexplored. 2. how well the libcw has configured the mechanism. Commit cb99e7884dc5370519dc3a3eacfb3184959b0f87 has fixed an error in configuration of ALSA's HW period size. That incorrect configuration has led to the callback being called at totally invalid (very, very short) intervals. I started writing this test to verify that the fix from commit cb99e7884dc5370519dc3a3eacfb3184959b0f87 has been working, but then I thought that the test can work for any sound system supported by libcw. I don't need to verify if low-level configuration of ALSA is working correctly, I just need to verify if the callback mechanism (relying on proper configuration of a sound system) is working correctly. */ /* With this input string we expect: 6*3 letters, each with 3 marks and 3 inter-mark-spaces = 108. */ static const char * input_string = "ooo""ooo""ooo""sss""sss""sss"; #define INPUT_ELEMENTS_COUNT 108 #define CW_EOE_REPRESENTATION '^' typedef struct divergence_t { float min; float avg; float max; } divergence_t; typedef struct cw_element_t { char representation; int duration; /* microseconds */ } cw_element_t; typedef struct cw_element_stats_t { int duration_min; int duration_avg; int duration_max; int duration_total; int count; } cw_element_stats_t; /* Type of data passed to callback. We need to store a persistent state of some data between callback calls. */ typedef struct callback_data_t { struct timeval prev_timestamp; /* Timestamp at which previous callback was made. */ int counter; } callback_data_t; typedef struct test_data_t { /* Sound system for which we have reference data, and with which the current run of a test should be done. */ enum cw_audio_systems sound_system; /* Speed (WPM) for which we have reference data, and at which current run of a test should be done (otherwise we will be comparing results of tests made in different conditions). */ int speed; /* Reference values from tests in post_3.5.1 branch. */ struct divergence_t reference_div_dots; struct divergence_t reference_div_dashes; /* Values obtained in current test run. */ struct divergence_t current_div_dots; struct divergence_t current_div_dashes; } test_data_t; static void gen_callback_fn(void * callback_arg, int state); static void update_element_stats(cw_element_stats_t * stats, int element_duration); static void print_element_stats_and_divergences(const cw_element_stats_t * stats, const divergence_t * divergences, const char * name, int duration_expected); static void calculate_divergences_from_stats(const cw_element_stats_t * stats, divergence_t * divergences, int duration_expected); static cwt_retv test_cw_gen_state_callback_sub(cw_test_executor_t * cte, test_data_t * test_data, const char * sound_device); static void calculate_test_results(const cw_element_t * test_input_elements, test_data_t * test_data, int dot_usecs, int dash_usecs); static void evaluate_test_results(cw_test_executor_t * cte, test_data_t * test_data); static void clear_data(cw_element_t * test_input_elements); /** Results of test from reference branch post_3.5.1 with: 1. fixed ALSA HW period size, 2. modified PA parameters, copied from these two commits: https://github.com/m5evt/unixcw-3.5.1/commit/2d5491a461587ac4686e2d1b897619c98be05c9e https://github.com/m5evt/unixcw-3.5.1/commit/c86785b595a6d711aae915150df2ccb848ace05c For OSS sound system I'm copying results for ALSA. For Console sound system I'm copying results for Null sound system (both systems simulate a blocking write with sleep function). Null sound system, 4 WMP [II] duration of dashes: min/avg/max = 900130/900208/900239, expected = 900000, divergence min/avg/max = 0.014%/ 0.023%/ 0.027% [II] duration of dots: min/avg/max = 300123/300204/300225, expected = 300000, divergence min/avg/max = 0.041%/ 0.068%/ 0.075% ALSA sound system, 4 WPM [II] duration of dashes: min/avg/max = 892462/895727/898281, expected = 900000, divergence min/avg/max = -0.838%/ -0.475%/ -0.191% [II] duration of dots: min/avg/max = 293087/296211/299484, expected = 300000, divergence min/avg/max = -2.304%/ -1.263%/ -0.172% PulseAudio sound system, 4 WPM [II] duration of dashes: min/avg/max = 897406/900200/906788, expected = 900000, divergence min/avg/max = -0.288%/ 0.022%/ 0.754% [II] duration of dots: min/avg/max = 294511/299532/303330, expected = 300000, divergence min/avg/max = -1.830%/ -0.156%/ 1.110% Null sound system, 12 WPM [II] duration of dashes: min/avg/max = 300102/300192/300238, expected = 300000, divergence min/avg/max = 0.034%/ 0.064%/ 0.079% [II] duration of dots: min/avg/max = 100113/100189/100241, expected = 100000, divergence min/avg/max = 0.113%/ 0.189%/ 0.241% ALSA sound system, 12 WPM [II] duration of dashes: min/avg/max = 292760/295770/299096, expected = 300000, divergence min/avg/max = -2.413%/ -1.410%/ -0.301% [II] duration of dots: min/avg/max = 93308/ 96458/ 98940, expected = 100000, divergence min/avg/max = -6.692%/ -3.542%/ -1.060% PulseAudio sound system, 12 WPM [II] duration of dashes: min/avg/max = 295562/299878/303009, expected = 300000, divergence min/avg/max = -1.479%/ -0.041%/ 1.003% [II] duration of dots: min/avg/max = 97274/ 99937/106259, expected = 100000, divergence min/avg/max = -2.726%/ -0.063%/ 6.259% Null sound system, 24 WPM [II] duration of dashes: min/avg/max = 150116/150197/150255, expected = 150000, divergence min/avg/max = 0.077%/ 0.131%/ 0.170% [II] duration of dots: min/avg/max = 50110/ 50158/ 50222, expected = 50000, divergence min/avg/max = 0.220%/ 0.316%/ 0.444% ALSA sound system, 24 WPM [II] duration of dashes: min/avg/max = 141977/147780/154997, expected = 150000, divergence min/avg/max = -5.349%/ -1.480%/ 3.331% [II] duration of dots: min/avg/max = 44731/ 47361/ 49472, expected = 50000, divergence min/avg/max = -10.538%/ -5.278%/ -1.056% PulseAudio sound system, 24 WPM [II] duration of dashes: min/avg/max = 142998/149541/152615, expected = 150000, divergence min/avg/max = -4.668%/ -0.306%/ 1.743% [II] duration of dots: min/avg/max = 45374/ 50002/ 53423, expected = 50000, divergence min/avg/max = -9.252%/ 0.004%/ 6.846% Null sound system, 36 WPM [II] duration of dashes: min/avg/max = 100149/100217/100260, expected = 99999, divergence min/avg/max = 0.150%/ 0.218%/ 0.261% [II] duration of dots: min/avg/max = 33451/ 33497/ 33567, expected = 33333, divergence min/avg/max = 0.354%/ 0.492%/ 0.702% ALSA sound system, 36 WPM [II] duration of dashes: min/avg/max = 93665/ 98788/106536, expected = 99999, divergence min/avg/max = -6.334%/ -1.211%/ 6.537% [II] duration of dots: min/avg/max = 28537/ 32776/ 41330, expected = 33333, divergence min/avg/max = -14.388%/ -1.671%/ 23.991% PulseAudio sound system, 36 WPM [II] duration of dashes: min/avg/max = 97477/100152/105279, expected = 99999, divergence min/avg/max = -2.522%/ 0.153%/ 5.280% [II] duration of dots: min/avg/max = 27914/ 33103/ 35747, expected = 33333, divergence min/avg/max = -16.257%/ -0.690%/ 7.242% Null sound system, 60WMP [II] duration of dashes: min/avg/max = 60143/ 60206/ 60256, expected = 60000, divergence min/avg/max = 0.238%/ 0.343%/ 0.427% [II] duration of dots: min/avg/max = 20126/ 20178/ 20229, expected = 20000, divergence min/avg/max = 0.630%/ 0.890%/ 1.145% ALSA sound system, 60WMP [II] duration of dashes: min/avg/max = 52534/ 56223/ 57938, expected = 60000, divergence min/avg/max = -12.443%/ -6.295%/ -3.437% [II] duration of dots: min/avg/max = 12107/ 16164/ 16757, expected = 20000, divergence min/avg/max = -39.465%/ -19.180%/ -16.215% PulseAudio sound system, 60WMP [II] duration of dashes: min/avg/max = 57549/ 60050/ 65158, expected = 60000, divergence min/avg/max = -4.085%/ 0.083%/ 8.597% [II] duration of dots: min/avg/max = 15114/ 19959/ 25528, expected = 20000, divergence min/avg/max = -24.430%/ -0.205%/ 27.640% */ static test_data_t g_test_data[] = { { .sound_system = CW_AUDIO_NULL, .speed = 4, .reference_div_dots = { 0.041, 0.068, 0.075 }, .reference_div_dashes = { 0.014, 0.023, 0.027 }}, { .sound_system = CW_AUDIO_NULL, .speed = 12, .reference_div_dots = { 0.113, 0.189, 0.241 }, .reference_div_dashes = { 0.034, 0.064, 0.079 }}, { .sound_system = CW_AUDIO_NULL, .speed = 24, .reference_div_dots = { 0.220, 0.316, 0.444 }, .reference_div_dashes = { 0.077, 0.131, 0.170 }}, { .sound_system = CW_AUDIO_NULL, .speed = 36, .reference_div_dots = { 0.354, 0.492, 0.702 }, .reference_div_dashes = { 0.150, 0.218, 0.261 }}, { .sound_system = CW_AUDIO_NULL, .speed = 60, .reference_div_dots = { 0.630, 0.890, 1.145 }, .reference_div_dashes = { 0.238, 0.343, 0.427 }}, { .sound_system = CW_AUDIO_CONSOLE, .speed = 4, .reference_div_dots = { 0.041, 0.068, 0.075 }, .reference_div_dashes = { 0.014, 0.023, 0.027 }}, { .sound_system = CW_AUDIO_CONSOLE, .speed = 12, .reference_div_dots = { 0.113, 0.189, 0.241 }, .reference_div_dashes = { 0.034, 0.064, 0.079 }}, { .sound_system = CW_AUDIO_CONSOLE, .speed = 24, .reference_div_dots = { 0.220, 0.316, 0.444 }, .reference_div_dashes = { 0.077, 0.131, 0.170 }}, { .sound_system = CW_AUDIO_CONSOLE, .speed = 36, .reference_div_dots = { 0.354, 0.492, 0.702 }, .reference_div_dashes = { 0.150, 0.218, 0.261 }}, { .sound_system = CW_AUDIO_CONSOLE, .speed = 60, .reference_div_dots = { 0.630, 0.890, 1.145 }, .reference_div_dashes = { 0.238, 0.343, 0.427 }}, { .sound_system = CW_AUDIO_OSS, .speed = 4, .reference_div_dots = { -2.304, -1.263, -0.172 }, .reference_div_dashes = { -0.838, -0.475, -0.191 }}, { .sound_system = CW_AUDIO_OSS, .speed = 12, .reference_div_dots = { -6.692, -3.542, -1.060 }, .reference_div_dashes = { -2.413, -1.410, -0.301 }}, { .sound_system = CW_AUDIO_OSS, .speed = 24, .reference_div_dots = { -10.538, -5.278, -1.056 }, .reference_div_dashes = { -5.349, -1.480, 3.331 }}, { .sound_system = CW_AUDIO_OSS, .speed = 36, .reference_div_dots = { -14.388, -1.671, 23.991 }, .reference_div_dashes = { -6.334, -1.211, 6.537 }}, { .sound_system = CW_AUDIO_OSS, .speed = 60, .reference_div_dots = { -39.465, -19.180, -16.215 }, .reference_div_dashes = { -12.443, -6.295, -3.437 }}, { .sound_system = CW_AUDIO_ALSA, .speed = 4, .reference_div_dots = { -2.304, -1.263, -0.172 }, .reference_div_dashes = { -0.838, -0.475, -0.191 }}, { .sound_system = CW_AUDIO_ALSA, .speed = 12, .reference_div_dots = { -6.692, -3.542, -1.060 }, .reference_div_dashes = { -2.413, -1.410, -0.301 }}, { .sound_system = CW_AUDIO_ALSA, .speed = 24, .reference_div_dots = { -10.538, -5.278, -1.056 }, .reference_div_dashes = { -5.349, -1.480, 3.331 }}, { .sound_system = CW_AUDIO_ALSA, .speed = 36, .reference_div_dots = { -14.388, -1.671, 23.991 }, .reference_div_dashes = { -6.334, -1.211, 6.537 }}, { .sound_system = CW_AUDIO_ALSA, .speed = 60, .reference_div_dots = { -39.465, -19.180, -16.215 }, .reference_div_dashes = { -12.443, -6.295, -3.437 }}, { .sound_system = CW_AUDIO_PA, .speed = 4, .reference_div_dots = { -1.830, -0.156, 1.110 }, .reference_div_dashes = { -0.288, 0.022, 0.754 }}, { .sound_system = CW_AUDIO_PA, .speed = 12, .reference_div_dots = { -2.726, -0.063, 6.259 }, .reference_div_dashes = { -1.479, -0.041, 1.003 }}, { .sound_system = CW_AUDIO_PA, .speed = 24, .reference_div_dots = { -9.252, 0.004, 6.846 }, .reference_div_dashes = { -4.668, -0.306, 1.743 }}, { .sound_system = CW_AUDIO_PA, .speed = 36, .reference_div_dots = { -16.257, -0.690, 7.242 }, .reference_div_dashes = { -2.522, 0.153, 5.280 }}, { .sound_system = CW_AUDIO_PA, .speed = 60, .reference_div_dots = { -24.430, -0.205, 27.640 }, .reference_div_dashes = { -4.085, 0.083, 8.597 }}, }; static cw_element_t g_test_input_elements[INPUT_ELEMENTS_COUNT] = { /* "o" 1 */ { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* TODO: this should be EOE + EOC, right? */ /* "o" 2 */ { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "o" 3 */ { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "o" 4 */ { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "o" 5 */ { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "o" 6 */ { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "o" 7 */ { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "o" 8 */ { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "o" 9 */ { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DASH_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "s" 1 */ { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "s" 2 */ { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "s" 3 */ { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "s" 4 */ { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "s" 5 */ { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "s" 6 */ { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "s" 7 */ { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "s" 8 */ { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, /* "s" 9 */ { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, { CW_DOT_REPRESENTATION, 0 }, { CW_EOE_REPRESENTATION, 0 }, }; static void gen_callback_fn(void * callback_arg, int state) { callback_data_t * callback_data = (callback_data_t *) callback_arg; struct timeval now_timestamp = { 0 }; gettimeofday(&now_timestamp, NULL); if (callback_data->counter > 0) { /* Don't do anything for zero-th element, for which there is no 'prev timestamp'. */ const int diff = cw_timestamp_compare_internal(&callback_data->prev_timestamp, &now_timestamp); #if 1 fprintf(stderr, "[II] Call %3d, state %d, representation = '%c', duration of previous element = %6d us\n", callback_data->counter, state, g_test_input_elements[callback_data->counter].representation, diff); #endif /* Notice that we do -1 here. We are at the beginning of new element, and currently calculated diff is how long *previous* element was. */ g_test_input_elements[callback_data->counter - 1].duration = diff; if (state) { if (CW_DOT_REPRESENTATION != g_test_input_elements[callback_data->counter].representation && CW_DASH_REPRESENTATION != g_test_input_elements[callback_data->counter].representation) { fprintf(stderr, "[EE] Unexpected representation '%c' at %d for state 'closed'\n", g_test_input_elements[callback_data->counter].representation, callback_data->counter); } } else { if (CW_EOE_REPRESENTATION != g_test_input_elements[callback_data->counter].representation) { fprintf(stderr, "[EE] Unexpected representation '%c' at %d for state 'open'\n", g_test_input_elements[callback_data->counter].representation, callback_data->counter); } } } callback_data->counter++; callback_data->prev_timestamp = now_timestamp; } static void update_element_stats(cw_element_stats_t * stats, int element_duration) { stats->duration_total += element_duration; stats->count++; stats->duration_avg = stats->duration_total / stats->count; if (element_duration > stats->duration_max) { stats->duration_max = element_duration; } if (element_duration < stats->duration_min) { stats->duration_min = element_duration; } } static void calculate_divergences_from_stats(const cw_element_stats_t * stats, divergence_t * divergences, int duration_expected) { divergences->min = 100 * (stats->duration_min - duration_expected) / (1.0 * duration_expected); divergences->avg = 100 * (stats->duration_avg - duration_expected) / (1.0 * duration_expected); divergences->max = 100 * (stats->duration_max - duration_expected) / (1.0 * duration_expected); } static void print_element_stats_and_divergences(const cw_element_stats_t * stats, const divergence_t * divergences, const char * name, int duration_expected) { fprintf(stderr, "[II] duration of %7s: min/avg/max = %6d/%6d/%6d, expected = %6d, divergence min/avg/max = %8.3f%%/%8.3f%%/%8.3f%%\n", name, stats->duration_min, stats->duration_avg, stats->duration_max, duration_expected, (double) divergences->min, (double) divergences->avg, (double) divergences->max); } /* Top-level test function. */ cwt_retv test_cw_gen_state_callback(cw_test_executor_t * cte) { cte->print_test_header(cte, "%s", __func__); cwt_retv retv = cwt_retv_ok; const size_t n_tests = sizeof (g_test_data) / sizeof (g_test_data[0]); for (size_t i = 0; i < n_tests; i++) { test_data_t * test_data = &g_test_data[i]; if (CW_AUDIO_NONE == test_data->sound_system) { continue; } if (cte->current_gen_conf.sound_system != test_data->sound_system) { continue; } if (cwt_retv_ok != test_cw_gen_state_callback_sub(cte, test_data, cte->current_gen_conf.sound_device)) { retv = cwt_retv_err; break; } } cte->print_test_footer(cte, __func__); return retv; } static cwt_retv test_cw_gen_state_callback_sub(cw_test_executor_t * cte, test_data_t * test_data, const char * sound_device) { cw_gen_config_t gen_conf = { .sound_system = test_data->sound_system }; snprintf(gen_conf.sound_device, sizeof (gen_conf.sound_device), "%s", sound_device); cw_gen_t * gen = cw_gen_new(&gen_conf); cw_gen_set_speed(gen, test_data->speed); cw_gen_set_frequency(gen, cte->config->frequency); callback_data_t callback_data = { 0 }; cw_gen_register_value_tracking_callback_internal(gen, gen_callback_fn, &callback_data); int dot_usecs, dash_usecs, end_of_element_usecs, ics_usecs, end_of_word_usecs, additional_usecs, adjustment_usecs; cw_gen_get_timing_parameters_internal(gen, &dot_usecs, &dash_usecs, &end_of_element_usecs, &ics_usecs, &end_of_word_usecs, &additional_usecs, &adjustment_usecs); fprintf(stderr, "[II] dot duration = %6d us\n" "[II] dash duration = %6d us\n" "[II] eoe duration = %6d us\n" "[II] ics duration = %6d us\n" "[II] iws duration = %6d us\n" "[II] additional duration = %6d us\n" "[II] adjustment duration = %6d us\n", dot_usecs, dash_usecs, end_of_element_usecs, ics_usecs, end_of_word_usecs, additional_usecs, adjustment_usecs); fprintf(stderr, "[II] speed = %d WPM\n", test_data->speed); cw_gen_start(gen); cw_gen_enqueue_string(gen, input_string); cw_gen_wait_for_queue_level(gen, 0); cw_gen_stop(gen); cw_gen_delete(&gen); calculate_test_results(g_test_input_elements, test_data, dot_usecs, dash_usecs); evaluate_test_results(cte, test_data); clear_data(g_test_input_elements); return 0; } /** Calculate current divergences (from current run of test) that will be compared with reference values */ static void calculate_test_results(const cw_element_t * test_input_elements, test_data_t * test_data, int dot_usecs, int dash_usecs) { cw_element_stats_t stats_dot = { .duration_min = 1000000000, .duration_avg = 0, .duration_max = 0, .duration_total = 0, .count = 0 }; cw_element_stats_t stats_dash = { .duration_min = 1000000000, .duration_avg = 0, .duration_max = 0, .duration_total = 0, .count = 0 }; /* Skip first two elements and a last element. The way the test is structured may impact correctness of values of these elements. TODO: make the elements correct. */ for (int i = 2; i < INPUT_ELEMENTS_COUNT - 1; i++) { switch (test_input_elements[i].representation) { case CW_DOT_REPRESENTATION: update_element_stats(&stats_dot, test_input_elements[i].duration); break; case CW_DASH_REPRESENTATION: update_element_stats(&stats_dash, test_input_elements[i].duration); break; case CW_EOE_REPRESENTATION: /* TODO: implement. */ break; default: break; } } calculate_divergences_from_stats(&stats_dot, &test_data->current_div_dots, dot_usecs); calculate_divergences_from_stats(&stats_dash, &test_data->current_div_dashes, dash_usecs); print_element_stats_and_divergences(&stats_dot, &test_data->current_div_dots, "dots", dot_usecs); print_element_stats_and_divergences(&stats_dash, &test_data->current_div_dashes, "dashes", dash_usecs); return; } /** Compare test results from current test run with reference data. Update test results in @p cte. */ static void evaluate_test_results(cw_test_executor_t * cte, test_data_t * test_data) { /* Margin above 1.0: allow current results to be slightly worse than reference. Margin below 1.0: accept current results only if they are better than reference. */ const float margin = 1.5F; { const float expected_div = fabsf(test_data->reference_div_dots.min) * margin; const float current_div = fabsf(test_data->current_div_dots.min); cte->expect_op_float(cte, expected_div, ">", current_div, "divergence of dots, min"); } { const float expected_div = fabsf(test_data->reference_div_dots.avg) * margin; const float current_div = fabsf(test_data->current_div_dots.avg); cte->expect_op_float(cte, expected_div, ">", current_div, "divergence of dots, avg"); } { const float expected_div = fabsf(test_data->reference_div_dots.max) * margin; const float current_div = fabsf(test_data->current_div_dots.max); cte->expect_op_float(cte, expected_div, ">", current_div, "divergence of dots, max"); } { const float expected_div = fabsf(test_data->reference_div_dashes.min) * margin; const float current_div = fabsf(test_data->current_div_dashes.min); cte->expect_op_float(cte, expected_div, ">", current_div, "divergence of dashes, min"); } { const float expected_div = fabsf(test_data->reference_div_dashes.avg) * margin; const float current_div = fabsf(test_data->current_div_dashes.avg); cte->expect_op_float(cte, expected_div, ">", current_div, "divergence of dashes, avg"); } { const float expected_div = fabsf(test_data->reference_div_dashes.max) * margin; const float current_div = fabsf(test_data->current_div_dashes.max); cte->expect_op_float(cte, expected_div, ">", current_div, "divergence of dashes, max"); } /* TODO: the test should also have test for absolute value of divergence, not only for comparison with post_3.5.1 branch. The production code should aim at low absolute divergence, e.g. no higher than 3%. */ return; } /** Clear data accumulated in current test run. The function should be used as a preparation for next test run. */ static void clear_data(cw_element_t * test_input_elements) { /* Clear durations calculated in current test before next call of this test function. */ for (int i = 0; i < INPUT_ELEMENTS_COUNT; i++) { test_input_elements[i].duration = 0; } return; } unixcw-3.6.0/src/libcw/tests/test_sets.c0000644000175000017500000002670414000344554015200 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "libcw_utils_tests.h" #include "libcw_data_tests.h" #include "libcw_debug_tests.h" #include "libcw_tq_tests.h" #include "libcw_gen_tests.h" #include "libcw_key_tests.h" #include "libcw_rec_tests.h" #include "test_framework.h" #include "libcw_legacy_api_tests.h" #include "libcw_legacy_api_tests_rec_poll.h" #include "libcw_test_tq_short_space.h" #include "libcw_gen_tests_state_callback.h" #define WITH_LIBCW_LEGACY_API 1 cw_test_set_t cw_test_sets[] = { #if WITH_LIBCW_LEGACY_API { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_LEGACY, { LIBCW_TEST_TOPIC_TQ, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_cw_wait_for_tone, true), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_cw_wait_for_tone_queue, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_cw_queue_tone, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_empty_tone_queue, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_full_tone_queue, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_tone_queue_callback, false), LIBCW_TEST_FUNCTION_INSERT(NULL, false), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_LEGACY, { LIBCW_TEST_TOPIC_GEN, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_volume_functions, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_send_primitives, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_send_character_and_string, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_representations, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_basic_gen_operations, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_gen_remove_last_character, false), LIBCW_TEST_FUNCTION_INSERT(NULL, false), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_LEGACY, { LIBCW_TEST_TOPIC_KEY, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_iambic_key_dot, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_iambic_key_dash, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_iambic_key_alternating, false), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_iambic_key_none, true), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_straight_key, false), LIBCW_TEST_FUNCTION_INSERT(NULL, false), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_LEGACY, { LIBCW_TEST_TOPIC_OTHER, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_low_level_gen_parameters, true), LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_parameter_ranges, true), //LIBCW_TEST_FUNCTION_INSERT(legacy_api_cw_test_delayed_release, true), //LIBCW_TEST_FUNCTION_INSERT(legacy_api_cw_test_signal_handling, true), /* FIXME - not sure why this test fails :( */ LIBCW_TEST_FUNCTION_INSERT(NULL, true), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_LEGACY, { LIBCW_TEST_TOPIC_OTHER, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { /* This test does its own generator setup and deconfig. */ LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_tq_short_space, false), LIBCW_TEST_FUNCTION_INSERT(NULL, true), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_LEGACY, { LIBCW_TEST_TOPIC_REC, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { /* This test does its own generator setup and deconfig. */ LIBCW_TEST_FUNCTION_INSERT(legacy_api_test_rec_poll, false), LIBCW_TEST_FUNCTION_INSERT(NULL, true), } }, #endif /* #if WITH_LIBCW_LEGACY_API */ { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_MODERN, { LIBCW_TEST_TOPIC_OTHER, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { /* cw_utils topic */ LIBCW_TEST_FUNCTION_INSERT(test_cw_timestamp_compare_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_timestamp_validate_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_usecs_to_timespec_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_version_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_license_internal, true), /* cw_debug topic */ LIBCW_TEST_FUNCTION_INSERT(test_cw_debug_flags_internal, true), LIBCW_TEST_FUNCTION_INSERT(NULL, true), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_MODERN, { LIBCW_TEST_TOPIC_DATA, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { /* cw_data topic */ LIBCW_TEST_FUNCTION_INSERT(test_cw_representation_to_hash_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_representation_to_character_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_representation_to_character_internal_speed_gain, true), LIBCW_TEST_FUNCTION_INSERT(test_data_main_table_get_count, true), LIBCW_TEST_FUNCTION_INSERT(test_data_main_table_get_contents, true), LIBCW_TEST_FUNCTION_INSERT(test_data_main_table_get_representation_len_max, true), LIBCW_TEST_FUNCTION_INSERT(test_data_main_table_lookups, true), LIBCW_TEST_FUNCTION_INSERT(test_prosign_lookups_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_phonetic_lookups_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_validate_character_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_validate_string_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_validate_representation_internal, true), LIBCW_TEST_FUNCTION_INSERT(NULL, true), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_MODERN, { LIBCW_TEST_TOPIC_TQ, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. All sound systems are included in tests of tq, because sometimes a running gen is necessary. */ { LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_test_capacity_A, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_test_capacity_B, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_wait_for_level_internal, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_is_full_internal_while_enqueueing, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_is_full_internal_while_dequeueing, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_enqueue_dequeue_internal, true), #if 0 LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_enqueue_args_internal, true), #endif LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_enqueue_internal_tone_validity, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_new_delete_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_capacity_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_length_internal_1, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_prev_index_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_next_index_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_callback, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_gen_operations_A, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_gen_operations_B, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_properties_empty, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_properties_full, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_tq_dequeue_internal_returns, true), LIBCW_TEST_FUNCTION_INSERT(NULL, true), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_MODERN, { LIBCW_TEST_TOPIC_GEN, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_new_delete, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_new_start_delete, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_new_stop_delete, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_new_start_stop_delete, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_set_tone_slope, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_tone_slope_shape_enums, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_get_timing_parameters_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_parameter_getters_setters, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_volume_functions, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_enqueue_primitives, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_enqueue_representations, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_enqueue_character, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_enqueue_string, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_remove_last_character, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_forever_internal, false), LIBCW_TEST_FUNCTION_INSERT(test_cw_gen_state_callback, false), LIBCW_TEST_FUNCTION_INSERT(NULL, true), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_MODERN, { LIBCW_TEST_TOPIC_KEY, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { LIBCW_TEST_FUNCTION_INSERT(test_keyer, false), LIBCW_TEST_FUNCTION_INSERT(test_straight_key, false), LIBCW_TEST_FUNCTION_INSERT(NULL, true), } }, { LIBCW_TEST_SET_VALID, LIBCW_TEST_API_MODERN, { LIBCW_TEST_TOPIC_REC, LIBCW_TEST_TOPIC_MAX }, /* Topics. */ { CW_AUDIO_NULL, CW_AUDIO_CONSOLE, CW_AUDIO_OSS, CW_AUDIO_ALSA, CW_AUDIO_PA, CW_AUDIO_NONE /* Guard. */ }, /* Sound systems. */ { LIBCW_TEST_FUNCTION_INSERT(test_cw_rec_get_receive_parameters, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_rec_parameter_getters_setters_1, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_rec_parameter_getters_setters_2, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_rec_identify_mark_internal, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_rec_test_with_constant_speeds, true), LIBCW_TEST_FUNCTION_INSERT(test_cw_rec_test_with_varying_speeds, true), LIBCW_TEST_FUNCTION_INSERT(NULL, true) /* Guard. */ } }, /* Guard. */ { LIBCW_TEST_SET_INVALID, LIBCW_TEST_API_MODERN, /* This field doesn't matter here, test set is invalid. */ { LIBCW_TEST_TOPIC_MAX }, { CW_AUDIO_NONE /* Guard. */ }, { LIBCW_TEST_FUNCTION_INSERT(NULL, true) } } }; unixcw-3.6.0/src/libcw/tests/libcw_gen_tests.h0000644000175000017500000000227114000344554016334 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_GEN_TESTS_H_ #define _LIBCW_GEN_TESTS_H_ #include "test_framework.h" int test_cw_gen_new_delete(cw_test_executor_t * cte); int test_cw_gen_new_start_delete(cw_test_executor_t * cte); int test_cw_gen_new_stop_delete(cw_test_executor_t * cte); int test_cw_gen_new_start_stop_delete(cw_test_executor_t * cte); int test_cw_gen_set_tone_slope(cw_test_executor_t * cte); int test_cw_gen_tone_slope_shape_enums(cw_test_executor_t * cte); int test_cw_gen_forever_internal(cw_test_executor_t * cte); int test_cw_gen_get_timing_parameters_internal(cw_test_executor_t * cte); int test_cw_gen_parameter_getters_setters(cw_test_executor_t * cte); int test_cw_gen_volume_functions(cw_test_executor_t * cte); int test_cw_gen_enqueue_primitives(cw_test_executor_t * cte); int test_cw_gen_enqueue_representations(cw_test_executor_t * cte); int test_cw_gen_enqueue_character(cw_test_executor_t * cte); int test_cw_gen_enqueue_string(cw_test_executor_t * cte); cwt_retv test_cw_gen_remove_last_character(cw_test_executor_t * cte); #endif /* #ifndef _LIBCW_GEN_TESTS_H_ */ unixcw-3.6.0/src/libcw/tests/libcw_key_tests.c0000644000175000017500000002367714000344554016363 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include "libcw.h" #include "libcw2.h" #include "libcw_gen.h" #include "libcw_key.h" #include "libcw_key_tests.h" #include "libcw_debug.h" #include "libcw_utils.h" #include "test_framework.h" test_straight_key_data_t g_test_straight_key_data[TEST_STRAIGHT_KEY_DATA_COUNT] = { /* See what happens when we tell the library 'max' times in a row that key is open. */ { 0, 0, { CW_KEY_VALUE_OPEN, CW_KEY_VALUE_OPEN }, "consecutive open", NULL, NULL, NULL, NULL, NULL }, /* See what happens when we tell the library 'max' times in a row that key is closed. */ { 0, 0, { CW_KEY_VALUE_CLOSED, CW_KEY_VALUE_CLOSED }, "consecutive closed", NULL, NULL, NULL, NULL, NULL }, /* During development I noticed a bug that happened only if test was started from 'open' state. So test both possibilities: when starting from open and from closed. */ { 0, CW_USECS_PER_SEC, { CW_KEY_VALUE_OPEN, CW_KEY_VALUE_CLOSED }, "open/closed", NULL, NULL, NULL, NULL, NULL }, { 0, CW_USECS_PER_SEC, { CW_KEY_VALUE_CLOSED, CW_KEY_VALUE_OPEN }, "closed/open", NULL, NULL, NULL, NULL, NULL }, }; static int key_setup(cw_test_executor_t * cte, cw_key_t ** key, cw_gen_t ** gen); static void key_destroy(cw_key_t ** key, cw_gen_t ** gen); static int test_keyer_helper(cw_test_executor_t * cte, cw_key_t * key, cw_key_value_t intended_dot_paddle, cw_key_value_t intended_dash_paddle, char mark_representation, const char * marks_name, int max); /** @reviewed on 2019-10-12 */ static int key_setup(cw_test_executor_t * cte, cw_key_t ** key, cw_gen_t ** gen) { *key = cw_key_new(); if (!*key) { cte->log_error(cte, "Can't create key, stopping the test\n"); return -1; } *gen = cw_gen_new(&cte->current_gen_conf); if (!*gen) { cte->log_error(cte, "Can't create gen, stopping the test\n"); return -1; } if (CW_SUCCESS != cw_gen_start(*gen)) { cte->log_error(cte, "Can't start generator, stopping the test\n"); cw_gen_delete(gen); cw_key_delete(key); return -1; } cw_key_register_generator(*key, *gen); cw_gen_reset_parameters_internal(*gen); cw_gen_sync_parameters_internal(*gen); cw_gen_set_speed(*gen, 30); return 0; } /** @reviewed on 2019-10-12 */ void key_destroy(cw_key_t ** key, cw_gen_t ** gen) { if (NULL != key) { if (NULL != *key) { cw_key_delete(key); } } if (NULL != gen) { if (NULL != *gen) { cw_gen_delete(gen); } } } /** @reviewed on 2019-10-12 */ int test_keyer_helper(cw_test_executor_t * cte, cw_key_t * key, cw_key_value_t intended_dot_paddle, cw_key_value_t intended_dash_paddle, char mark_representation, const char * marks_name, int max) { /* Test: keying dot. */ { /* Seems like this function calls means "keyer pressed until further notice". First argument is true, so this is a dot. */ cw_ret_t cwret = LIBCW_TEST_FUT(cw_key_ik_notify_paddle_event)(key, intended_dot_paddle, intended_dash_paddle); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_key_ik_notify_paddle_event(key, %d, %d)", intended_dot_paddle, intended_dash_paddle); bool failure = false; /* Since a X paddle is pressed, get "max" X marks from the keyer. Notice that they aren't enqueued - we won't run out of marks. Iambic keyer can produce them indefinitely, as long as a paddle is pressed. We just want to get N marks. */ cte->log_info(cte, "%s: ", marks_name); for (int i = 0; i < max; i++) { cwret = LIBCW_TEST_FUT(cw_key_ik_wait_for_end_of_current_element)(key); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "wait for iambic key element (%s), #%d", marks_name, i)) { failure = true; break; } cte->log_info_cont(cte, "%c", mark_representation); } cte->log_info_cont(cte, "\n"); cte->expect_op_int(cte, false, "==", failure, "wait for iambic key elements (%s)", marks_name); } /* Test: preserving of paddle states. */ { cw_key_value_t readback_dot_paddle; cw_key_value_t readback_dash_paddle; LIBCW_TEST_FUT(cw_key_ik_get_paddles)(key, &readback_dot_paddle, &readback_dash_paddle); cte->expect_op_int(cte, intended_dot_paddle, "==", readback_dot_paddle, "cw_keyer_get_keyer_paddles(): preserving dot paddle (%s)", marks_name); cte->expect_op_int(cte, intended_dash_paddle, "==", readback_dash_paddle, "cw_keyer_get_keyer_paddles(): preserving dash paddle (%s)", marks_name); } return 0; } /** @reviewed on 2019-10-27 */ int test_keyer(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); cw_key_t * key = NULL; cw_gen_t * gen = NULL; if (0 != key_setup(cte, &key, &gen)) { return -1; } /* Perform some tests on the iambic keyer. The latch finer timing points are not tested here, just the basics - dots, dashes, and alternating dots and dashes. */ /* Test: keying dot. */ test_keyer_helper(cte, key, CW_KEY_VALUE_CLOSED, CW_KEY_VALUE_OPEN, CW_DOT_REPRESENTATION, "dots", loops); /* Test: keying dash. */ test_keyer_helper(cte, key, CW_KEY_VALUE_OPEN, CW_KEY_VALUE_CLOSED, CW_DASH_REPRESENTATION, "dashes", loops); /* Test: keying alternate dit/dash. */ test_keyer_helper(cte, key, CW_KEY_VALUE_CLOSED, CW_KEY_VALUE_CLOSED, '#', "alternating", loops); /* Test: set new state of paddles: no paddle pressed. */ { cw_ret_t cwret = LIBCW_TEST_FUT(cw_key_ik_notify_paddle_event)(key, CW_KEY_VALUE_OPEN, CW_KEY_VALUE_OPEN); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "cw_key_ik_notify_paddle_event(%d, %d)", CW_KEY_VALUE_OPEN, CW_KEY_VALUE_OPEN); } cw_key_ik_wait_for_keyer(key); key_destroy(&key, &gen); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2020-10-08 */ cwt_retv test_helper_test_straight_key(cw_test_executor_t * cte, volatile cw_key_t * key, test_straight_key_data_t * test_data) { bool event_failure = false; bool state_failure = false; bool busy_failure = false; for (int i = 0; i < test_data->loops; i++) { const cw_key_value_t intended_key_value = test_data->values_set[i % 2]; cw_ret_t cwret = CW_FAILURE; if (test_data->modern_set) { cwret = test_data->modern_set(key, intended_key_value); } else { cwret = test_data->legacy_set(intended_key_value); } if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "%s: set key value %d", test_data->test_name, intended_key_value)) { event_failure = true; break; } cw_key_value_t readback_value; if (test_data->modern_get) { cwret = test_data->modern_get(key, &readback_value); } else { cwret = CW_SUCCESS; readback_value = test_data->legacy_get(); } if (!cte->expect_op_int_errors_only(cte, intended_key_value, "==", readback_value, "%s: get key value %d", test_data->test_name, intended_key_value)) { state_failure = true; break; } /* "busy" is misleading. This function just asks if key is down. */ if (test_data->legacy_is_busy) { const bool is_busy = test_data->legacy_is_busy(); const bool expected_is_busy = intended_key_value == CW_KEY_VALUE_CLOSED; if (!cte->expect_op_int_errors_only(cte, expected_is_busy, "==", is_busy, "%s: is sk busy", test_data->test_name)) { busy_failure = true; break; } } cte->log_info_cont(cte, "%d", intended_key_value); if (test_data->usecs) { #ifdef __FreeBSD__ /* There is a problem with nanosleep() and signals on FreeBSD. TODO: see if the problem still persists after moving from signals to conditional variables. */ sleep(1); #else cw_usleep_internal(test_data->usecs); #endif } } cte->log_info_cont(cte, "\n"); /* Never leave the key closed. */ cw_key_sk_set_value(key, CW_KEY_VALUE_OPEN); cte->expect_op_int(cte, false, "==", event_failure, "set sk state(%s)", test_data->test_name); cte->expect_op_int(cte, false, "==", state_failure, "get sk state(%s)", test_data->test_name); if (test_data->legacy_is_busy) { cte->expect_op_int(cte, false, "==", busy_failure, "is sk busy(%s)", test_data->test_name); } return cwt_retv_ok; } /** @reviewed on 2020-10-08 */ int test_straight_key(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); cw_key_t * key = NULL; cw_gen_t * gen = NULL; if (0 != key_setup(cte, &key, &gen)) { return -1; } for (size_t i = 0; i < TEST_STRAIGHT_KEY_DATA_COUNT; i++) { g_test_straight_key_data[i].loops = loops; g_test_straight_key_data[i].legacy_set = NULL; g_test_straight_key_data[i].legacy_get = NULL; g_test_straight_key_data[i].legacy_is_busy = NULL; g_test_straight_key_data[i].modern_set = LIBCW_TEST_FUT(cw_key_sk_set_value); g_test_straight_key_data[i].modern_get = LIBCW_TEST_FUT(cw_key_sk_get_value); test_helper_test_straight_key(cte, key, &g_test_straight_key_data[i]); } /* Don't go immediately to key_destroy(), because this will cut the sound of the last dot short. TODO: shouldn't this be some kind of wait()? */ sleep(1); key_destroy(&key, &gen); cte->print_test_footer(cte, __func__); return 0; } unixcw-3.6.0/src/libcw/tests/test_main.c0000644000175000017500000001130514000344554015135 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #if defined(HAVE_STRING_H) # include #endif #if defined(HAVE_STRINGS_H) # include #endif #include #include #include #include #include #include #include "libcw2.h" #include "libcw_debug.h" #include "test_framework.h" extern cw_debug_t cw_debug_object; extern cw_debug_t cw_debug_object_dev; /* Depending on which object file are we linked with, this will be a set of legacy API tests or set of all API/modern API/other stuff. */ extern cw_test_set_t cw_test_sets[]; static void cw_test_print_stats_wrapper(void); static void signal_handler(int signal_number); static void register_signal_handler(void); static cw_test_executor_t g_tests_executor; static void deinit_executor(void) { cw_test_deinit(&g_tests_executor); } /** \return EXIT_SUCCESS if all tests complete successfully, \return EXIT_FAILURE otherwise */ int main(int argc, char * const argv[]) { fprintf(stderr, "%s\n\n", argv[0]); if (1) { /* This should be a default debug config for most of the time (unless a specific feature is being debugged): show all warnings an errors. */ cw_debug_set_flags(&cw_debug_object, CW_DEBUG_MASK); cw_debug_object.level = CW_DEBUG_WARNING; cw_debug_set_flags(&cw_debug_object_dev, CW_DEBUG_MASK); cw_debug_object_dev.level = CW_DEBUG_WARNING; } cw_test_executor_t * cte = &g_tests_executor; cw_test_init(cte, stdout, stderr, "libcw/tests"); atexit(deinit_executor); cte->config->has_feature_sound_system = true; cte->config->has_feature_generator = true; cte->config->has_feature_libcw_test_specific = true; cte->config->has_feature_test_loops = true; cte->config->has_feature_test_name = true; cte->config->has_feature_test_quick_only = true; cte->config->has_feature_test_random_seed = true; cte->config->test_loops = 5; /* May cause exit on errors or "-h" option. */ if (cwt_retv_ok != cte->process_args(cte, argc, argv)) { exit(EXIT_FAILURE); } cte->print_test_options(cte); /* Let the test options be clearly visible for few seconds before screen is filled with testcase debugs. */ sleep(3); atexit(cw_test_print_stats_wrapper); register_signal_handler(); if (cwt_retv_ok != cte->main_test_loop(cte, cw_test_sets)) { cte->log_error(cte, "Test loop returned with error\n"); exit(EXIT_FAILURE); } const unsigned int errors_count = cte->get_total_errors_count(cte); if (0 != errors_count) { cte->log_error(cte, "Non-zero errors count: %u\n", errors_count); exit(EXIT_FAILURE); } cte->log_info(cte, "Total errors count: %u\n", errors_count); /* "make check" facility requires this message to be printed on stdout; don't localize it */ cte->log_info(cte, "Test result: success\n\n"); exit(EXIT_SUCCESS); } /* Show the signal caught, and exit. */ void signal_handler(int signal_number) { g_tests_executor.log_info(&g_tests_executor, "Caught signal %d, exiting...\n", signal_number); exit(EXIT_SUCCESS); } void register_signal_handler(void) { /* Set up signal handler to exit on a range of signals. */ const int SIGNALS[] = { SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGTERM, 0 }; for (int i = 0; SIGNALS[i]; i++) { struct sigaction action; memset(&action, 0, sizeof(action)); action.sa_handler = signal_handler; action.sa_flags = 0; int rv = sigaction(SIGNALS[i], &action, (struct sigaction *) NULL); if (rv == -1) { g_tests_executor.log_error(&g_tests_executor, "Can't register signal %d: '%s'\n", SIGNALS[i], strerror(errno)); exit(EXIT_FAILURE); } } return; } /** \brief Print statistics of tests Wrapper around call to method of global object. This function should be passed to atexit() to print stats before program exits (whether in normal way or during some error). */ void cw_test_print_stats_wrapper(void) { g_tests_executor.print_test_stats(&g_tests_executor); } unixcw-3.6.0/src/libcw/tests/libcw_test_tq_short_space.c0000644000175000017500000001301014000344554020402 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include /* atoi() */ #include /* sleep() */ #include #include #include "libcw_debug.h" #include "test_framework.h" #include "libcw_test_tq_short_space.h" /* This test checks presence of a specific bug in tone queue. The bug occurs when the application has registered low-tone-queue callback, the threshold for the callback is set to 1, and a single inter-word-space has been enqueued by client application. When the inter-word-space is enqueued as a single tone-queue tone (or even as two tones) ("short space"), libcw may miss the event of passing of tone queue level from 2 to 1 and will not call the callback. This "miss" is probably caused by the fact that the first tone is deqeued and played before the second one gets enqueued. The solution in libcw is to enqueue inter-word-space as more than two tones (three tones seem to be ok). The bug sits at the border between tone queue and generator, but since it's related to how tones are enqueued and dequeued, then I'm treating the bug as related to tone queue. */ struct tq_short_space_data { cw_test_executor_t * cte; int cw_speed; /* This is how many actual calls to callback have been made. We expect this value to be equal to n_expected_callback_executions that you will find below. */ int n_actual_callback_executions; }; static bool single_test_over_speed_range(struct tq_short_space_data * data, int i, int n); static void tone_queue_low_callback(void * arg); /* Callback to be called when tone queue level passes this mark. */ static const int tq_low_watermark = 1; /** @reviewed on 2019-10-16 */ int legacy_api_test_tq_short_space(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); struct tq_short_space_data data = { 0 }; //memset(&data, 0, sizeof (data)); data.cte = cte; bool success = true;; for (int i = 0; i < loops; i++) { cte->log_info(cte, "Testing dequeuing short space, iteration #%d / %d\n", i + 1, loops); data.n_actual_callback_executions = 0; success = single_test_over_speed_range(&data, i, loops); if (!success) { break; } } cte->expect_op_int(cte, true, "==", success, "Testing dequeuing short space"); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-16 */ bool single_test_over_speed_range(struct tq_short_space_data * data, int i, int n) { /* Library initialization. */ cw_generator_new(data->cte->current_gen_conf.sound_system, data->cte->current_gen_conf.sound_device); cw_generator_start(); cw_register_tone_queue_low_callback(tone_queue_low_callback, data, tq_low_watermark); /* Let's test this for a full range of supported speeds (from min to max). MIN and MAX are even numbers, so delta == 2 is ok. */ const int speed_delta = 2; int n_iterations = 0; for (data->cw_speed = CW_SPEED_MIN; data->cw_speed <= CW_SPEED_MAX; data->cw_speed += speed_delta) { cw_set_send_speed(data->cw_speed); cw_set_volume(50); cw_set_frequency(200); data->cte->log_info(data->cte, "current send speed = %d WPM\n", data->cw_speed); /* Correct action for libcw upon sending a single space will be to enqueue few tones, and to call callback when tq_low_watermark is passed. In incorrect implementation of libcw's tone queue the event of passing tq_low_watermark threshold will be missed and callback won't be called. */ cw_send_character(' '); cw_wait_for_tone_queue(); usleep(300); n_iterations++; } /* Library cleanup. */ cw_generator_stop(); cw_generator_delete(); /* This is how many times we did a following test: send a single space and wait for queue to drain. */ const int n_expected_callback_executions = ((CW_SPEED_MAX - CW_SPEED_MIN) / speed_delta) + 1; data->cte->assert2(data->cte, n_expected_callback_executions == n_iterations, "Number of loop iterations does not meet expectations: %d vs. %d\n", n_expected_callback_executions, data->n_actual_callback_executions); const bool success = data->cte->expect_op_int_errors_only(data->cte, n_expected_callback_executions, "==", data->n_actual_callback_executions, "test execution %d out of %d", i + 1, n); return success; } /** @reviewed on 2019-10-16 */ void tone_queue_low_callback(void * arg) { struct tq_short_space_data * data = (struct tq_short_space_data *) arg; /* TODO: move the log to a loop looping over speeds, and add an expect there? */ data->cte->log_info(data->cte, "current send speed = %d WPM, callback has been called (as expected)\n", data->cw_speed); data->n_actual_callback_executions++; return; } unixcw-3.6.0/src/libcw/tests/test_data.c0000644000175000017500000000300414000344554015117 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include /* Various static pieces of data to be used in tests. */ const char * test_valid_representations[] = { ".-.-.-", ".-", "---", "...-", NULL }; /* Guard. */ const char * test_invalid_representations[] = { "INVALID", "_._T", "_.A_.", "S-_-", "_._", /* This does not represent a valid letter/digit. */ "-_-", /* This does not represent a valid letter/digit. */ NULL }; /* Guard. */ /* Strings that cannot be enqueued to libcw generator and sent/played because they are invalid in some way. */ const char * test_invalid_strings[] = { "%INVALID%", NULL }; /* Guard. */ unixcw-3.6.0/src/libcw/tests/libcw_tq_tests.c0000644000175000017500000015447114000344554016214 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include "libcw.h" #include "libcw2.h" #include "libcw_utils.h" #include "libcw_tq.h" #include "libcw_tq_internal.h" #include "libcw_tq_tests.h" #include "libcw_debug.h" #include "test_framework.h" #define MSG_PREFIX "libcw/tq: " static int test_cw_tq_enqueue_internal(cw_test_executor_t * cte, cw_tone_queue_t * tq); static int test_cw_tq_dequeue_internal(cw_test_executor_t * cte, cw_tone_queue_t * tq); static void gen_setup(cw_test_executor_t * cte, cw_gen_t ** gen); static void gen_destroy(cw_gen_t ** gen); static void enqueue_tone_low_level(cw_test_executor_t * cte, cw_tone_queue_t * tq, const cw_tone_t * tone); static cw_tone_queue_t * test_cw_tq_capacity_test_init(cw_test_executor_t * cte, size_t capacity, size_t high_water_mark, int head_shift); static void test_helper_tq_callback(void * data); static cwt_retv test_helper_fill_queue(cw_test_executor_t * cte, cw_tone_queue_t * tq, size_t count); static void gen_setup(cw_test_executor_t * cte, cw_gen_t ** gen) { *gen = cw_gen_new(&cte->current_gen_conf); if (!*gen) { cte->log_error(cte, "Can't create generator, stopping the test\n"); return; } cw_gen_reset_parameters_internal(*gen); cw_gen_sync_parameters_internal(*gen); cw_gen_set_speed(*gen, 30); cw_gen_set_volume(*gen, 70); return; } static void gen_destroy(cw_gen_t ** gen) { cw_gen_delete(gen); } /** @reviewed on 2019-10-03 */ int test_cw_tq_new_delete_internal(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); bool failure = false; cw_tone_queue_t * tq = NULL; for (int i = 0; i < loops; i++) { tq = LIBCW_TEST_FUT(cw_tq_new_internal)(); if (!cte->expect_valid_pointer_errors_only(cte, tq, "creating new tone queue")) { failure = true; break; } /* Try to access some fields in cw_tone_queue_t just to be sure that the tq has been allocated properly. Trying to read and write tq->head and tq->tail may seem silly, but I just want to dereference tq pointer and be sure that nothing crashes. */ { if (!cte->expect_op_int_errors_only(cte, 0, "==", tq->head, "trying to dereference tq (read ::head)")) { failure = true; break; } tq->tail = tq->head + 10; if (!cte->expect_op_int_errors_only(cte, 10, "==", tq->tail, "trying to dereference tq (read ::tail)")) { failure = true; break; } } LIBCW_TEST_FUT(cw_tq_delete_internal)(&tq); if (!cte->expect_null_pointer_errors_only(cte, tq, "deleting tone queue")) { failure = true; break; } } cte->expect_op_int(cte, false, "==", failure, "using tone queue's new/delete methods"); /* Cleanup after (possibly) failed tests. */ if (tq) { cw_tq_delete_internal(&tq); } cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-03 */ int test_cw_tq_capacity_internal(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); bool failure = false; cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, tq, "failed to create new tone queue"); for (int i = 0; i < loops; i++) { /* This is a silly test, but let's have any test of the getter. TODO: come up with better test. */ const size_t intended_capacity = (lrand48() % 4000) + 10; tq->capacity = intended_capacity; const size_t readback_capacity = LIBCW_TEST_FUT(cw_tq_capacity_internal)(tq); if (!cte->expect_op_int_errors_only(cte, intended_capacity, "==", readback_capacity, "getting tone queue capacity")) { failure = true; break; } } cw_tq_delete_internal(&tq); cte->expect_op_int(cte, false, "==", failure, "getting tone queue capacity"); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-03 */ int test_cw_tq_prev_index_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, NULL != tq, "failed to create new tone queue"); struct { size_t current_index; size_t expected_prev_index; bool guard; } input[] = { { tq->capacity - 4, tq->capacity - 5, false }, { tq->capacity - 3, tq->capacity - 4, false }, { tq->capacity - 2, tq->capacity - 3, false }, { tq->capacity - 1, tq->capacity - 2, false }, /* This one should never happen. We can't pass index equal "capacity" because it's out of range. */ /* { tq->capacity - 0, tq->capacity - 1, false }, */ { 0, tq->capacity - 1, false }, { 1, 0, false }, { 2, 1, false }, { 3, 2, false }, { 4, 3, false }, { 0, 0, true } /* guard */ }; int i = 0; bool failure = false; while (!input[i].guard) { const size_t readback_prev_index = LIBCW_TEST_FUT(cw_tq_prev_index_internal)(tq, input[i].current_index); if (!cte->expect_op_int_errors_only(cte, input[i].expected_prev_index, "==", readback_prev_index, "calculating 'prev' index, test %d", i)) { failure = true; break; } i++; } cw_tq_delete_internal(&tq); cte->expect_op_int(cte, false, "==", failure, "calculating 'prev' index"); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-03 */ int test_cw_tq_next_index_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, NULL != tq, "failed to create new tone queue"); struct { size_t current_index; size_t expected_next_index; bool guard; } input[] = { { tq->capacity - 5, tq->capacity - 4, false }, { tq->capacity - 4, tq->capacity - 3, false }, { tq->capacity - 3, tq->capacity - 2, false }, { tq->capacity - 2, tq->capacity - 1, false }, { tq->capacity - 1, 0, false }, { 0, 1, false }, { 1, 2, false }, { 2, 3, false }, { 3, 4, false }, { 0, 0, true } /* guard */ }; int i = 0; bool failure = false; while (!input[i].guard) { const size_t readback_next_index = LIBCW_TEST_FUT(cw_tq_next_index_internal)(tq, input[i].current_index); if (!cte->expect_op_int_errors_only(cte, input[i].expected_next_index, "==", readback_next_index, "calculating 'next' index, test %d", i)) { failure = true; break; } i++; } cw_tq_delete_internal(&tq); cte->expect_op_int(cte, false, "==", failure, "calculating 'next' index"); cte->print_test_footer(cte, __func__); return 0; } /** Helper function, wrapper for some low-level operations. @reviewed on 2019-10-04 */ void enqueue_tone_low_level(cw_test_executor_t * cte, cw_tone_queue_t * tq, const cw_tone_t * tone) { /* This is just some code copied from implementation of 'enqueue' function. I don't use 'enqueue' function itself because it's not tested yet. I get rid of all the other code from the 'enqueue' function and use only the essential part to manually add elements to list, and then to check length of the list. */ /* This block of code pretends to be enqueue function. The most important functionality of enqueue function is done here manually. We don't do any checks of boundaries of tq, we trust that this is enforced by for loop's conditions. */ /* Notice that this is *before* enqueueing the tone. */ cte->assert2(cte, tq->len < tq->capacity, "length before enqueue reached capacity: %zu / %zu", tq->len, tq->capacity); /* Enqueue the new tone and set the new tail index. */ tq->queue[tq->tail] = *tone; tq->tail = cw_tq_next_index_internal(tq, tq->tail); tq->len++; cte->assert2(cte, tq->len <= tq->capacity, "length after enqueue exceeded capacity: %zu / %zu", tq->len, tq->capacity); } /** The second function is just a wrapper for the first one, so this test case tests both functions at once. @reviewed on 2019-10-04 */ int test_cw_tq_length_internal_1(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, tq, "failed to create new tone queue"); cw_tone_t tone; CW_TONE_INIT(&tone, 1, 1, CW_SLOPE_MODE_NO_SLOPES); bool failure = false; for (size_t i = 0; i < tq->capacity; i++) { enqueue_tone_low_level(cte, tq, &tone); /* OK, added a tone, ready to measure length of the queue. */ const size_t expected_len = i + 1; const size_t readback_len = LIBCW_TEST_FUT(cw_tq_length_internal)(tq); if (!cte->expect_op_int_errors_only(cte, expected_len, "==", readback_len, "tone queue length A, readback #1")) { failure = true; break; } if (!cte->expect_op_int_errors_only(cte, tq->len, "==", readback_len, "tone queue length A, readback #2")) { failure = true; break; } } cw_tq_delete_internal(&tq); cte->expect_op_int(cte, false, "==", failure, "tone queue length A"); cte->print_test_footer(cte, __func__); return 0; } /** \brief Wrapper for tests of enqueue() and dequeue() function First we fill a tone queue when testing enqueue(), and then use the filled tone queue to test dequeue(). @reviewed 2020-10-03 */ int test_cw_tq_enqueue_dequeue_internal(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, tq, "failed to create new tone queue"); for (int i = 0; i < loops; i++) { /* Fill the tone queue with tones. */ test_cw_tq_enqueue_internal(cte, tq); /* Use the same (now filled) tone queue to test dequeue() function. */ test_cw_tq_dequeue_internal(cte, tq); } cw_tq_delete_internal(&tq); cte->print_test_footer(cte, __func__); return 0; } /** @brief Test enqueueing operation @reviewed 2020-10-02 */ cwt_retv test_cw_tq_enqueue_internal(cw_test_executor_t * cte, cw_tone_queue_t * tq) { /* At this point cw_tq_length_internal() should be tested, so we can use it to verify correctness of 'enqueue' function. */ cw_tone_t tone; CW_TONE_INIT(&tone, 1, 1, CW_SLOPE_MODE_NO_SLOPES); bool enqueue_failure = false; bool length_failure = false; for (size_t i = 0; i < tq->capacity; i++) { /* This tests for potential problems with function call. */ const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "%s:%d: enqueueing tone", __func__, __LINE__)) { enqueue_failure = true; break; } /* This tests for correctness of working of the 'enqueue' function and of keeping track of tone queue length. */ const size_t expected_len = i + 1; const size_t readback_len = cw_tq_length_internal(tq); if (!cte->expect_op_int_errors_only(cte, expected_len, "==", readback_len, "%s:%d:readback #1", __func__, __LINE__)) { length_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, tq->len, "==", readback_len, "%s:%d: readback #2", __func__, __LINE__)) { length_failure = true; break; } } cte->expect_op_int(cte, false, "==", enqueue_failure, "%s:%d: enqueueing", __func__, __LINE__); cte->expect_op_int(cte, false, "==", length_failure, "%s:%d: tone queue length", __func__, __LINE__); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @reviewed on 2019-10-04 */ int test_cw_tq_dequeue_internal(cw_test_executor_t * cte, cw_tone_queue_t * tq) { /* tq should be completely filled after tests of enqueue() function. */ /* Test some assertions about full tq, just to be sure. */ cte->assert2(cte, tq->capacity == tq->len, "dequeue: capacity != len of full queue: %zu != %zu", tq->capacity, tq->len); cw_tone_t tone; CW_TONE_INIT(&tone, 1, 1, CW_SLOPE_MODE_NO_SLOPES); bool dequeue_failure = false; bool length_failure = false; for (size_t i = tq->capacity; i > 0; i--) { size_t expected_len; size_t readback_len; expected_len = i; readback_len = tq->len; /* Length of tone queue before dequeue. */ if (!cte->expect_op_int_errors_only(cte, expected_len, "==", readback_len, "dequeue: length before dequeueing tone #%zu", i)) { length_failure = true; break; } const cw_queue_state_t queue_state = LIBCW_TEST_FUT(cw_tq_dequeue_internal)(tq, &tone); if (1 == i) { /* Dequeueing last tone from the queue. */ if (!cte->expect_op_int_errors_only(cte, CW_TQ_JUST_EMPTIED, "==", queue_state, "dequeue: dequeueing tone #%zu", i)) { dequeue_failure = true; break; } } else { /* Dequeueing last tone from the queue. */ if (!cte->expect_op_int_errors_only(cte, CW_TQ_NONEMPTY, "==", queue_state, "dequeue: dequeueing tone #%zu", i)) { dequeue_failure = true; break; } } /* Length of tone queue after dequeue. */ expected_len = i - 1; readback_len = tq->len; if (!cte->expect_op_int_errors_only(cte, expected_len, "==", readback_len, "dequeue: length after dequeueing tone #%zu", i)) { length_failure = true; break; } } cte->expect_op_int(cte, false, "==", dequeue_failure, "dequeue: dequeueing tones"); cte->expect_op_int(cte, false, "==", length_failure, "dequeue: length of tq"); return 0; } /** @brief Test 'is full' function while enqueueing tones to it Remember that the function checks whether tq is full, not whether it is non-empty. @reviewed 2020-10-03 */ cwt_retv test_cw_tq_is_full_internal_while_enqueueing(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, tq, "failed to create new tq"); bool failure = false; cw_tone_t tone; CW_TONE_INIT(&tone, 1, 1, CW_SLOPE_MODE_NO_SLOPES); for (size_t i = 0; i < tq->capacity; i++) { /* The 'enqueue' function is tested elsewhere, but it won't hurt to check this simple condition here as well. */ const cw_ret_t cwret = cw_tq_enqueue_internal(tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "%s:%d: enqueuing tone #%zu", __func__, __LINE__, i)) { failure = true; break; } /* The last tone will make the queue full. */ const bool expected_value = i == (tq->capacity - 1); const bool is_full = LIBCW_TEST_FUT(cw_tq_is_full_internal)(tq); if (!cte->expect_op_int_errors_only(cte, expected_value, "==", is_full, "%s:%d: is tone queue full after enqueueing tone #%zu", __func__, __LINE__, i)) { failure = true; break; } } cte->expect_op_int(cte, false, "==", failure, "%s:%d: 'full' state during enqueueing", __func__, __LINE__); cw_tq_delete_internal(&tq); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Test 'is full' function while dequeueing tones from it Remember that the function checks whether tq is full, not whether it is non-empty. @reviewed 2020-10-03 */ cwt_retv test_cw_tq_is_full_internal_while_dequeueing(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, tq, "failed to create new tq"); bool failure = false; /* Fill tone queue completely. */ if (cwt_retv_ok != test_helper_fill_queue(cte, tq, tq->capacity)) { cte->log_error(cte, "%s:%d: failed to fill tone queue\n", __func__, __LINE__); return cwt_retv_err; } /* First test the function on filled and not-dequeued-from queue. */ bool is_full = LIBCW_TEST_FUT(cw_tq_is_full_internal)(tq); if (!cte->expect_op_int(cte, true, "==", is_full, "%s:%d: queue should be full after completely filling it", __func__, __LINE__)) { } /* Now test the 'is full' function as we dequeue ALL tones. */ for (size_t i = tq->capacity; i > 0; i--) { /* The 'dequeue' function has been already tested, but it won't hurt to check this simple condition here as well. The dequeue operation may return CW_TQ_JUST_EMPTIED for last tone, but it shouldn't return CW_TQ_EMPTY. */ cw_tone_t tone; const cw_queue_state_t queue_state = LIBCW_TEST_FUT(cw_tq_dequeue_internal)(tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_TQ_EMPTY, "!=", queue_state, "%s:%d: dequeueing tone #%zd", __func__, __LINE__, i)) { failure = true; break; } /* Here is the proper test of tested function. Since we have called "dequeue" above, the queue becomes non-full during first iteration. */ is_full = LIBCW_TEST_FUT(cw_tq_is_full_internal)(tq); if (!cte->expect_op_int_errors_only(cte, false, "==", is_full, "%s:%d: queue should not be full after dequeueing tone %zd", __func__, __LINE__, i)) { failure = true; break; } } cte->expect_op_int(cte, false, "==", failure, "%s:%d: 'full' state during dequeueing", __func__, __LINE__); cw_tq_delete_internal(&tq); cte->print_test_footer(cte, __func__); return 0; } /** \brief Test "capacity" property of tone queue Function tests "capacity" property of tone queue, and also tests related properties: head and tail. Just like in test_cw_tq_test_capacity_B(), enqueueing is done with cw_tq_enqueue_internal(). Unlike test_cw_tq_test_capacity_B(), this function dequeues tones using "manual" method. After every dequeue we check that dequeued tone is the one that we were expecting to get. @reviewed on 2019-10-04 */ int test_cw_tq_test_capacity_A(cw_test_executor_t * cte) { /* We don't need to check tq with capacity == CW_TONE_QUEUE_CAPACITY_MAX (yet). Let's test a smaller queue capacity. */ const size_t capacity = (lrand48() % 40) + 30; const size_t watermark = capacity - (capacity * 0.2); cte->print_test_header(cte, "%s (%zu)", __func__, capacity); /* We will do tests of queue with constant capacity, but with different initial position at which we insert first element (tone), i.e. different position of queue's head. Elements of the array should be no larger than capacity. -1 is a guard. TODO: allow negative head shifts in the test. */ const int head_shifts[] = { 0, 5, 10, 29, -1, 30, -1 }; int shift_idx = 0; while (head_shifts[shift_idx] != -1) { bool enqueue_failure = false; bool dequeue_failure = false; const int current_head_shift = head_shifts[shift_idx]; cte->log_info_cont(cte, "\n"); cte->log_info(cte, "Testing with head shift = %d\n", current_head_shift); /* For every new test with new head shift we need a "clean" queue. */ cw_tone_queue_t * tq = test_cw_tq_capacity_test_init(cte, capacity, watermark, current_head_shift); /* Fill all positions in queue with tones of known frequency. If shift_head != 0, the enqueue function should make sure that the enqueued tones are nicely wrapped after end of queue. */ for (size_t i = 0; i < tq->capacity; i++) { cw_tone_t tone; CW_TONE_INIT(&tone, (int) i, 1000, CW_SLOPE_MODE_NO_SLOPES); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "capacity A: enqueueing ton #%zu, queue size %zu, head shift %d", i, capacity, current_head_shift)) { enqueue_failure = true; break; } } /* With the queue filled with valid and known data, it's time to read back the data and verify that the tones were placed in correct positions, as expected. Let's do the readback N times, just for fun. Every time the results should be the same. We don't remove/dequeue tones from tq, we just iterate over tq and check that tone at shifted_i has correct, expected properties. */ for (int loop = 0; loop < 3; loop++) { for (size_t i = 0; i < tq->capacity; i++) { /* When shift of head == 0, tone with frequency 'i' is at index 'i'. But with non-zero shift of head, tone with frequency 'i' is at index 'shifted_i'. */ const size_t shifted_i = (i + current_head_shift) % (tq->capacity); const size_t expected_freq = i; const size_t readback_freq = tq->queue[shifted_i].frequency; if (!cte->expect_op_int_errors_only(cte, expected_freq, "==", readback_freq, "capacity A: readback loop #%d: queue position %zu, head shift %d", loop, i, current_head_shift)) { dequeue_failure = true; break; } } } /* Matches tone queue creation made in test_cw_tq_capacity_test_init(). */ cw_tq_delete_internal(&tq); cte->expect_op_int(cte, false, "==", enqueue_failure, "capacity A: enqueue @ head shift = %d:", current_head_shift); cte->expect_op_int(cte, false, "==", dequeue_failure, "capacity A: dequeue @ head shift = %d:", current_head_shift); shift_idx++; } cte->print_test_footer(cte, __func__); return 0; } /** \brief Test "capacity" property of tone queue Function tests "capacity" property of tone queue, and also tests related properties: head and tail. Just like in test_cw_tq_test_capacity_A(), enqueueing is done with cw_tq_enqueue_internal(). Unlike test_cw_tq_test_capacity_A(), this function dequeues tones using cw_tq_dequeue_internal(). After every dequeue we check that dequeued tone is the one that we were expecting to get. @reviewed on 2019-10-04 */ int test_cw_tq_test_capacity_B(cw_test_executor_t * cte) { /* We don't need to check tq with capacity == CW_TONE_QUEUE_CAPACITY_MAX (yet). Let's test a smaller queue. */ const size_t capacity = (lrand48() % 40) + 30; const size_t watermark = capacity - (capacity * 0.2); cte->print_test_header(cte, "%s", __func__); /* We will do tests of queue with constant capacity, but with different initial position at which we insert first element (tone), i.e. different position of queue's head. Elements of the array should be no larger than capacity. -1 is a guard. TODO: allow negative head shifts in the test. */ const int head_shifts[] = { 0, 5, 10, 29, -1, 30, -1 }; int shift_idx = 0; while (head_shifts[shift_idx] != -1) { bool enqueue_failure = false; bool dequeue_failure = false; bool capacity_failure = false; const int current_head_shift = head_shifts[shift_idx]; cte->log_info_cont(cte, "\n"); cte->log_info(cte, "Testing with head shift = %d, capacity = %zd\n", current_head_shift, capacity); /* For every new test with new head shift we need a "clean" queue. */ cw_tone_queue_t * tq = test_cw_tq_capacity_test_init(cte, capacity, watermark, current_head_shift); /* Fill all positions in queue with tones of known frequency. If shift_head != 0, the enqueue function should make sure that the enqueued tones are nicely wrapped after end of queue. */ for (size_t i = 0; i < tq->capacity; i++) { cw_tone_t tone; CW_TONE_INIT(&tone, (int) i, 1000, CW_SLOPE_MODE_NO_SLOPES); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "%s:%d: enqueueing tone #%zu/%zu", __func__, __LINE__, i + 1, tq->capacity)) { enqueue_failure = true; break; } } /* With the queue filled with valid and known data, it's time to read back the data and verify that the tones were placed in correct positions, as expected. In test_cw_tq_test_capacity_A() we did the readback "manually" (or rather we just iterated over a tone queue, without actually taking anything out of the tq), this time let's use "dequeue" function to do the job. Since the "dequeue" function moves queue pointers, we can do this test only once (we can't repeat the readback N times with calls to dequeue() expecting the same results). */ size_t i = 0; cw_tone_t deq_tone; /* For output only, so no need to initialize. */ cw_queue_state_t state; while (CW_TQ_EMPTY != (state = LIBCW_TEST_FUT(cw_tq_dequeue_internal)(tq, &deq_tone))) { /* When shift of head == 0, tone with frequency 'i' is at index 'i'. But with non-zero shift of head, tone with frequency 'i' is at index 'shifted_i'. */ const size_t expected_freq = i; const size_t readback_freq = deq_tone.frequency; if (!cte->expect_op_int_errors_only(cte, expected_freq, "==", readback_freq, "%s:%d: readback: state = %d, queue position %zu/%zu, head shift %d", __func__, __LINE__, state, i + 1, tq->capacity, current_head_shift)) { dequeue_failure = true; break; } i++; } const size_t n_dequeues = i; if (!cte->expect_op_int_errors_only(cte, tq->capacity, "==", n_dequeues, "capacity B: number of dequeues vs tone queue capacity")) { capacity_failure = true; } /* Matches tone queue creation made in test_cw_tq_capacity_test_init(). */ cw_tq_delete_internal(&tq); cte->expect_op_int(cte, false, "==", enqueue_failure, "capacity B: enqueue @ shift = %d:", current_head_shift); cte->expect_op_int(cte, false, "==", dequeue_failure, "capacity B: dequeue @ shift = %d:", current_head_shift); cte->expect_op_int(cte, false, "==", capacity_failure, "capacity B: capacity @ shift = %d:", current_head_shift); shift_idx++; } cte->print_test_footer(cte, __func__); return 0; } /** \brief Create and initialize tone queue for tests of capacity Create new tone queue for tests using three given parameters: \p capacity, \p high_water_mark, \p head_shift. The function is used to create a new tone queue in tests of "capacity" parameter of a tone queue. First two function parameters are rather boring. What is interesting is the third parameter: \p head_shift. In general the behaviour of tone queue (a circular list) should be independent of initial position of queue's head (i.e. from which position in the queue we start adding new elements to the queue). By initializing the queue with different initial positions of head pointer, we can test this assertion about irrelevance of initial head position. The "initialize" word may be misleading. The function does not enqueue any tones, it just initializes (resets) every slot in queue to non-random value. Returned pointer is owned by caller. @reviewed on 2019-10-04 \param capacity - intended capacity of tone queue \param high_water_mark - high water mark to be set for tone queue \param head_shift - position of first element that will be inserted in empty queue \return newly allocated and initialized tone queue */ cw_tone_queue_t * test_cw_tq_capacity_test_init(cw_test_executor_t * cte, size_t capacity, size_t high_water_mark, int head_shift) { cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, tq, "failed to create new tone queue"); // tq->state = CW_TQ_NONEMPTY; TODO: what does it do here? /* TODO: we can come up with better test for "set_capacity_internal": 1. have empty queue, 2. set capacity of queue to N, 3. try to add N tones - should succeed, 4. try to add N+1 tones - should fail. */ cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_set_capacity_internal)(tq, capacity, high_water_mark); cte->assert2(cte, cwret == CW_SUCCESS, "failed to set capacity/high water mark"); cte->assert2(cte, tq->capacity == capacity, "incorrect capacity: %zu != %zu", tq->capacity, capacity); cte->assert2(cte, tq->high_water_mark == high_water_mark, "incorrect high water mark: %zu != %zu", tq->high_water_mark, high_water_mark); /* Initialize *all* tones with known value. Do this manually, to be 100% sure that all tones in queue table have been initialized. */ for (int i = 0; i < CW_TONE_QUEUE_CAPACITY_MAX; i++) { CW_TONE_INIT(&tq->queue[i], 10000 + i, 1, CW_SLOPE_MODE_STANDARD_SLOPES); } /* Move head and tail of empty queue to initial position. The queue is empty because the initialization of fields done above is not considered as real enqueueing of valid tones. */ tq->tail = head_shift; tq->head = tq->tail; tq->len = 0; /* TODO: why do this here? */ //tq->state = CW_TQ_NONEMPTY; return tq; } /** \brief Test the limits of the parameters to the tone queue routine @reviewed on 2019-10-04 */ cwt_retv test_cw_tq_enqueue_internal_tone_validity(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, tq, "failed to create a tone queue\n"); cw_tone_t tone; cw_ret_t cwret; int freq_min, freq_max; cw_get_frequency_limits(&freq_min, &freq_max); /* Test 1: invalid duration of tone. */ errno = 0; tone.duration = -1; /* Invalid duration. */ tone.frequency = freq_min; /* Valid frequency. */ cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(tq, &tone); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "enqueued tone with invalid duration (cwret)"); cte->expect_op_int(cte, EINVAL, "==", errno, "enqueued tone with invalid duration (cwret)"); /* Test 2: tone's frequency too low. */ errno = 0; tone.duration = 100; /* Valid duration. */ tone.frequency = freq_min - 1; /* Invalid frequency. */ cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(tq, &tone); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "enqueued tone with too low frequency (cwret)"); cte->expect_op_int(cte, EINVAL, "==", errno, "enqueued tone with too low frequency (errno)"); /* Test 3: tone's frequency too high. */ errno = 0; tone.duration = 100; /* Valid duration. */ tone.frequency = freq_max + 1; /* Invalid frequency. */ cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(tq, &tone); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "enqueued tone with too high frequency (cwret)"); cte->expect_op_int(cte, EINVAL, "==", errno, "enqueued tone with too high frequency (errno)"); cw_tq_delete_internal(&tq); cte->expect_null_pointer(cte, tq, "tone queue not deleted properly"); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Add @param count tones to tone queue This is a test helper function. @reviewed 2020-10-03 @param[in] cte test executor @param[in/out] tq tone queue to which to add tones @param[in] count how many tones to add? @return cwt_retv_ok on success @return cwt_retv_err otherwise */ cwt_retv test_helper_fill_queue(cw_test_executor_t * cte, cw_tone_queue_t * tq, size_t count) { /* This is a test helper function, so I don't use cte->expect_op_int...(). Using this function would increment test status counters. */ cw_tone_t tone; CW_TONE_INIT(&tone, 20, 10000, CW_SLOPE_MODE_STANDARD_SLOPES); for (size_t i = 0; i < count; i++) { const cw_ret_t cwret = cw_tq_enqueue_internal(tq, &tone); if (CW_SUCCESS != cwret) { cte->log_error(cte, "%s:%d: enqueue tone #%zd failed\n", __func__, __LINE__, i); return cwt_retv_err; } } const size_t len = tq->len; if (len == count) { return cwt_retv_ok; } else { cte->log_error(cte, "%s:%d: queue len invalid: %zd != %zd\n", __func__, __LINE__, len, count); return cwt_retv_err; } } /** This function creates a generator that internally uses a tone queue. The generator is needed to perform automatic dequeueing operations, so that cw_tq_wait_for_level_internal() can detect expected level. @reviewed on */ cwt_retv test_cw_tq_wait_for_level_internal(cw_test_executor_t * cte) { /* +1 for cases where repetition count is 1. In such cases modulo operation in the loop would fail. */ const int loops = cte->get_loops_count(cte) + 1; cte->print_test_header(cte, "%s (%d)", __func__, loops); bool wait_failure = false; bool diff_failure = false; cw_gen_t * gen = NULL; for (int i = 0; i < loops; i++) { gen = cw_gen_new(&cte->current_gen_conf); cte->assert2(cte, gen, "failed to create a tone queue\n"); cw_gen_start(gen); if (cwt_retv_ok != test_helper_fill_queue(cte, gen->tq, loops)) { cte->log_error(cte, "%s:%d: failed to fill tone queue\n", __func__, __LINE__); return cwt_retv_err; } /* Notice that level is always smaller than number of items added to queue. TODO: reconsider if we want to randomize this value. */ const int level = lrand48() % (int) (floorf(0.7F * loops)); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_wait_for_level_internal)(gen->tq, level); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "wait for level = %d, tone queue items count = %d", level, loops)) { wait_failure = true; break; } const size_t readback_len = cw_tq_length_internal(gen->tq); /* cw_tq_length_internal() is called after return of tested function, so 'len' can be smaller by one, but never larger, than 'level'. During initial tests, for function implemented with signals and with alternative (newer) inter-thread comm method, len was always equal to level. */ const size_t expected_len_lower = level == 0 ? level : level - 1; const size_t expected_len_higher = level; if (!cte->expect_between_int_errors_only(cte, expected_len_lower, readback_len, expected_len_higher, "wait for level: length of queue after end of waiting")) { diff_failure = true; break; } cw_gen_stop(gen); cw_gen_delete(&gen); } cte->expect_op_int(cte, false, "==", wait_failure, "wait for level (wait function)"); cte->expect_op_int(cte, false, "==", diff_failure, "wait for level (queue length)"); /* For those tests that fail. */ if (gen) { cw_gen_stop(gen); cw_gen_delete(&gen); } cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** \brief Simple tests of queueing and dequeueing of tones This is not an entirely stand-alone queue, but a queue that is a part of generator. Ensure we can generate a few simple tones, and wait for them to end. @reviewed on */ int test_cw_tq_gen_operations_A(cw_test_executor_t * cte) { /* +1 for cases where repetition count is 1. In such cases division operation in the loop would fail. */ const int loops = cte->get_loops_count(cte) + 1; cte->print_test_header(cte, "%s (%d)", __func__, loops); cw_gen_t * gen = NULL; gen_setup(cte, &gen); /* Notice that we start the generator later. */ int freq_min, freq_max; cw_get_frequency_limits(&freq_min, &freq_max); const int duration = 100000; /* Duration of tone. */ const int delta_freq = ((freq_max - freq_min) / (loops - 1)); /* Test 1: enqueue max tones, and wait for each of them separately. Control length of tone queue in the process. */ { bool length_failure = false; bool enqueue_failure = false; /* Enqueue first tone. Don't check queue length yet. The first tone is being dequeued right after enqueueing, so checking the queue length would yield incorrect result. Instead, enqueue the first tone, and during the process of dequeueing it, enqueue rest of the tones in the loop, together with checking length of the tone queue. */ int freq = freq_min; cw_tone_t tone; CW_TONE_INIT(&tone, freq, duration, CW_SLOPE_MODE_NO_SLOPES); /* Enqueue rest of tones. Generator is not started yet, so tones won't be dequeued parallel to being enqueued, so we always have certainty how many tones must there be in queue. */ for (int i = 0; i < loops; i++) { /* Monitor length of a queue as it is filled - before adding a new tone. */ size_t expected_length = (size_t) i; /* Expected length of tone queue. */ /* Measured length of tone queue. */ size_t readback_length = LIBCW_TEST_FUT(cw_tq_length_internal)(gen->tq); if (!cte->expect_op_int_errors_only(cte, expected_length, "==", readback_length, "tq gen operations A: length pre-enqueue (#%02d):", i)) { length_failure = true; break; } /* Add a tone to queue. All frequencies should be within allowed range, so there should be no error. */ freq = freq_min + i * delta_freq; CW_TONE_INIT(&tone, freq, duration, CW_SLOPE_MODE_NO_SLOPES); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(gen->tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "tq gen operations A: enqueue (#%02d)", i)) { enqueue_failure = true; break; } /* Monitor length of a queue as it is filled - after adding a new tone. */ readback_length = LIBCW_TEST_FUT(cw_tq_length_internal)(gen->tq); expected_length = (size_t) (i + 1); if (!cte->expect_op_int_errors_only(cte, expected_length, "==", readback_length, "tq gen operations A: length: post-enqueue (#%02d):", i)) { length_failure = true; break; } } cte->expect_op_int(cte, false, "==", length_failure, "tq gen operations A: length during enqueue"); cte->expect_op_int(cte, false, "==", enqueue_failure, "tq gen operations A: enqueue"); } /* And this is the proper test - waiting for dequeueing tones. The dequeueing must happen automatically, so we have to start the generator. Starting the generator will dequeue first tone, so we will expect the measured length to be in a range of values. */ cw_gen_start(gen); /* TODO: I understand that when generator is started, one tone is taken from tq: this is reflected in using "-1" in initialization of for() loop. But then testing the tone queue with ranges is not really necessary: we should be able to tell exactly what the length of queue in each iteration will be. */ bool length_failure = false; bool wait_failure = false; for (int i = loops - 1; i > 0; i--) { /* -1 because after starting the generator one tone is already dequeued. */ size_t readback_length = 0; /* Measured length of tone queue. */ size_t expected_length_min = 0; size_t expected_length_max = 0; /* Monitor length of a queue as it is emptied - before dequeueing. */ readback_length = LIBCW_TEST_FUT(cw_tq_length_internal)(gen->tq); expected_length_min = (size_t) i - 1; expected_length_max = (size_t) i; if (!cte->expect_between_int_errors_only(cte, expected_length_min, readback_length, expected_length_max, "tq gen operations A: length pre-dequeue (#%02d)", i)) { length_failure = true; break; } /* Wait for each of N tones to be dequeued. */ const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_wait_for_end_of_current_tone_internal)(gen->tq); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "tq gen operations A: wait for tone (#%02d)", i)) { wait_failure = true; break; } /* Monitor length of a queue as it is emptied - after dequeueing. */ readback_length = LIBCW_TEST_FUT(cw_tq_length_internal)(gen->tq); expected_length_min = (size_t) i - 1 - 1; expected_length_max = (size_t) i - 1; if (!cte->expect_between_int_errors_only(cte, expected_length_min, readback_length, expected_length_max, "tq gen operations A: length post-dequeue (#%02d)", i)) { length_failure = true; break; } } cte->expect_op_int(cte, false, "==", length_failure, "tq gen operations A: length during dequeue"); cte->expect_op_int(cte, false, "==", wait_failure, "tq gen operations A: wait for tone"); /* Test 2: fill a queue, but this time don't wait for each tone separately, but wait for a whole queue to become empty. */ bool failure = false; for (int i = 0; i < loops; i++) { int freq = freq_min + i * delta_freq; cw_tone_t tone; CW_TONE_INIT(&tone, freq, duration, CW_SLOPE_MODE_NO_SLOPES); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(gen->tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "tq gen operations A: enqueue all, tone %04d)", i)) { failure = true; break; } } cte->expect_op_int(cte, false, "==", failure, "tq gen operations A: enqueue all"); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_wait_for_level_internal)(gen->tq, 0); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "tq gen operations A: final wait for level 0"); gen_destroy(&gen); cte->print_test_footer(cte, __func__); return 0; } /** Run the complete range of tone generation, at 1Hz intervals, first up the octaves, and then down. If the queue fills (which is expected with frequency step so small) then pause until it isn't so full. @reviewed on 2019-10-06 */ int test_cw_tq_gen_operations_B(cw_test_executor_t * cte) { cw_gen_t * gen = NULL; gen_setup(cte, &gen); const size_t capacity = cw_tq_capacity_internal(gen->tq); const int level = lrand48() % (capacity / 2); cte->print_test_header(cte, "%s (%d)", __func__, level); const int duration = 4000; int freq_min, freq_max; cw_get_frequency_limits(&freq_min, &freq_max); const int freq_delta = 1; /* Because in the loops below we want to saturate (completely fill) tone queue with tones and then wait (with cw_tq_is_full_internal() + cw_tq_wait_for_level_internal()) for some free space in the queue, we have to be able to enqueue more tones than the capacity of tq. Because we want to enqueue (freq_max - freq_min) tones, we better check that capacity is smaller than that. */ cte->assert2(cte, capacity < (size_t) (freq_max - freq_min), "range of frequencies is too small"); bool wait_failure = false; bool queue_failure = false; /* The test loops below use cw_tq_wait_for_level_internal(), so generator must be running. */ cw_gen_start(gen); for (int freq = freq_min; freq < freq_max; freq += freq_delta) { while (cw_tq_is_full_internal(gen->tq)) { /* TODO: currently there is no guarantee that the tone queue will become full and the tested function will be called. It would be better to make capacity rather small (e.g. 200) compared to range of frequencies. */ const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_wait_for_level_internal)(gen->tq, level); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "wait for level %d (up)", level)) { wait_failure = true; break; } } cw_tone_t tone; CW_TONE_INIT(&tone, freq, duration, CW_SLOPE_MODE_NO_SLOPES); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(gen->tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueue tone %d (up)", freq)) { queue_failure = true; break; } } cte->expect_op_int(cte, false, "==", queue_failure, "enqueueing tone (up)"); cte->expect_op_int(cte, false, "==", wait_failure, "waiting for level %d (up)", level); wait_failure = false; queue_failure = false; for (int freq = freq_max; freq > freq_min; freq -= 1) { while (cw_tq_is_full_internal(gen->tq)) { const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_wait_for_level_internal)(gen->tq, level); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "wait for level %d (down)", level)) { wait_failure = true; break; } } cw_tone_t tone; CW_TONE_INIT(&tone, freq, duration, CW_SLOPE_MODE_NO_SLOPES); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(gen->tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueue tone %d (down)", freq)) { queue_failure = true; break; } } cte->expect_op_int(cte, false, "==", queue_failure, "enqueueing tone (down)"); cte->expect_op_int(cte, false, "==", wait_failure, "waiting for level %d (down)", level); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_wait_for_level_internal)(gen->tq, 0); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "waiting for level 0 (final)"); cw_tone_t tone; CW_TONE_INIT(&tone, 0, 100, CW_SLOPE_MODE_NO_SLOPES); cw_tq_enqueue_internal(gen->tq, &tone); cw_tq_wait_for_level_internal(gen->tq, 0); gen_destroy(&gen); cte->print_test_footer(cte, __func__); return 0; } /** @brief Test properties (capacity, length and other) of empty tone queue @reviewed 2020-10-03 */ cwt_retv test_cw_tq_properties_empty(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); cw_tone_queue_t * tq = cw_tq_new_internal(); bool failure_capacity = false; bool failure_length = false; bool failure_dequeue = false; /* Do the tests few times in a loop. In the loop simulate using the queue (by filling it) and then emptying it and in next iteration re-test properties of emptied queue (i.e. queue that is empty not because it's never been used, but because it has been emptied). */ for (size_t i = 0; i < 5; i++) { /* Capacity of queue should always be the same. */ const int capacity = LIBCW_TEST_FUT(cw_tq_capacity_internal)(tq); if (!cte->expect_op_int_errors_only(cte, CW_TONE_QUEUE_CAPACITY_MAX, "==", capacity, "%s:%d: empty queue's capacity", __func__, __LINE__)) { failure_capacity = true; break; } /* Length of empty tone queue should be zero (of course). */ const size_t readback_len = LIBCW_TEST_FUT(cw_tq_length_internal)(tq); if (!cte->expect_op_int_errors_only(cte, 0, "==", readback_len, "%s:%d: empty queue's length", __func__, __LINE__)) { failure_length = true; break; } if (!cte->expect_op_int_errors_only(cte, 0, "==", tq->len, "%s:%d: empty queue's length", __func__, __LINE__)) { failure_length = true; break; } /* Dequeueing from empty tone queue should return CW_TQ_EMPTY. */ cw_tone_t tone; const cw_queue_state_t queue_state = LIBCW_TEST_FUT(cw_tq_dequeue_internal)(tq, &tone); if (!cte->expect_op_int_errors_only(cte, CW_TQ_EMPTY, "==", queue_state, "%s:%d: dequeueing from empty queue", __func__, __LINE__)) { failure_dequeue = true; break; } /* Prepare tone queue for next iteration of the loop: fill and empty the tone queue. */ if (cwt_retv_ok != test_helper_fill_queue(cte, tq, tq->capacity)) { cte->log_error(cte, "%s:%d: failed to fill tone queue\n", __func__, __LINE__); return cwt_retv_err; } cw_tq_flush_internal(tq); cw_tq_wait_for_level_internal(tq, 0); /* Go to checking properties of emptied tq in next iteration. */ } cte->expect_op_int(cte, false, "==", failure_capacity, "%s:%d: empty queue's capacity", __func__, __LINE__); cte->expect_op_int(cte, false, "==", failure_length, "%s:%d: empty queue's length", __func__, __LINE__); cte->expect_op_int(cte, false, "==", failure_dequeue, "%s:%d: dequeueing from empty queue", __func__, __LINE__); cw_tq_delete_internal(&tq); cte->expect_null_pointer_errors_only(cte, tq, "deleting tone queue"); cte->print_test_footer(cte, __func__); return 0; } /** @brief Test properties (capacity, length and other) of full tq Since the queue is not a member of a generator that is started, there will be no automatic dequeueing. TODO: add a test that verifies that non-started-generator doesn't dequeue tones. @reviewed 2020-10-02 */ cwt_retv test_cw_tq_properties_full(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); cw_tone_queue_t * tq = cw_tq_new_internal(); if (cwt_retv_ok != test_helper_fill_queue(cte, tq, tq->capacity)) { cte->log_error(cte, "%s:%d: failed to fill tone queue\n", __func__, __LINE__); return cwt_retv_err; } const size_t capacity = LIBCW_TEST_FUT(cw_tq_capacity_internal)(tq); cte->expect_op_int(cte, CW_TONE_QUEUE_CAPACITY_MAX, "==", capacity, "%s:%d: full queue's capacity", __func__, __LINE__); const size_t len_full = LIBCW_TEST_FUT(cw_tq_length_internal)(tq); cte->expect_op_int(cte, CW_TONE_QUEUE_CAPACITY_MAX, "==", len_full, "%s:%d: full queue's length", __func__, __LINE__); /* This tests for correctness of working of the 'enqueue' function. Full tq should not grow beyond its capacity. */ cte->expect_op_int(cte, tq->capacity, "==", tq->len, "%s:%d: length of full queue vs. capacity (first variant)", __func__, __LINE__); cte->expect_op_int(cte, capacity, "==", len_full, "%s:%d: length of full queue vs. capacity (second variant)", __func__, __LINE__); /* Try adding a tone to full tq. This tests for potential problems with 'enqueue' function. Enqueueing should fail when the queue is full. */ cte->log_info(cte, "*** you may now see \"EE: can't enqueue tone, tq is full\" message ***\n"); cw_tone_t tone; CW_TONE_INIT(&tone, 0, 100, CW_SLOPE_MODE_NO_SLOPES); const cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_enqueue_internal)(tq, &tone); cte->expect_op_int(cte, CW_FAILURE, "==", cwret, "%s:%d: attempting to enqueue tone to full queue", __func__, __LINE__); cw_tq_delete_internal(&tq); cte->print_test_footer(cte, __func__); return 0; } struct cw_callback_struct { cw_gen_t * gen; cw_test_executor_t * cte; size_t captured_level; bool already_captured; }; /** @reviewed on 2019-10-07 */ int test_cw_tq_callback(cw_test_executor_t * cte) { const int loops = cte->get_loops_count(cte); cte->print_test_header(cte, "%s (%d)", __func__, loops); cw_gen_t * gen = NULL; bool register_failure = false; bool enqueue_failure = false; bool capture_failure = false; for (int i = 1; i < loops; i++) { /* TODO: the test doesn't work for i == 0. This needs to be communicated in documentation. */ gen_setup(cte, &gen); struct cw_callback_struct test_data; memset(&test_data, 0, sizeof (test_data)); test_data.gen = gen; test_data.cte = cte; test_data.captured_level = 999999; test_data.already_captured = false; /* Used to prevent multiple captures of level in one iteration of this loop. */ /* Test the callback mechanism for very small values, but for a bit larger as well. */ int level = i <= 5 ? i : 3 * i; cw_ret_t cwret = LIBCW_TEST_FUT(cw_tq_register_low_level_callback_internal)(gen->tq, test_helper_tq_callback, (void *) &test_data, level); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "registering low level callback with level %d", level)) { register_failure = true; break; } /* Add a lot of tones to tone queue. "a lot" means two times more than a value of trigger level. */ for (int j = 0; j < 2 * level; j++) { cwret = cw_gen_enqueue_character_no_ics(gen, 'e'); /* TODO: if we want to add 'a lot", then single-mark 'e' may not be the best choice. */ if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "enqueueing tones, tone #%d", j)) { enqueue_failure = true; break; } } if (enqueue_failure) { break; } /* Start generator (and dequeueing) only after the tq has been filled. */ cw_gen_start(gen); /* Wait for the queue to be drained to zero. While the tq is drained, and level of tq reaches trigger level, a callback will be called. Its only task is to copy the current level (tq level at time of calling the callback) value into test_data.captured_level. Since the value of trigger level is different in consecutive iterations of loop, we can test the callback for different values of trigger level. */ cw_tq_wait_for_level_internal(gen->tq, 0); /* Because of order of calling callback and decreasing length of queue, I think that it's safe to assume that level captured by registered callback may be within a range of expected values. */ const int expected_level_lower = level - 1; const int expected_level_higher = level; if (!cte->expect_between_int_errors_only(cte, expected_level_lower, test_data.captured_level, expected_level_higher, "tone queue callback: capturing level")) { capture_failure = true; break; } cw_tq_flush_internal(gen->tq); gen_destroy(&gen); } cte->expect_op_int(cte, false, "==", register_failure, "registering low level callback"); cte->expect_op_int(cte, false, "==", enqueue_failure, "enqueueing tones"); cte->expect_op_int(cte, false, "==", capture_failure, "capturing tone queue level"); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-07 */ static void test_helper_tq_callback(void * data) { struct cw_callback_struct * s = (struct cw_callback_struct *) data; if (!s->already_captured) { s->captured_level = cw_tq_length_internal(s->gen->tq); s->already_captured = true; s->cte->log_info(s->cte, "tq callback helper: captured level = %zd\n", s->captured_level); } return; } /** @brief Test return values of dequeue function @reviewed 2020-10-03 */ cwt_retv test_cw_tq_dequeue_internal_returns(cw_test_executor_t * cte) { cte->print_test_header(cte, "%s", __func__); bool failure = false; cw_tone_queue_t * tq = cw_tq_new_internal(); cte->assert2(cte, tq, "failed to create new tone queue"); struct { int frequency; cw_queue_state_t expected_state; } test_data[] = { { 100, CW_TQ_NONEMPTY }, { 100, CW_TQ_NONEMPTY }, { 100, CW_TQ_NONEMPTY }, { 100, CW_TQ_NONEMPTY }, { 100, CW_TQ_NONEMPTY }, { 100, CW_TQ_NONEMPTY }, { 100, CW_TQ_NONEMPTY }, { 100, CW_TQ_NONEMPTY }, { 100, CW_TQ_NONEMPTY }, /* This will be the last tone added to queue, so dequeuing it should return 'just emptied'. */ { 100, CW_TQ_JUST_EMPTIED }, /* This tone won't be enqueued by test code (frequency == 0), and dequeue operation should return 'empty' when doing this N-th dequeue attempt because the queue should transition from 'just emptied' to 'empty' */ { 0, CW_TQ_EMPTY }, }; const size_t n_items = sizeof (test_data) / sizeof (test_data[0]); /* Enqueue tones. */ for (size_t i = 0; i < n_items; i++) { if (test_data[i].frequency) { cw_tone_t tone; const int duration = 100; CW_TONE_INIT(&tone, test_data[i].frequency, duration, CW_SLOPE_MODE_STANDARD_SLOPES); cw_tq_enqueue_internal(tq, &tone); } } /* Dequeue tones, do the test. */ for (size_t i = 0; i < n_items; i++) { cw_tone_t tone; const cw_queue_state_t queue_state = LIBCW_TEST_FUT(cw_tq_dequeue_internal)(tq, &tone); if (!cte->expect_op_int_errors_only(cte, test_data[i].expected_state, "==", queue_state, "%s:%d: dequeue return value for test data #%zd", __func__, __LINE__, i)) { failure = true; break; } } cw_tq_delete_internal(&tq); cte->expect_op_int(cte, false, "==", failure, "dequeue's return values"); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } unixcw-3.6.0/src/libcw/tests/libcw_utils_tests.c0000644000175000017500000002270214000344554016717 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include "libcw.h" #include "libcw2.h" #include "libcw_debug.h" #include "libcw_key.h" #include "libcw_utils.h" #include "libcw_utils_tests.h" #include "test_framework.h" /** @reviewed on 2019-10-15 */ int test_cw_timestamp_compare_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* TODO: I think that there may be more tests to perform for the function, testing handling of overflow. */ struct { struct timeval earlier; struct timeval later; int expected_delta_usecs; bool test_valid; } test_data[] = { { { 17, 19 }, { 17, 19 }, 0, true }, /* Two same timestamps. */ { { 17, 19 }, { 17, 20 }, 1, true }, /* Simple one microsecond difference. */ { { 17, CW_USECS_PER_SEC - 1 }, { 18, 0 }, 1, true }, /* Less simple one microsecond difference. */ { { 17, CW_USECS_PER_SEC - 1 }, { 17, CW_USECS_PER_SEC + 1 }, 2, true }, /* Two microseconds difference with usecs larger than limit. */ { { 17, 1 * CW_USECS_PER_SEC }, { 17, 2 * CW_USECS_PER_SEC }, 1 * CW_USECS_PER_SEC, true }, /* One second difference because of count of microseconds. */ { { 17, (1 * CW_USECS_PER_SEC) - 1 }, { 17, (2 * CW_USECS_PER_SEC) + 1 }, (1 * CW_USECS_PER_SEC) + 2, true }, /* One second and two microseconds difference because of count of microseconds. */ { { 0, 0 }, { 0, 0 }, 0, false } /* Guard. */ }; bool failure = false; int i = 0; while (test_data[i].test_valid) { const int calculated_delta_usecs = LIBCW_TEST_FUT(cw_timestamp_compare_internal)(&test_data[i].earlier, &test_data[i].later); if (!cte->expect_op_int_errors_only(cte, test_data[i].expected_delta_usecs, "==", calculated_delta_usecs, "timestamps diff: test #%d", i)) { failure = true; break; } i++; } cte->expect_op_int(cte, false, "==", failure, "timestamps diff"); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-13 */ int test_cw_timestamp_validate_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Test 1 - get current time. */ { /* Get reference time through gettimeofday(). */ struct timeval ref_timestamp = { 0, 0 }; /* Reference timestamp. */ cte->assert2(cte, 0 == gettimeofday(&ref_timestamp, NULL), "failed to get reference time"); /* Get current time through libcw function. */ struct timeval out_timestamp = { 0, 0 }; cw_ret_t cwret = LIBCW_TEST_FUT(cw_timestamp_validate_internal)(&out_timestamp, NULL); cte->expect_op_int(cte, CW_SUCCESS, "==", cwret, "current timestamp"); /* Check the diff between the two timestamps. On my desktop PC it's ~8us. */ const int diff = cw_timestamp_compare_internal(&ref_timestamp, &out_timestamp); #if 1 cte->log_info(cte, "delay in getting timestamp is %d microseconds\n", diff); #endif cte->expect_op_int(cte, 100, ">", diff, "delay in getting timestamp"); } struct test_data { bool valid; struct timeval in; int expected_cwret; int expected_errno; const char * name; } test_data[] = { { true, { 1234, 987 }, CW_SUCCESS, 0, "valid" }, /* Test 2 - validate valid input timestamp and copy it to output timestamp. */ { true, { -1, 987 }, CW_FAILURE, EINVAL, "invalid seconds" }, /* Test 3 - detect invalid seconds in input timestamp. */ { true, { 123, CW_USECS_PER_SEC + 1 }, CW_FAILURE, EINVAL, "microseconds too large" }, /* Test 4 - detect invalid microseconds in input timestamp (microseconds too large). */ { true, { 123, -1 }, CW_FAILURE, EINVAL, "microseconds negative" }, /* Test 5 - detect invalid microseconds in input timestamp (microseconds negative). */ { false, { 0, 0 }, CW_SUCCESS, 0, "" }, /* Guard. */ }; int i = 0; while (test_data[i].valid) { struct timeval out = { 0, 0 }; errno = 0; cw_ret_t cwret = LIBCW_TEST_FUT(cw_timestamp_validate_internal)(&out, &test_data[i].in); cte->expect_op_int(cte, test_data[i].expected_cwret, "==", cwret, "%s (cwret)", test_data[i].name); cte->expect_op_int(cte, test_data[i].expected_errno, "==", errno, "%s (errno)", test_data[i].name); if (CW_SUCCESS == test_data[i].expected_cwret) { cte->expect_op_int(cte, test_data[i].in.tv_sec, "==", out.tv_sec, "%s (copy sec)", test_data[i].name); cte->expect_op_int(cte, test_data[i].in.tv_usec, "==", out.tv_usec, "%s (copy usec)", test_data[i].name); } i++; } cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-13 */ int test_cw_usecs_to_timespec_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); struct { int input; struct timespec t; } input_data[] = { /* input in ms / expected output seconds:milliseconds */ { 0, { 0, 0 }}, { 1000000, { 1, 0 }}, { 1000004, { 1, 4000 }}, { 15000350, { 15, 350000 }}, { 73, { 0, 73000 }}, { -1, { 0, 0 }}, /* Guard. */ }; bool seconds_failure = false; bool microseconds_failure = false; int i = 0; while (input_data[i].input != -1) { struct timespec result = { .tv_sec = 0, .tv_nsec = 0 }; LIBCW_TEST_FUT(cw_usecs_to_timespec_internal)(&result, input_data[i].input); #if 0 fprintf(stderr, "input = %d usecs, output = %ld.%ld\n", input_data[i].input, (long) result.tv_sec, (long) result.tv_nsec); #endif if (!cte->expect_op_int_errors_only(cte, input_data[i].t.tv_sec, "==", result.tv_sec, "test %d: seconds", i)) { seconds_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, input_data[i].t.tv_nsec, "==", result.tv_nsec, "test %d: microseconds", i)) { microseconds_failure = true; break; } i++; } cte->expect_op_int(cte, false, "==", seconds_failure, "seconds"); cte->expect_op_int(cte, false, "==", microseconds_failure, "microseconds"); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-13 */ int test_cw_version_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); int readback_current = 77; int readback_revision = 88; int readback_age = 99; /* Dummy initial values. */ LIBCW_TEST_FUT(cw_get_lib_version)(&readback_current, &readback_revision, &readback_age); /* Library's version is defined in LIBCW_VERSION. cw_version() uses three calls to strtol() to get three parts of the library version. Let's use a different approach to convert LIBCW_VERSION into numbers. */ #define VERSION_LEN_MAX 30 cte->assert2(cte, strlen(LIBCW_VERSION) <= VERSION_LEN_MAX, "LIBCW_VERSION longer than expected!\n"); char buffer[VERSION_LEN_MAX + 1] = { 0 }; strncpy(buffer, LIBCW_VERSION, VERSION_LEN_MAX); buffer[VERSION_LEN_MAX] = '\0'; #undef VERSION_LEN_MAX char *str = buffer; int expected_current = 0; int expected_revision = 0; int expected_age = 0; bool tokens_failure = false; int i_tokens = 0; for (; ; i_tokens++, str = NULL) { char * token = strtok(str, ":"); if (token == NULL) { /* We should end tokenizing process after 3 valid tokens, no more and no less. */ cte->expect_op_int(cte, 3, "==", i_tokens, "stopping at token %d", i_tokens); break; } if (0 == i_tokens) { expected_current = atoi(token); } else if (1 == i_tokens) { expected_revision = atoi(token); } else if (2 == i_tokens) { expected_age = atoi(token); } else { tokens_failure = true; break; } } cte->expect_op_int(cte, false, "==", tokens_failure, "number of tokens"); cte->expect_op_int(cte, readback_current, "==", expected_current, "current: %d / %d", readback_current, expected_current); cte->expect_op_int(cte, readback_revision, "==", expected_revision, "revision: %d / %d", readback_revision, expected_revision); cte->expect_op_int(cte, readback_age, "==", expected_age, "age: %d / %d", readback_age, expected_age); cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2019-10-13 */ int test_cw_license_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Well, there isn't much to test here. The function just prints the license to stdout, and that's it. */ LIBCW_TEST_FUT(cw_license)(); cte->expect_op_int(cte, false, "==", false, "libcw license:"); cte->print_test_footer(cte, __func__); return 0; } unixcw-3.6.0/src/libcw/tests/libcw_tq_tests.h0000644000175000017500000000302514000344554016205 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_TQ_TESTS_H_ #define _LIBCW_TQ_TESTS_H_ #include "test_framework.h" #include "libcw_gen.h" int test_cw_tq_init_internal(void); cwt_retv test_cw_tq_enqueue_internal_tone_validity(cw_test_executor_t * cte); int test_cw_tq_test_capacity_A(cw_test_executor_t * cte); int test_cw_tq_test_capacity_B(cw_test_executor_t * cte); cwt_retv test_cw_tq_wait_for_level_internal(cw_test_executor_t * cte); cwt_retv test_cw_tq_is_full_internal_while_enqueueing(cw_test_executor_t * cte); cwt_retv test_cw_tq_is_full_internal_while_dequeueing(cw_test_executor_t * cte); int test_cw_tq_enqueue_dequeue_internal(cw_test_executor_t * cte); int test_cw_tq_enqueue_args_internal(cw_test_executor_t * cte); int test_cw_tq_new_delete_internal(cw_test_executor_t * cte); int test_cw_tq_capacity_internal(cw_test_executor_t * cte); int test_cw_tq_length_internal_1(cw_test_executor_t * cte); int test_cw_tq_callback(cw_test_executor_t * cte); int test_cw_tq_prev_index_internal(cw_test_executor_t * cte); int test_cw_tq_next_index_internal(cw_test_executor_t * cte); int test_cw_tq_gen_operations_A(cw_test_executor_t * cte); int test_cw_tq_gen_operations_B(cw_test_executor_t * cte); cwt_retv test_cw_tq_properties_empty(cw_test_executor_t * cte); cwt_retv test_cw_tq_properties_full(cw_test_executor_t * cte); cwt_retv test_cw_tq_dequeue_internal_returns(cw_test_executor_t * cte); #endif /* #ifndef _LIBCW_TQ_TESTS_H_ */ unixcw-3.6.0/src/libcw/tests/Makefile.am0000644000175000017500000000534114000344554015045 00000000000000# Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) # Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # -include $(top_builddir)/Makefile.inc TESTS = $(check_SCRIPTS) # targets to be built in this directory check_PROGRAMS = libcw_tests # run test programs check_SCRIPTS = libcw_test_quick.sh libcw_test_quick.sh: # empty # List of files that implement tests of specific bug fixes. bug_test_files = \ libcw_test_tq_short_space.c \ libcw_test_tq_short_space.h test_framework_files = \ test_framework.c \ test_framework.h \ test_framework_tools.c \ test_framework_tools.h modern_api_test_files = \ libcw_data_tests.c \ libcw_data_tests.h \ libcw_gen_tests.c \ libcw_gen_tests.h \ libcw_gen_tests_state_callback.c \ libcw_gen_tests_state_callback.h \ libcw_rec_tests.c \ libcw_rec_tests.h \ libcw_utils_tests.c \ libcw_utils_tests.h \ libcw_key_tests.c \ libcw_key_tests.h \ libcw_debug_tests.c \ libcw_debug_tests.h \ libcw_tq_tests.c \ libcw_tq_tests.h legacy_api_test_files = \ libcw_legacy_api_tests.c \ libcw_legacy_api_tests.h \ libcw_legacy_api_tests_rec_poll.c \ libcw_legacy_api_tests_rec_poll.h libcw_tests_SOURCES = \ $(legacy_api_test_files) \ $(modern_api_test_files) \ $(bug_test_files) \ $(test_framework_files) \ test_data.c \ test_sets.c \ test_main.c libcw_tests_CPPFLAGS = $(AM_CPPFLAGS) -DLIBCW_UNIT_TESTS #libcw_tests_CFLAGS = -I../../ # Target-specific linker flags (objects to link). Order is important: # first static libraries then dynamic. Otherwise linker may not find # symbols from the dynamic library. libcw_tests_LDADD = $(top_builddir)/src/cwutils/lib_libcw_tests.a libcw_tests_LDADD += $(top_builddir)/src/cwutils/lib_rec_tests.a libcw_tests_LDADD += $(INTL_LIB) -lm -lpthread $(DL_LIB) -L../.libs -lcw_test EXTRA_DIST = \ $(check_SCRIPTS) \ count_functions_under_test.py # sources, references # # source of snippet related to "check_SCRIPTS" and related sh script: # http://www.freesoftwaremagazine.com/books/agaal/automatically_writing_makefiles_with_autotools unixcw-3.6.0/src/libcw/tests/libcw_test_tq_short_space.h0000644000175000017500000000054514000344554020420 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_TEST_TQ_SHORT_SPACE_H_ #define _LIBCW_TEST_TQ_SHORT_SPACE_H_ #include "test_framework.h" int legacy_api_test_tq_short_space(cw_test_executor_t * cte); #endif /* #ifndef _LIBCW_TEST_TQ_SHORT_SPACE_H_ */ unixcw-3.6.0/src/libcw/tests/libcw_legacy_api_tests_rec_poll.h0000644000175000017500000000070714000344554021541 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_LEGACY_TESTS_REC_POLL_H_ #define _LIBCW_LEGACY_TESTS_REC_POLL_H_ #include "test_framework.h" /* 'Receiver' test area - poll interface as used in xcwcp from package 3.5.1 and earlier. */ cwt_retv legacy_api_test_rec_poll(cw_test_executor_t * cte); #endif /* #ifndef _LIBCW_LEGACY_TESTS_REC_POLL_H_ */ unixcw-3.6.0/src/libcw/tests/libcw_test_quick.sh0000755000175000017500000000010314000344554016672 00000000000000#!/bin/sh ./libcw_tests -Q -S n -L 1 | grep "Test result: success" unixcw-3.6.0/src/libcw/tests/test_framework.c0000644000175000017500000012312714000344554016214 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** \file test_framework.c \brief Test framework for libcw test code */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifndef __FreeBSD__ #include #endif #if defined(HAVE_STRING_H) #include #endif #if defined(HAVE_STRINGS_H) #include #endif #include "libcw.h" #include "libcw_debug.h" #include "cw_cmdline.h" #include "test_framework.h" #include "test_framework_tools.h" /* Make pause between tests. Let the resources measurement tool go back to zero, so that e.g. high CPU usage in test N is visible only in that test, but not in test N+1 that will be executed right after test N. */ #define LIBCW_TEST_INTER_TEST_PAUSE_MSECS (2 * LIBCW_TEST_MEAS_CPU_MEAS_INTERVAL_MSECS) static bool cw_test_expect_op_int(struct cw_test_executor_t * self, int expected_value, const char * operator, int received_value, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); static bool cw_test_expect_op_int_errors_only(struct cw_test_executor_t * self, int expected_value, const char * operator, int received_value, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); static bool cw_test_expect_op_int_sub(struct cw_test_executor_t * self, int expected_value, const char * operator, int received_value, bool errors_only, const char * va_buf); static bool cw_test_expect_op_float(struct cw_test_executor_t * self, float expected_value, const char * operator, float received_value, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); static bool cw_test_expect_op_float_errors_only(struct cw_test_executor_t * self, float expected_value, const char * operator, float received_value, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); static bool cw_test_expect_op_float_sub(struct cw_test_executor_t * self, float expected_value, const char * operator, float received_value, bool errors_only, const char * va_buf); static bool cw_test_expect_between_int(struct cw_test_executor_t * self, int expected_lower, int received_value, int expected_higher, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); static bool cw_test_expect_between_int_errors_only(struct cw_test_executor_t * self, int expected_lower, int received_value, int expected_higher, const char * fmt, ...) __attribute__ ((format (printf, 5, 6))); static bool cw_test_expect_null_pointer(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); static bool cw_test_expect_null_pointer_errors_only(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); static bool cw_test_expect_valid_pointer(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); static bool cw_test_expect_valid_pointer_errors_only(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); static void cw_assert2(struct cw_test_executor_t * self, bool condition, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); static void cw_test_print_test_header(cw_test_executor_t * self, const char * fmt, ...); static void cw_test_print_test_footer(cw_test_executor_t * self, const char * test_name); static void cw_test_append_status_string(cw_test_executor_t * self, char * msg_buf, int n, const char * status_string); static cwt_retv cw_test_process_args(cw_test_executor_t * self, int argc, char * const argv[]); static int cw_test_get_loops_count(cw_test_executor_t * self); static int cw_test_fill_default_sound_systems_and_topics(cw_test_executor_t * self); static void cw_test_print_test_options(cw_test_executor_t * self); static bool cw_test_test_topic_was_requested(cw_test_executor_t * self, int libcw_test_topic); static bool cw_test_sound_system_was_requested(cw_test_executor_t * self, cw_sound_system sound_system); static const char * cw_test_get_current_topic_label(cw_test_executor_t * self); static const char * cw_test_get_current_sound_system_label(cw_test_executor_t * self); static const char * cw_test_get_current_sound_device(cw_test_executor_t * self); static void cw_test_set_current_topic_and_gen_config(cw_test_executor_t * self, int topic, int sound_system); static void cw_test_print_test_stats(cw_test_executor_t * self); static int cw_test_log_info(struct cw_test_executor_t * self, const char * fmt, ...) __attribute__ ((format (printf, 2, 3))); static void cw_test_log_info_cont(struct cw_test_executor_t * self, const char * fmt, ...) __attribute__ ((format (printf, 2, 3))); static void cw_test_flush_info(struct cw_test_executor_t * self); static void cw_test_log_error(struct cw_test_executor_t * self, const char * fmt, ...) __attribute__ ((format (printf, 2, 3))); static void cw_test_print_sound_systems(cw_test_executor_t * self, cw_sound_system * sound_systems, int max); static void cw_test_print_topics(cw_test_executor_t * self, int * topics, int max); static bool cw_test_test_topic_is_member(cw_test_executor_t * cte, int topic, int * topics, int max); static bool cw_test_sound_system_is_member(cw_test_executor_t * cte, cw_sound_system sound_system, cw_sound_system * sound_systems, int max); static cwt_retv cw_test_main_test_loop(cw_test_executor_t * cte, cw_test_set_t * test_sets); static unsigned int cw_test_get_total_errors_count(cw_test_executor_t * cte); static cwt_retv iterate_over_topics(cw_test_executor_t * cte, cw_test_set_t * test_set); static cwt_retv iterate_over_sound_systems(cw_test_executor_t * cte, cw_test_set_t * test_set, int topic); static cwt_retv iterate_over_test_objects(cw_test_executor_t * cte, cw_test_object_t * test_objects, int topic, cw_sound_system sound_system); /** \brief Set default contents of cw_test_executor_t::config::tested_sound_systems[] and cw_test_executor_t::config::tested_areas[] One or both sets of defaults will be used if related argument was not used in command line. When during preparation of default set of sound system we detect that some sound set is not available, we will not include it in set of default sound systems. This is a private function so it is not put into cw_test_executor_t class. */ int cw_test_fill_default_sound_systems_and_topics(cw_test_executor_t * self) { /* NULL means "use default device" for every sound system */ const char * default_device = NULL; int dest_idx = 0; if (cw_is_null_possible(default_device)) { self->config->tested_sound_systems[dest_idx] = CW_AUDIO_NULL; dest_idx++; } else { self->log_info(self, "Null sound system is not available on this machine - will skip it\n"); } if (cw_is_console_possible(default_device)) { self->config->tested_sound_systems[dest_idx] = CW_AUDIO_CONSOLE; dest_idx++; } else { self->log_info(self, "Console sound system is not available on this machine - will skip it\n"); } if (cw_is_oss_possible(default_device)) { self->config->tested_sound_systems[dest_idx] = CW_AUDIO_OSS; dest_idx++; } else { self->log_info(self, "OSS sound system is not available on this machine - will skip it\n"); } if (cw_is_alsa_possible(default_device)) { self->config->tested_sound_systems[dest_idx] = CW_AUDIO_ALSA; dest_idx++; } else { self->log_info(self, "ALSA sound system is not available on this machine - will skip it\n"); } if (cw_is_pa_possible(default_device)) { self->config->tested_sound_systems[dest_idx] = CW_AUDIO_PA; dest_idx++; } else { self->log_info(self, "PulseAudio sound system is not available on this machine - will skip it\n"); } self->config->tested_sound_systems[dest_idx] = CW_AUDIO_NONE; /* Guard. */ self->config->tested_areas[0] = LIBCW_TEST_TOPIC_TQ; self->config->tested_areas[1] = LIBCW_TEST_TOPIC_GEN; self->config->tested_areas[2] = LIBCW_TEST_TOPIC_KEY; self->config->tested_areas[3] = LIBCW_TEST_TOPIC_REC; self->config->tested_areas[4] = LIBCW_TEST_TOPIC_DATA; self->config->tested_areas[5] = LIBCW_TEST_TOPIC_OTHER; self->config->tested_areas[6] = LIBCW_TEST_TOPIC_MAX; /* Guard element. */ return 0; } cwt_retv cw_test_process_args(cw_test_executor_t * self, int argc, char * const argv[]) { cw_test_fill_default_sound_systems_and_topics(self); if (argc == 1) { /* Use defaults configured by cw_test_fill_default_sound_systems_and_topics(). */ self->random_seed = time(0); srand48(self->random_seed); return cwt_retv_ok; } if (CW_SUCCESS != cw_process_program_arguments(argc, argv, self->config)) { return cwt_retv_err; } if (self->config->test_random_seed > 0) { self->random_seed = self->config->test_random_seed; } else { struct timeval tv; gettimeofday(&tv, NULL); self->random_seed = (long int) tv.tv_sec; } srand48(self->random_seed); return cwt_retv_ok; } static int cw_test_get_loops_count(cw_test_executor_t * self) { return self->config->test_loops; } bool cw_test_expect_op_int(struct cw_test_executor_t * self, int expected_value, const char * operator, int received_value, const char * fmt, ...) { char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); /* FIXME: this vsnprintf() introduces large delays when running tests under valgrind/callgrind. */ vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); return cw_test_expect_op_int_sub(self, expected_value, operator, received_value, false, va_buf); } bool cw_test_expect_op_int_errors_only(struct cw_test_executor_t * self, int expected_value, const char * operator, int received_value, const char * fmt, ...) { char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); /* FIXME: this vsnprintf() introduces large delays when running tests under valgrind/callgrind. */ vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); return cw_test_expect_op_int_sub(self, expected_value, operator, received_value, true, va_buf); } static bool cw_test_expect_op_int_sub(struct cw_test_executor_t * self, int expected_value, const char * operator, int received_value, bool errors_only, const char * va_buf) { bool as_expected = false; char msg_buf[1024] = { 0 }; /* FIXME: these snprintf() call introduce large delays when running tests under valgrind/callgrind. */ int n = snprintf(msg_buf, sizeof (msg_buf), "%s", self->msg_prefix); const int message_len = n + snprintf(msg_buf + n, sizeof (msg_buf) - n, "%s", va_buf); n += snprintf(msg_buf + n, sizeof (msg_buf) - n, "%-*s", (self->console_n_cols - n), va_buf); bool success = false; if (operator[0] == '=' && operator[1] == '=') { success = expected_value == received_value; } else if (operator[0] == '<' && operator[1] == '=') { success = expected_value <= received_value; } else if (operator[0] == '>' && operator[1] == '=') { success = expected_value >= received_value; } else if (operator[0] == '!' && operator[1] == '=') { success = expected_value != received_value; } else if (operator[0] == '<' && operator[1] == '\0') { success = expected_value < received_value; } else if (operator[0] == '>' && operator[1] == '\0') { success = expected_value > received_value; } else { self->log_error(self, "Unhandled operator '%s'\n", operator); assert(0); } if (success) { if (!errors_only) { self->stats->successes++; /* FIXME: believe it or not, this line introduces large delays when running tests under valgrind/callgrind. */ cw_test_append_status_string(self, msg_buf, message_len, "[ OK ]"); self->log_info(self, "%s\n", msg_buf); } as_expected = true; } else { self->stats->failures++; cw_test_append_status_string(self, msg_buf, message_len, "[FAIL]"); self->log_error(self, "%s\n", msg_buf); self->log_error(self, " *** expected %d, got %d ***\n", expected_value, received_value); as_expected = false; } return as_expected; } bool cw_test_expect_op_float(struct cw_test_executor_t * self, float expected_value, const char * operator, float received_value, const char * fmt, ...) { char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); return cw_test_expect_op_float_sub(self, expected_value, operator, received_value, false, va_buf); } bool cw_test_expect_op_float_errors_only(struct cw_test_executor_t * self, float expected_value, const char * operator, float received_value, const char * fmt, ...) { char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); return cw_test_expect_op_float_sub(self, expected_value, operator, received_value, true, va_buf); } static bool cw_test_expect_op_float_sub(struct cw_test_executor_t * self, float expected_value, const char * operator, float received_value, bool errors_only, const char * va_buf) { char msg_buf[1024] = { 0 }; int n = snprintf(msg_buf, sizeof (msg_buf), "%s", self->msg_prefix); const int message_len = n + snprintf(msg_buf + n, sizeof (msg_buf) - n, "%s", va_buf); n += snprintf(msg_buf + n, sizeof (msg_buf) - n, "%-*s", (self->console_n_cols - n), va_buf); bool success = false; if (operator[0] == '<' && operator[1] == '\0') { success = expected_value < received_value; } else if (operator[0] == '>' && operator[1] == '\0') { success = expected_value > received_value; } else { self->log_error(self, "Unhandled operator '%s'\n", operator); assert(0); } bool as_expected = false; if (success) { if (!errors_only) { self->stats->successes++; cw_test_append_status_string(self, msg_buf, message_len, "[ OK ]"); self->log_info(self, "%s\n", msg_buf); } as_expected = true; } else { self->stats->failures++; cw_test_append_status_string(self, msg_buf, message_len, "[FAIL]"); self->log_error(self, "%s\n", msg_buf); self->log_error(self, " *** expected %f, got %f ***\n", (double) expected_value, (double) received_value); as_expected = false; } return as_expected; } /** @brief Append given status string at the end of buffer, but within cw_test::console_n_cols limit This is a private function so it is not put into cw_test_executor_t class. */ void cw_test_append_status_string(cw_test_executor_t * self, char * msg_buf, int n, const char * status_string) { const char * separator = " "; /* Separator between test message and test status string, for better visibility of status string. */ const size_t space_left = self->console_n_cols - n; if (space_left > strlen(separator) + strlen(status_string)) { sprintf(msg_buf + self->console_n_cols - strlen(separator) - strlen(status_string), "%s%s", separator, status_string); } else { sprintf(msg_buf + self->console_n_cols - strlen("...") - strlen(separator) - strlen(status_string), "...%s%s", separator, status_string); } } bool cw_test_expect_between_int(struct cw_test_executor_t * self, int expected_lower, int received_value, int expected_higher, const char * fmt, ...) { bool as_expected = true; char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); char msg_buf[1024] = { 0 }; int n = snprintf(msg_buf, sizeof (msg_buf), "%s", self->msg_prefix); const int message_len = n + snprintf(msg_buf + n, sizeof (msg_buf) - n, "%s", va_buf); n += snprintf(msg_buf + n, sizeof (msg_buf) - n, "%-*s", (self->console_n_cols - n), va_buf); if (expected_lower <= received_value && received_value <= expected_higher) { self->stats->successes++; cw_test_append_status_string(self, msg_buf, message_len, "[ OK ]"); //self->log_info(self, "%s\n", msg_buf); self->log_info(self, "%s %d %d %d\n", msg_buf, expected_lower, received_value, expected_higher); as_expected = true; } else { self->stats->failures++; cw_test_append_status_string(self, msg_buf, message_len, "[FAIL]"); self->log_error(self, "%s\n", msg_buf); self->log_error(self, " *** expected within %d-%d, got %d ***\n", expected_lower, expected_higher, received_value); as_expected = false; } return as_expected; } bool cw_test_expect_between_int_errors_only(struct cw_test_executor_t * self, int expected_lower, int received_value, int expected_higher, const char * fmt, ...) { bool as_expected = true; char buf[128] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof (buf), fmt, ap); va_end(ap); if (expected_lower <= received_value && received_value <= expected_higher) { as_expected = true; } else { const int n = fprintf(self->file_err, "%s%s", self->msg_prefix, buf); self->stats->failures++; self->log_error(self, "%*s", self->console_n_cols - n, "failure: "); self->log_error(self, "expected value within %d-%d, got %d\n", expected_lower, expected_higher, received_value); as_expected = false; } return as_expected; } bool cw_test_expect_null_pointer(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) { bool as_expected = false; char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); char msg_buf[1024] = { 0 }; int n = snprintf(msg_buf, sizeof (msg_buf), "%s", self->msg_prefix); const int message_len = n + snprintf(msg_buf + n, sizeof (msg_buf) - n, "%s", va_buf); n += snprintf(msg_buf + n, sizeof (msg_buf) - n, "%-*s", (self->console_n_cols - n), va_buf); if (NULL == pointer) { self->stats->successes++; cw_test_append_status_string(self, msg_buf, message_len, "[ OK ]"); self->log_info(self, "%s\n", msg_buf); as_expected = true; } else { self->stats->failures++; cw_test_append_status_string(self, msg_buf, message_len, "[FAIL]"); self->log_error(self, "%s\n", msg_buf); self->log_error(self, " *** expected NULL, got %p ***\n", pointer); as_expected = false; } return as_expected; } bool cw_test_expect_null_pointer_errors_only(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) { bool as_expected = false; char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); char msg_buf[1024] = { 0 }; int n = snprintf(msg_buf, sizeof (msg_buf), "%s", self->msg_prefix); const int message_len = n + snprintf(msg_buf + n, sizeof (msg_buf) - n, "%s", va_buf); n += snprintf(msg_buf + n, sizeof (msg_buf) - n, "%-*s", (self->console_n_cols - n), va_buf); if (NULL == pointer) { as_expected = true; } else { self->stats->failures++; cw_test_append_status_string(self, msg_buf, message_len, "[FAIL]"); self->log_error(self, "%s\n", msg_buf); self->log_error(self, " *** expected NULL, got %p ***\n", pointer); as_expected = false; } return as_expected; } bool cw_test_expect_valid_pointer(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) { bool as_expected = false; char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); char msg_buf[1024] = { 0 }; int n = snprintf(msg_buf, sizeof (msg_buf), "%s", self->msg_prefix); const int message_len = n + snprintf(msg_buf + n, sizeof (msg_buf) - n, "%s", va_buf); n += snprintf(msg_buf + n, sizeof (msg_buf) - n, "%-*s", (self->console_n_cols - n), va_buf); if (NULL != pointer) { self->stats->successes++; cw_test_append_status_string(self, msg_buf, message_len, "[ OK ]"); self->log_info(self, "%s\n", msg_buf); as_expected = true; } else { self->stats->failures++; cw_test_append_status_string(self, msg_buf, message_len, "[FAIL]"); self->log_error(self, "%s\n", msg_buf); self->log_error(self, " *** expected valid pointer, got NULL ***\n"); as_expected = false; } return as_expected; } bool cw_test_expect_valid_pointer_errors_only(struct cw_test_executor_t * self, const void * pointer, const char * fmt, ...) { bool as_expected = false; char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); char msg_buf[1024] = { 0 }; int n = snprintf(msg_buf, sizeof (msg_buf), "%s", self->msg_prefix); const int message_len = n + snprintf(msg_buf + n, sizeof (msg_buf) - n, "%s", va_buf); n += snprintf(msg_buf + n, sizeof (msg_buf) - n, "%-*s", (self->console_n_cols - n), va_buf); if (NULL != pointer) { as_expected = true; } else { self->stats->failures++; cw_test_append_status_string(self, msg_buf, message_len, "[FAIL]"); self->log_error(self, "%s\n", msg_buf); self->log_error(self, " *** expected valid pointer, got NULL ***\n"); as_expected = false; } return as_expected; } void cw_assert2(struct cw_test_executor_t * self, bool condition, const char * fmt, ...) { if (!condition) { char va_buf[128] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); self->log_error(self, "Assertion failed: %s\n", va_buf); exit(EXIT_FAILURE); } return; } bool cw_test_test_topic_was_requested(cw_test_executor_t * self, int libcw_test_topic) { const int n = sizeof (self->config->tested_areas) / sizeof (self->config->tested_areas[0]); switch (libcw_test_topic) { case LIBCW_TEST_TOPIC_TQ: case LIBCW_TEST_TOPIC_GEN: case LIBCW_TEST_TOPIC_KEY: case LIBCW_TEST_TOPIC_REC: case LIBCW_TEST_TOPIC_DATA: case LIBCW_TEST_TOPIC_OTHER: for (int i = 0; i < n; i++) { if (LIBCW_TEST_TOPIC_MAX == self->config->tested_areas[i]) { /* Found guard element. */ return false; } if (libcw_test_topic == self->config->tested_areas[i]) { return true; } } return false; case LIBCW_TEST_TOPIC_MAX: default: fprintf(stderr, "Unexpected test topic %d\n", libcw_test_topic); exit(EXIT_FAILURE); } } bool cw_test_sound_system_was_requested(cw_test_executor_t * self, cw_sound_system sound_system) { const int n = sizeof (self->config->tested_sound_systems) / sizeof (self->config->tested_sound_systems[0]); switch (sound_system) { case CW_AUDIO_NULL: case CW_AUDIO_CONSOLE: case CW_AUDIO_OSS: case CW_AUDIO_ALSA: case CW_AUDIO_PA: for (int i = 0; i < n; i++) { if (CW_AUDIO_NONE == self->config->tested_sound_systems[i]) { /* Found guard element. */ return false; } if (sound_system == self->config->tested_sound_systems[i]) { return true; } } return false; case CW_AUDIO_NONE: case CW_AUDIO_SOUNDCARD: default: fprintf(stderr, "Unexpected sound system %d\n", sound_system); exit(EXIT_FAILURE); } } void cw_test_print_test_header(cw_test_executor_t * self, const char * fmt, ...) { self->log_info_cont(self, "\n"); self->log_info(self, "Beginning of test\n"); { self->log_info(self, " "); for (size_t i = 0; i < self->console_n_cols - (strlen ("[II] ")); i++) { self->log_info_cont(self, "-"); } self->log_info_cont(self, "\n"); } char va_buf[256] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); self->log_info(self, "Test name: %s\n", va_buf); self->log_info(self, "Current test topic: %s\n", self->get_current_topic_label(self)); self->log_info(self, "Current sound system: %s\n", self->get_current_sound_system_label(self)); self->log_info(self, "Current sound device: '%s'\n", self->get_current_sound_device(self)); { self->log_info(self, " "); for (size_t i = 0; i < self->console_n_cols - (strlen ("[II] ")); i++) { self->log_info_cont(self, "-"); } self->log_info_cont(self, "\n"); } } void cw_test_print_test_footer(cw_test_executor_t * self, const char * test_name) { self->log_info(self, "End of test: %s\n", test_name); } const char * cw_test_get_current_sound_system_label(cw_test_executor_t * self) { return cw_get_audio_system_label(self->current_gen_conf.sound_system); } const char * cw_test_get_current_sound_device(cw_test_executor_t * self) { return self->current_gen_conf.sound_device; } const char * cw_test_get_current_topic_label(cw_test_executor_t * self) { switch (self->current_topic) { case LIBCW_TEST_TOPIC_TQ: return "tq"; case LIBCW_TEST_TOPIC_GEN: return "gen"; case LIBCW_TEST_TOPIC_KEY: return "key"; case LIBCW_TEST_TOPIC_REC: return "rec"; case LIBCW_TEST_TOPIC_DATA: return "data"; case LIBCW_TEST_TOPIC_OTHER: return "other"; default: return "*** unknown ***"; } } /** @brief Set a test topic and sound system that is about to be tested This is a private function so it is not put into cw_test_executor_t class. Call this function before calling each test function. @p topic and @p sound_system values to be passed to this function should be taken from the same test set that the test function is taken. */ static void cw_test_set_current_topic_and_gen_config(cw_test_executor_t * self, int topic, int sound_system) { self->current_topic = topic; self->current_gen_conf.sound_system = sound_system; /* TODO: we have to somehow organize copying of these values from program config to test executor config. For now this is ad-hoc solution. */ self->current_gen_conf.alsa_period_size = self->config->gen_conf.alsa_period_size; self->current_gen_conf.sound_device[0] = '\0'; /* Clear value from previous run of test. */ switch (self->current_gen_conf.sound_system) { case CW_AUDIO_ALSA: if ('\0' != self->config->test_alsa_device_name[0]) { snprintf(self->current_gen_conf.sound_device, sizeof (self->current_gen_conf.sound_device), "%s", self->config->test_alsa_device_name); } break; case CW_AUDIO_NULL: case CW_AUDIO_CONSOLE: case CW_AUDIO_OSS: case CW_AUDIO_PA: /* We don't have a buffer with device name for this sound system. */ break; case CW_AUDIO_NONE: case CW_AUDIO_SOUNDCARD: default: /* Technically speaking this is an error, but we shouldn't get here because test binary won't accept such sound systems through command line. */ break; } self->stats = &self->all_stats[sound_system][topic]; } void cw_test_print_test_stats(cw_test_executor_t * self) { const char sound_systems[] = " NCOAP"; fprintf(self->file_err, "\n\nlibcw tests: Statistics of tests (failures/total)\n\n"); // 12345 12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 #define SEPARATOR_LINE " --+-----------+-----------+-----------+-----------+-----------+-----------+\n" #define FRONT_FORMAT "%s %c |" #define BACK_FORMAT "%s\n" #define CELL_FORMAT_D "% 10d |" #define CELL_FORMAT_S "%10s |" fprintf(self->file_err, " | tone queue| generator | key | receiver | data | other |\n"); fprintf(self->file_err, "%s", SEPARATOR_LINE); for (int sound = CW_AUDIO_NULL; sound <= CW_AUDIO_PA; sound++) { /* If a row with error counter has non-zero values, use arrows at the beginning and end of the row to highlight/indicate row that has non-zero error counters. We want the errors to be visible and stand out. */ char error_indicator_empty[3] = " "; char error_indicator_front[3] = " "; char error_indicator_back[3] = " "; { bool has_errors = false; for (int topic = 0; topic < LIBCW_TEST_TOPIC_MAX; topic++) { if (self->all_stats[sound][topic].failures) { has_errors = true; break; } } if (has_errors) { snprintf(error_indicator_front, sizeof (error_indicator_front), "%s", "->"); snprintf(error_indicator_back, sizeof (error_indicator_back), "%s", "<-"); } } /* Print line with errors. Print numeric values only if some tests for given combination of sound system/topic were performed. */ fprintf(self->file_err, FRONT_FORMAT, error_indicator_front, sound_systems[sound]); for (int topic = 0; topic < LIBCW_TEST_TOPIC_MAX; topic++) { int total = self->all_stats[sound][topic].failures + self->all_stats[sound][topic].successes; int failures = self->all_stats[sound][topic].failures; if (0 == total && 0 == failures) { fprintf(self->file_err, CELL_FORMAT_S, " "); } else { fprintf(self->file_err, CELL_FORMAT_D, failures); } } fprintf(self->file_err, BACK_FORMAT, error_indicator_back); /* Print line with totals. Print numeric values only if some tests for given combination of sound system/topic were performed. */ fprintf(self->file_err, FRONT_FORMAT, error_indicator_empty, sound_systems[sound]); for (int topic = 0; topic < LIBCW_TEST_TOPIC_MAX; topic++) { int total = self->all_stats[sound][topic].failures + self->all_stats[sound][topic].successes; int failures = self->all_stats[sound][topic].failures; if (0 == total && 0 == failures) { fprintf(self->file_err, CELL_FORMAT_S, " "); } else { fprintf(self->file_err, CELL_FORMAT_D, total); } } fprintf(self->file_err, BACK_FORMAT, error_indicator_empty); fprintf(self->file_err, "%s", SEPARATOR_LINE); } #ifndef __FreeBSD__ struct sysinfo sys_info; sysinfo(&sys_info); self->uptime_end = sys_info.uptime; const long test_duration = self->uptime_end - self->uptime_begin; fprintf(self->file_err, "Duration of tests = %ld minutes, %ld seconds\n", test_duration / 60, test_duration % 60); #endif return; } void cw_test_init(cw_test_executor_t * self, FILE * stdout, FILE * stderr, const char * msg_prefix) { memset(self, 0, sizeof (cw_test_executor_t)); self->config = cw_config_new("libcw tests"); self->file_out = stdout; self->file_err = stderr; self->use_resource_meas = false; self->expect_op_int = cw_test_expect_op_int; self->expect_op_int_errors_only = cw_test_expect_op_int_errors_only; self->expect_op_float = cw_test_expect_op_float; self->expect_op_float_errors_only = cw_test_expect_op_float_errors_only; self->expect_between_int = cw_test_expect_between_int; self->expect_between_int_errors_only = cw_test_expect_between_int_errors_only; self->expect_null_pointer = cw_test_expect_null_pointer; self->expect_null_pointer_errors_only = cw_test_expect_null_pointer_errors_only; self->expect_valid_pointer = cw_test_expect_valid_pointer; self->expect_valid_pointer_errors_only = cw_test_expect_valid_pointer_errors_only; self->assert2 = cw_assert2; self->print_test_header = cw_test_print_test_header; self->print_test_footer = cw_test_print_test_footer; self->process_args = cw_test_process_args; self->get_loops_count = cw_test_get_loops_count; self->print_test_options = cw_test_print_test_options; self->test_topic_was_requested = cw_test_test_topic_was_requested; self->sound_system_was_requested = cw_test_sound_system_was_requested; self->get_current_topic_label = cw_test_get_current_topic_label; self->get_current_sound_system_label = cw_test_get_current_sound_system_label; self->get_current_sound_device = cw_test_get_current_sound_device; self->print_test_stats = cw_test_print_test_stats; self->log_info = cw_test_log_info; self->log_info_cont = cw_test_log_info_cont; self->flush_info = cw_test_flush_info; self->log_error = cw_test_log_error; self->main_test_loop = cw_test_main_test_loop; self->get_total_errors_count = cw_test_get_total_errors_count; self->console_n_cols = default_cw_test_print_n_chars; snprintf(self->msg_prefix, sizeof (self->msg_prefix), "%s: ", msg_prefix); } void cw_test_deinit(cw_test_executor_t * self) { cw_config_delete(&self->config); } int cw_test_log_info(struct cw_test_executor_t * self, const char * fmt, ...) { if (NULL == self->file_out) { return 0; } char va_buf[256] = { 0 }; va_list ap; va_start(ap, fmt); /* FIXME: this vsnprintf() introduces *some* delays when running tests under valgrind/callgrind. Fixing this FIXME will have very small impact, so try this as last. */ vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); const int n = fprintf(self->file_out, "[II] %s", va_buf); fflush(self->file_out); return n; } void cw_test_log_info_cont(struct cw_test_executor_t * self, const char * fmt, ...) { if (NULL == self->file_out) { return; } char va_buf[256] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); fprintf(self->file_out, "%s", va_buf); fflush(self->file_out); return; } void cw_test_flush_info(struct cw_test_executor_t * self) { if (NULL == self->file_out) { return; } fflush(self->file_out); return; } void cw_test_log_error(struct cw_test_executor_t * self, const char * fmt, ...) { if (NULL == self->file_out) { return; } char va_buf[256] = { 0 }; va_list ap; va_start(ap, fmt); vsnprintf(va_buf, sizeof (va_buf), fmt, ap); va_end(ap); fprintf(self->file_out, "[EE] %s", va_buf); fflush(self->file_out); return; } /** @brief Print labels of sound systems specified by @param sound_systems array There are no more than @param max items in @param sound_systems vector. CW_AUDIO_NONE is considered a guard element. Function stops either after processing @param max elements, or at guard element (without printing label for the guard element) - whichever comes first. */ void cw_test_print_sound_systems(cw_test_executor_t * self, cw_sound_system * sound_systems, int max) { for (int i = 0; i < max; i++) { if (CW_AUDIO_NONE == sound_systems[i]) { /* Found guard element. */ return; } if (CW_AUDIO_SOUNDCARD == sound_systems[i]) { /* Catch-all value that shouldn't be used in tests. */ continue; } switch (sound_systems[i]) { case CW_AUDIO_NULL: self->log_info_cont(self, "null "); break; case CW_AUDIO_CONSOLE: self->log_info_cont(self, "console "); break; case CW_AUDIO_OSS: self->log_info_cont(self, "OSS "); break; case CW_AUDIO_ALSA: self->log_info_cont(self, "ALSA "); break; case CW_AUDIO_PA: self->log_info_cont(self, "PulseAudio "); break; case CW_AUDIO_NONE: case CW_AUDIO_SOUNDCARD: /* Handled in 'if' before this switch. */ break; default: self->log_info_cont(self, "unknown! "); break; } } return; } /** @brief Print labels of test topics specified by @param topics array There are no more than @param max items in @param topics vector. LIBCW_TEST_TOPIC_MAX is considered a guard element. Function stops either after processing @param max elements, or at guard element (without printing label for the guard element) - whichever comes first. */ void cw_test_print_topics(cw_test_executor_t * self, int * topics, int max) { for (int i = 0; i < max; i++) { if (LIBCW_TEST_TOPIC_MAX == topics[i]) { /* Found guard element. */ return; } switch (topics[i]) { case LIBCW_TEST_TOPIC_TQ: self->log_info_cont(self, "tq "); break; case LIBCW_TEST_TOPIC_GEN: self->log_info_cont(self, "gen "); break; case LIBCW_TEST_TOPIC_KEY: self->log_info_cont(self, "key "); break; case LIBCW_TEST_TOPIC_REC: self->log_info_cont(self, "rec "); break; case LIBCW_TEST_TOPIC_DATA: self->log_info_cont(self, "data "); break; case LIBCW_TEST_TOPIC_OTHER: self->log_info_cont(self, "other "); break; default: self->log_info_cont(self, "unknown! "); break; } } self->log_info_cont(self, "\n"); return; } void cw_test_print_test_options(cw_test_executor_t * self) { self->log_info(self, "Sound systems that will be tested: "); cw_test_print_sound_systems(self, self->config->tested_sound_systems, sizeof (self->config->tested_sound_systems) / sizeof (self->config->tested_sound_systems[0])); self->log_info_cont(self, "\n"); self->log_info(self, "Areas that will be tested: "); cw_test_print_topics(self, self->config->tested_areas, sizeof (self->config->tested_areas) / sizeof (self->config->tested_areas[0])); self->log_info_cont(self, "\n"); self->log_info(self, "Random seed = %ld\n", self->random_seed); if (strlen(self->config->test_function_name)) { self->log_info(self, "Single function to be tested: '%s'\n", self->config->test_function_name); } fflush(self->file_out); } /** @brief See if given @param topic is a member of given list of test topics @param topics The size of @param topics is specified by @param max. */ bool cw_test_test_topic_is_member(__attribute__((unused)) cw_test_executor_t * cte, int topic, int * topics, int max) { for (int i = 0; i < max; i++) { if (LIBCW_TEST_TOPIC_MAX == topics[i]) { /* Found guard element. */ return false; } if (topic == topics[i]) { return true; } } return false; } /** @brief See if given @param sound_system is a member of given list of test topics @param sound_system The size of @param sound_system is specified by @param max. */ bool cw_test_sound_system_is_member(__attribute__((unused)) cw_test_executor_t * cte, cw_sound_system sound_system, cw_sound_system * sound_systems, int max) { for (int i = 0; i < max; i++) { if (CW_AUDIO_NONE == sound_systems[i]) { /* Found guard element. */ return false; } if (sound_system == sound_systems[i]) { return true; } } return false; } cwt_retv cw_test_main_test_loop(cw_test_executor_t * cte, cw_test_set_t * test_sets) { #ifndef __FreeBSD__ struct sysinfo sys_info; sysinfo(&sys_info); cte->uptime_begin = sys_info.uptime; #endif int set = 0; while (LIBCW_TEST_SET_VALID == test_sets[set].set_valid) { cw_test_set_t * test_set = &test_sets[set]; if (cwt_retv_ok != iterate_over_topics(cte, test_set)) { cte->log_error(cte, "Test framework failed for set %d\n", set); return cwt_retv_err; } set++; } return cwt_retv_ok; } static cwt_retv iterate_over_topics(cw_test_executor_t * cte, cw_test_set_t * test_set) { for (int topic = LIBCW_TEST_TOPIC_TQ; topic < LIBCW_TEST_TOPIC_MAX; topic++) { if (!cte->test_topic_was_requested(cte, topic)) { continue; } const int topics_max = sizeof (test_set->tested_areas) / sizeof (test_set->tested_areas[0]); if (!cw_test_test_topic_is_member(cte, topic, test_set->tested_areas, topics_max)) { continue; } if (cwt_retv_ok != iterate_over_sound_systems(cte, test_set, topic)) { cte->log_error(cte, "Test framework failed for topic %d\n", topic); return cwt_retv_err; } } return cwt_retv_ok; } static cwt_retv iterate_over_sound_systems(cw_test_executor_t * cte, cw_test_set_t * test_set, int topic) { for (cw_sound_system sound_system = CW_SOUND_SYSTEM_FIRST; sound_system <= CW_SOUND_SYSTEM_LAST; sound_system++) { if (!cte->sound_system_was_requested(cte, sound_system)) { continue; } const int systems_max = sizeof (test_set->tested_sound_systems) / sizeof (test_set->tested_sound_systems[0]); if (!cw_test_sound_system_is_member(cte, sound_system, test_set->tested_sound_systems, systems_max)) { continue; } if (cwt_retv_ok != iterate_over_test_objects(cte, test_set->test_objects, topic, sound_system)) { cte->log_error(cte, "Test framework failed for topic %d, sound system %d\n", topic, sound_system); return cwt_retv_err; } } return cwt_retv_ok; } static cwt_retv iterate_over_test_objects(cw_test_executor_t * cte, cw_test_object_t * test_objects, int topic, cw_sound_system sound_system) { for (cw_test_object_t * test_obj = test_objects; NULL != test_obj->test_function; test_obj++) { bool execute = true; if (0 != strlen(cte->config->test_function_name)) { if (0 != strcmp(cte->config->test_function_name, test_obj->name)) { execute = false; } } if (cte->config->test_quick_only && !test_obj->is_quick) { continue; } if (!execute) { continue; } if (cte->use_resource_meas) { /* Starting measurement right before it has something to measure. Starting measurements resets old results from previous measurement. This is significant when we want to reset 'max resources' value - we want to measure the 'max resources' value only per test object, not per whole test set. */ resource_meas_start(&cte->resource_meas, LIBCW_TEST_MEAS_CPU_MEAS_INTERVAL_MSECS); } cw_test_set_current_topic_and_gen_config(cte, topic, sound_system); //fprintf(stderr, "+++ %s +++\n", test_obj->name); const cwt_retv retv = test_obj->test_function(cte); if (cte->use_resource_meas) { usleep(1000 * LIBCW_TEST_INTER_TEST_PAUSE_MSECS); /* First stop the test, then display CPU usage summary. Otherwise it may happen that the summary will say that max CPU usage during test was zero, but then the meas object will take the last measurement, detect high CPU usage, and will display the high CPU usage information *after* the summary. */ resource_meas_stop(&cte->resource_meas); const int current_cpu_usage = resource_meas_get_current_cpu_usage(&cte->resource_meas); const int max_cpu_usage = resource_meas_get_maximal_cpu_usage(&cte->resource_meas); cte->log_info(cte, "CPU usage: last = "CWTEST_CPU_FMT", max = "CWTEST_CPU_FMT"\n", current_cpu_usage, max_cpu_usage); if (max_cpu_usage > LIBCW_TEST_MEAS_CPU_OK_THRESHOLD_PERCENT) { cte->stats->failures++; cte->log_error(cte, "Registered high CPU usage "CWTEST_CPU_FMT" during execution of '%s'\n", max_cpu_usage, test_obj->name); } } if (cwt_retv_ok != retv) { return cwt_retv_err; } } return cwt_retv_ok; } unsigned int cw_test_get_total_errors_count(cw_test_executor_t * cte) { unsigned int result = 0; for (cw_sound_system sound_system = CW_AUDIO_NULL; sound_system <= CW_AUDIO_PA; sound_system++) { for (int topic = 0; topic < LIBCW_TEST_TOPIC_MAX; topic++) { result += cte->all_stats[sound_system][topic].failures; } } return result; } unixcw-3.6.0/src/libcw/tests/libcw_data_tests.c0000644000175000017500000007335414000344554016501 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include "libcw.h" #include "libcw2.h" #include "libcw_data.h" #include "libcw_data_tests.h" #include "libcw_debug.h" #include "libcw_key.h" #include "libcw_utils.h" #include "test_framework.h" /* For maximum length of 7, there should be 254 items: 2^1 + 2^2 + 2^3 + ... + 2^7 */ #define REPRESENTATION_TABLE_SIZE ((1 << (CW_DATA_MAX_REPRESENTATION_LENGTH + 1)) - 2) extern const cw_entry_t CW_TABLE[]; extern const char * test_valid_representations[]; extern const char * test_invalid_representations[]; extern const char * test_invalid_strings[]; static int test_phonetic_lookups_internal_sub(cw_test_executor_t * cte, char * phonetic_buffer); static bool representation_is_valid(const char * representation); /** The function builds every possible well formed representation no longer than 7 chars, and then calculates a hash of the representation. Since a representation is well formed, the tested function should calculate a hash. The function does not compare a representation and its hash to verify that patterns in representation and in hash match. TODO: add code that would compare the patterns of dots/dashes in representation against pattern of bits in hash. TODO: test calling the function with malformed representation. @reviewed on 2019-10-12 */ int test_cw_representation_to_hash_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Intended contents of input[] is something like that: input[0] = "." input[1] = "-" input[2] = ".." input[3] = "-." input[4] = ".-" input[5] = "--" input[6] = "..." input[7] = "-.." input[8] = ".-." input[9] = "--." input[10] = "..-" input[11] = "-.-" input[12] = ".--" input[13] = "---" . . . input[248] = ".-.----" input[249] = "--.----" input[250] = "..-----" input[251] = "-.-----" input[252] = ".------" input[253] = "-------" */ char input[REPRESENTATION_TABLE_SIZE][CW_DATA_MAX_REPRESENTATION_LENGTH + 1]; /* Build table of all well formed representations ("well formed" as in "built from dash and dot, no longer than CW_DATA_MAX_REPRESENTATION_LENGTH"). */ long int rep_idx = 0; for (unsigned int rep_length = 1; rep_length <= CW_DATA_MAX_REPRESENTATION_LENGTH; rep_length++) { /* Build representations of all lengths, starting from shortest (single dot or dash) and ending with the longest representations. */ unsigned int bit_vector_length = 1 << rep_length; /* A representation of length "rep_length" can have 2^rep_length distinct variants. The "for" loop that we are in iterates over these 2^len variants. E.g. bit vector (and representation) of length 2 has 4 variants: .. .- -. -- */ for (unsigned int variant = 0; variant < bit_vector_length; variant++) { /* Turn every '0' in 'variant' into dot, and every '1' into dash. */ for (unsigned int bit_pos = 0; bit_pos < rep_length; bit_pos++) { unsigned int bit = variant & (1 << bit_pos); input[rep_idx][bit_pos] = bit ? '-' : '.'; // fprintf(stderr, "rep = %x, bit pos = %d, bit = %d\n", variant, bit_pos, bit); } input[rep_idx][rep_length] = '\0'; //fprintf(stderr, "input[%ld] = \"%s\"\n", rep_idx, input[rep_idx]); rep_idx++; } } const long int n_representations = rep_idx; cte->expect_op_int(cte, n_representations, "==", REPRESENTATION_TABLE_SIZE, "internal count of representations"); /* Compute hash for every well formed representation. */ bool failure = false; for (int i = 0; i < n_representations; i++) { const uint8_t hash = LIBCW_TEST_FUT(cw_representation_to_hash_internal)(input[i]); /* The function returns values in range CW_DATA_MIN_REPRESENTATION_HASH - CW_DATA_MAX_REPRESENTATION_HASH. */ if (!cte->expect_between_int_errors_only(cte, CW_DATA_MIN_REPRESENTATION_HASH, hash, CW_DATA_MAX_REPRESENTATION_HASH, "representation to hash: hash #%d\n", i)) { failure = true; break; } } cte->expect_op_int(cte, false, "==", failure, "representation to hash"); cte->print_test_footer(cte, __func__); return 0; } /** Verify that our fast lookup of characters works correctly. The verification is performed by comparing results of function using fast lookup table with results of function using direct method. @reviewed on 2019-10-12 */ int test_cw_representation_to_character_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); bool failure = false; int i = 0; for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { const int char_fast_lookup = LIBCW_TEST_FUT(cw_representation_to_character_internal)(cw_entry->representation); const int char_direct = LIBCW_TEST_FUT(cw_representation_to_character_direct_internal)(cw_entry->representation); if (!cte->expect_op_int_errors_only(cte, char_fast_lookup, "==", char_direct, "fast lookup vs. direct method: '%s'", cw_entry->representation)) { failure = true; break; } /* Also test old version of cw_representation_to_character(). */ { char char_old_lookup = 0; int cwret = LIBCW_TEST_FUT(cw_lookup_representation)(cw_entry->representation, &char_old_lookup); if (!cte->expect_op_int_errors_only(cte, cwret, "==", CW_SUCCESS, "fast lookup vs. old method: conversion from representation to character for #%d (representation '%s')", i, cw_entry->representation)) { failure = true; break; } if (!cte->expect_op_int_errors_only(cte, char_fast_lookup, "==", char_old_lookup, "fast lookup vs. old method: '%s'", cw_entry->representation)) { failure = true; break; } i++; } } cte->expect_op_int(cte, false, "==", failure, "representation to character"); cte->print_test_footer(cte, __func__); return 0; } /** Testing speed gain between function using direct method, and function with fast lookup table. Test is preformed by using timer to see how much time it takes to execute a function N times. @reviewed on 2019-10-12 */ int test_cw_representation_to_character_internal_speed_gain(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); const int N = 1000; struct timeval start; struct timeval stop; gettimeofday(&start, NULL); for (int i = 0; i < N; i++) { for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { __attribute__((unused)) int character = cw_representation_to_character_internal(cw_entry->representation); } } gettimeofday(&stop, NULL); const int fast_lookup = cw_timestamp_compare_internal(&start, &stop); gettimeofday(&start, NULL); for (int i = 0; i < N; i++) { for (const cw_entry_t * cw_entry = CW_TABLE; cw_entry->character; cw_entry++) { __attribute__((unused)) int character = cw_representation_to_character_direct_internal(cw_entry->representation); } } gettimeofday(&stop, NULL); const int direct = cw_timestamp_compare_internal(&start, &stop); const float gain = 1.0 * direct / fast_lookup; const bool failure = gain < 1.1f; cte->expect_op_int(cte, false, "==", failure, "lookup speed gain: %.2f", (double) gain); /* Casting to double to avoid compiler warning about implicit conversion from float to double. */ cte->print_test_footer(cte, __func__); return 0; } /** @brief Test getting number of characters known to libcw @reviewed 2020-08-29 */ cwt_retv test_data_main_table_get_count(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* libcw doesn't define a constant describing the number of known/supported/recognized characters, but there is a function calculating the number. Two things are certain - the number is larger than zero, - it's not larger than ASCII table size. */ const int count = LIBCW_TEST_FUT(cw_get_character_count)(); const int lower_inclusive = 1; const int upper_inclusive = 127; cte->expect_between_int(cte, lower_inclusive, count, upper_inclusive, "character count %d", count); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Test getting list of characters supported by libcw @reviewed 2020-08-29 */ cwt_retv test_data_main_table_get_contents(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); char charlist[UCHAR_MAX + 1] = { 0 }; LIBCW_TEST_FUT(cw_list_characters)(charlist); cte->log_info(cte, "list of characters: %s\n", charlist); /* Length of the list must match the character count returned by library. */ const int extracted_count = cw_get_character_count(); const int extracted_len = (int) strlen(charlist); cte->expect_op_int(cte, extracted_len, "==", extracted_count, "character count = %d, list length = %d", extracted_count, extracted_len); /* The 'sanity' of count of characters in charlist has been already indirectly tested in tests of cw_get_character_count(). */ cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** @brief Test getting maximum length of a representation (a string of Dots/Dashes) @reviewed on 2020-08-29 */ cwt_retv test_data_main_table_get_representation_len_max(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); const int max_rep_length = LIBCW_TEST_FUT(cw_get_maximum_representation_length)(); const int lower_inclusive = CW_DATA_MIN_REPRESENTATION_LENGTH; const int upper_inclusive = CW_DATA_MAX_REPRESENTATION_LENGTH; cte->expect_between_int(cte, lower_inclusive, max_rep_length, upper_inclusive, "maximum representation length (%d)", max_rep_length); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /* Simple helper function for validating string with representation. */ static bool representation_is_valid(const char * representation) { if (NULL == representation) { return false; } const size_t len = strlen(representation); if (len < CW_DATA_MIN_REPRESENTATION_LENGTH) { return false; } if (len > CW_DATA_MAX_REPRESENTATION_LENGTH) { return false; } for (size_t i = 0; i < len; i++) { if (CW_DOT_REPRESENTATION != representation[i] && CW_DASH_REPRESENTATION != representation[i]) { return false; } } return true; } /** @brief Test functions looking up characters and their representation @reviewed on 2019-10-12 */ cwt_retv test_data_main_table_lookups(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); char charlist[UCHAR_MAX + 1] = { 0 }; cw_list_characters(charlist); const int max_rep_length = cw_get_maximum_representation_length(); bool c2r_failure = false; bool r2c_failure = false; bool two_way_failure = false; bool length_failure = false; /* For each character, look up its representation, the look up each representation in the opposite direction. */ for (int i = 0; charlist[i] != '\0'; i++) { const char input_character = charlist[i]; char * representation = LIBCW_TEST_FUT(cw_character_to_representation)(input_character); if (!cte->expect_valid_pointer_errors_only(cte, representation, "character to representation: conversion from character to representation (new) for #%d (char '%c')\n", i, input_character)) { c2r_failure = true; break; } bool is_valid = representation_is_valid(representation); if (!cte->expect_op_int_errors_only(cte, is_valid, "==", true, "character to representation: validity of representation (new) for #%d (char '%c')", i, input_character)) { c2r_failure = true; break; } /* Also test the old version of cw_character_to_representation(). */ { /* Two things on purpose: - size larger than (CW_DATA_MAX_REPRESENTATION_LENGTH+1) to be able to look at how much is copied to the buffer. - initialization with something other than NUL to be able to look at what is copied to the buffer. */ char representation_old[2 * (CW_DATA_MAX_REPRESENTATION_LENGTH + 1)]; memset(representation_old, 'a', sizeof (representation_old)); int cwret = LIBCW_TEST_FUT(cw_lookup_character)(input_character, representation_old); if (!cte->expect_op_int_errors_only(cte, cwret, "==", CW_SUCCESS, "character to representation: conversion from character to representation (old) for #%d (char '%c')", i, input_character)) { c2r_failure = true; break; } is_valid = representation_is_valid(representation_old); if (!cte->expect_op_int_errors_only(cte, is_valid, "==", true, "character to representation: validity of representation (old) for #%d (char '%c')", i, input_character)) { c2r_failure = true; break; } const int cmp = strcmp(representation, representation_old); if (!cte->expect_op_int_errors_only(cte, cmp, "==", 0, "character to representation: result of new and old method for #%d: '%s' != '%s'", i, representation, representation_old)) { c2r_failure = true; break; } } /* Here we convert the representation back into a character. */ char reverse_character = LIBCW_TEST_FUT(cw_representation_to_character)(representation); if (!cte->expect_op_int_errors_only(cte, 0, "!=", reverse_character, "representation to character: conversion from representation to character (new) for #%d (representation '%s')", i, representation)) { r2c_failure = true; break; } /* Also test old version of cw_representation_to_character(). */ { char old_reverse_character = 0; int cwret = LIBCW_TEST_FUT(cw_lookup_representation)(representation, &old_reverse_character); if (!cte->expect_op_int_errors_only(cte, cwret, "==", CW_SUCCESS, "representation to character: conversion from representation to character (old) for #%d (representation '%s')", i, representation)) { r2c_failure = true; break; } if (!cte->expect_op_int_errors_only(cte, reverse_character, "==", old_reverse_character, "representation to character: result of new and old method for #%d: '%c' != '%c'", i, reverse_character, old_reverse_character)) { r2c_failure = true; break; } } /* Compare output char with input char. */ if (!cte->expect_op_int_errors_only(cte, reverse_character, "==", input_character, "character lookup: two-way lookup for #%d ('%c' -> '%s' -> '%c')", i, input_character, representation, reverse_character)) { two_way_failure = true; break; } const int length = (int) strlen(representation); const int rep_length_lower = 1; /* A representation will have at least one character. */ const int rep_length_upper = max_rep_length; if (!cte->expect_between_int_errors_only(cte, rep_length_lower, length, rep_length_upper, "character lookup: representation length of character '%c' (#%d)", input_character, i)) { length_failure = true; break; } free(representation); representation = NULL; } cte->expect_op_int(cte, false, "==", c2r_failure, "character lookup: char to representation"); cte->expect_op_int(cte, false, "==", r2c_failure, "character lookup: representation to char"); cte->expect_op_int(cte, false, "==", two_way_failure, "character lookup: two-way lookup"); cte->expect_op_int(cte, false, "==", length_failure, "character lookup: length"); cte->print_test_footer(cte, __func__); return cwt_retv_ok; } /** \brief Test functions looking up procedural characters and their representation. @revieded on 2019-10-12 */ int test_prosign_lookups_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Collect and print out a list of characters in the procedural signals expansion table. */ /* Test: get number of prosigns known to libcw. */ { const int count = LIBCW_TEST_FUT(cw_get_procedural_character_count)(); cte->expect_op_int_errors_only(cte, 0, "<", count, "procedural character count (%d):", count); } char procedural_characters[UCHAR_MAX + 1] = { 0 }; /* Test: get list of characters supported by libcw. */ { LIBCW_TEST_FUT(cw_list_procedural_characters)(procedural_characters); /* TODO: we need a version of the function that accepts size of buffer as argument. */ cte->log_info(cte, "list of procedural characters: %s\n", procedural_characters); const int extracted_len = (int) strlen(procedural_characters); const int extracted_count = cw_get_procedural_character_count(); cte->expect_op_int(cte, extracted_count, "==", extracted_len, "procedural character count = %d, list length = %d", extracted_count, extracted_len); } /* Test: expansion length. */ int max_expansion_length = 0; { max_expansion_length = LIBCW_TEST_FUT(cw_get_maximum_procedural_expansion_length)(); cte->expect_op_int(cte, 0, "<", max_expansion_length, "maximum procedural expansion length (%d)", max_expansion_length); } /* Test: lookup. */ { /* For each procedural character, look up its expansion, verify its length, and check a true/false assignment to the display hint. */ bool lookup_failure = false; bool length_failure = false; bool expansion_failure = false; for (int i = 0; procedural_characters[i] != '\0'; i++) { char expansion[256] = { 0 }; int is_usually_expanded = -1; /* This value should be set by libcw to either 0 (false) or 1 (true). */ const int cwret = LIBCW_TEST_FUT(cw_lookup_procedural_character)(procedural_characters[i], expansion, &is_usually_expanded); if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "procedural character lookup: lookup of character '%c' (#%d)", procedural_characters[i], i)) { lookup_failure = true; break; } const int length = (int) strlen(expansion); if (!cte->expect_between_int_errors_only(cte, 2, length, max_expansion_length, "procedural character lookup: expansion length of character '%c' (#%d)", procedural_characters[i], i)) { length_failure = true; break; } /* Check if call to tested function has modified the flag. */ if (!cte->expect_op_int_errors_only(cte, -1, "!=", is_usually_expanded, "procedural character lookup: expansion hint of character '%c' ((#%d)", procedural_characters[i], i)) { expansion_failure = true; break; } } cte->expect_op_int(cte, false, "==", lookup_failure, "procedural character lookup: lookup"); cte->expect_op_int(cte, false, "==", length_failure, "procedural character lookup: length"); cte->expect_op_int(cte, false, "==", expansion_failure, "procedural character lookup: expansion flag"); } cte->print_test_footer(cte, __func__); return 0; } /** @reviewed on 2020-07-25 */ int test_phonetic_lookups_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* For each ASCII character, look up its phonetic and check for a string that start with this character, if alphabetic, and false otherwise. */ /* Test: check that maximum phonetic length is larger than zero. */ { const int length = LIBCW_TEST_FUT(cw_get_maximum_phonetic_length)(); const bool failure = (length <= 0); cte->expect_op_int(cte, false, "==", failure, "phonetic lookup: maximum phonetic length (%d)", length); } /* Test: lookup of phonetic + reverse lookup. */ { char phonetic_buffer[sizeof ("VeryLongPhoneticString")] = { 0 }; /* Tested function should work with NULL output buffer as well. */ test_phonetic_lookups_internal_sub(cte, phonetic_buffer); test_phonetic_lookups_internal_sub(cte, NULL); } /* Test: simple test that the lookup is sane. */ { bool simple_failure = false; char phonetic_buffer[sizeof ("VeryLongPhoneticString")] = { 0 }; struct { char character; const char * string; } data[] = { { 'a', "Alfa" }, { 'A', "Alfa" }, { 'z', "Zulu" }, { 'Z', "Zulu" }, { 0, "" }}; /* Guard. */ int i = 0; while (data[i].character) { const int cwret = LIBCW_TEST_FUT(cw_lookup_phonetic)(data[i].character, phonetic_buffer); /* TODO: we need a version of the function that accepts size argument. */ if (!cte->expect_op_int_errors_only(cte, CW_SUCCESS, "==", cwret, "phonetic lookup: simple lookup for character '%c'", data[i].character)) { simple_failure = true; break; } int cmp = strcmp(phonetic_buffer, data[i].string); if (!cte->expect_op_int_errors_only(cte, 0, "==", cmp, "phonetic lookup: simple lookup for character '%c'/'%s' -> '%s'", data[i].character, data[i].string, phonetic_buffer)) { simple_failure = true; break; } i++; } cte->expect_op_int(cte, false, "==", simple_failure, "phonetic lookup: simple lookup test"); } cte->print_test_footer(cte, __func__); return 0; } /** Tested function should correctly handle NULL and non-NULL @p phonetic_buffer @reviewed on 2020-07-25 */ static int test_phonetic_lookups_internal_sub(cw_test_executor_t * cte, char * phonetic_buffer) { bool lookup_failure = false; bool reverse_failure = false; /* Notice that we go here through all possible values of char, not through all values returned from cw_list_characters(). */ for (int i = 0; i < UCHAR_MAX; i++) { const int cwret = LIBCW_TEST_FUT(cw_lookup_phonetic)((char) i, phonetic_buffer); /* TODO: we need a version of the function that accepts size argument. */ const bool is_alpha = (bool) isalpha(i); if (CW_SUCCESS == cwret) { /* Library claims that 'i' is a byte that has a phonetic (e.g. 'F' -> "Foxtrot"). Let's verify this using result of isalpha(). */ if (!cte->expect_op_int_errors_only(cte, true, "==", is_alpha, "phonetic lookup (A): lookup of phonetic for '%c' (#%d)", (char) i, i)) { lookup_failure = true; break; } } else { /* Library claims that 'i' is a byte that doesn't have a phonetic. Let's verify this using result of isalpha(). */ if (!cte->expect_op_int_errors_only(cte, false, "==", is_alpha, "phonetic lookup (B): lookup of phonetic for '%c' (#%d)", (char) i, i)) { lookup_failure = true; break; } } if (CW_SUCCESS == cwret && is_alpha && NULL != phonetic_buffer) { /* We have looked up a letter, it has a phonetic. Almost by definition, the first letter of phonetic should be the same as the looked up letter. */ reverse_failure = (phonetic_buffer[0] != toupper((char) i)); if (!cte->expect_op_int_errors_only(cte, false, "==", reverse_failure, "phonetic lookup: reverse lookup for phonetic \"%s\" ('%c' / #%d)", phonetic_buffer, (char) i, i)) { reverse_failure = true; break; } } } cte->expect_op_int(cte, false, "==", lookup_failure, "phonetic lookup: lookup"); cte->expect_op_int(cte, false, "==", reverse_failure, "phonetic lookup: reverse lookup"); return 0; } /** @brief Test validation of individual characters @reviewed on 2020-08-22 */ int test_validate_character_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Test: validation of individual characters. */ bool failure_valid = false; bool failure_invalid = false; char charlist[UCHAR_MAX + 1]; cw_list_characters(charlist); for (int i = 0; i < UCHAR_MAX; i++) { if (i == '\b') { /* For a short moment in development of post-3.5.1 I had a code that treated backspace as special character that removed last/previous character from the queue. This special behaviour has been removed before post-3.5.1 was published. Backspace should be handled in User Interface, e.g. by calling cw_gen_remove_last_character(), not in internals of library. Test that backspace is not treated as valid character. */ { const bool is_valid = (bool) LIBCW_TEST_FUT(cw_check_character)(i); if (!cte->expect_op_int_errors_only(cte, false, "==", is_valid, "validate character (old): valid character / #%d", i)) { failure_valid = true; break; } } { const bool is_valid = LIBCW_TEST_FUT(cw_character_is_valid)(i); if (!cte->expect_op_int_errors_only(cte, false, "==", is_valid, "validate character (new): character / #%d", i)) { failure_valid = true; break; } } } else if (i == ' ' || (i != 0 && strchr(charlist, toupper(i)) != NULL)) { /* Here we have a valid character, that is recognized/supported as 'sendable' by libcw. cw_check_character()/cw_character_is_valid() should confirm it. */ { const bool is_valid = (bool) LIBCW_TEST_FUT(cw_check_character)(i); if (!cte->expect_op_int_errors_only(cte, true, "==", is_valid, "validate character (old): valid character '%c' / #%d not recognized as valid", (char ) i, i)) { failure_valid = true; break; } } { const bool is_valid = LIBCW_TEST_FUT(cw_character_is_valid)(i); if (!cte->expect_op_int_errors_only(cte, true, "==", is_valid, "validate character (new): valid character '%c' / #%d not recognized as valid", (char ) i, i)) { failure_valid = true; break; } } } else { /* The 'i' character is not recognized/supported by libcw. cw_check_character()/cw_character_is_valid() should return false to signify that the char is invalid. */ { const bool is_valid = (bool) LIBCW_TEST_FUT(cw_check_character)(i); if (!cte->expect_op_int_errors_only(cte, false, "==", is_valid, "validate character (old): invalid character '%c' / #%d recognized as valid", (char ) i, i)) { failure_invalid = true; break; } } { const bool is_valid = LIBCW_TEST_FUT(cw_character_is_valid)(i); if (!cte->expect_op_int_errors_only(cte, false, "==", is_valid, "validate character (new): invalid character '%c' / #%d recognized as valid", (char ) i, i)) { failure_invalid = true; break; } } } } cte->expect_op_int(cte, false, "==", failure_valid, "validate character: valid characters"); cte->expect_op_int(cte, false, "==", failure_invalid, "validate character: invalid characters"); cte->print_test_footer(cte, __func__); return 0; } /** @brief Test validation of strings: valid strings and invalid strings @reviewed 2020-08-22 */ int test_validate_string_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Test: validation of string as a whole. */ /* Check the whole library-provided character list item as a single string. TODO: we should have array of valid strings, like we have for invalid strings below. */ char charlist[UCHAR_MAX + 1]; cw_list_characters(charlist); { const bool is_valid = (bool) LIBCW_TEST_FUT(cw_check_string)(charlist); cte->expect_op_int(cte, true, "==", is_valid, "validate string (old): valid string"); } { const bool is_valid = LIBCW_TEST_FUT(cw_string_is_valid)(charlist); cte->expect_op_int(cte, true, "==", is_valid, "validate string (new): valid string"); } /* Test invalid string. */ { int i = 0; while (NULL != test_invalid_strings[i]) { const char * test_string = test_invalid_strings[i]; const bool is_valid = LIBCW_TEST_FUT(cw_check_string)(test_string); cte->expect_op_int(cte, false, "==", is_valid, "validate string (old): invalid string %d/'%s'", i, test_string); i++; } } { int i = 0; while (NULL != test_invalid_strings[i]) { const char * test_string = test_invalid_strings[i]; const bool is_valid = LIBCW_TEST_FUT(cw_string_is_valid)(test_string); cte->expect_op_int(cte, false, "==", is_valid, "validate string (new): invalid string %d/'%s'", i, test_string); i++; } } cte->print_test_footer(cte, __func__); return 0; } /** @brief Test validation of representations of characters @reviewed 2020-08-22 */ int test_validate_representation_internal(cw_test_executor_t * cte) { cte->print_test_header(cte, __func__); /* Test: validating valid representations. */ { int i = 0; bool failure = false; while (NULL != test_valid_representations[i]) { { const bool is_valid = (bool) LIBCW_TEST_FUT(cw_check_representation)(test_valid_representations[i]); if (!cte->expect_op_int_errors_only(cte, true, "==", is_valid, "valid representation (old) (i = %d)", i)) { failure = true; break; } } { const bool is_valid = LIBCW_TEST_FUT(cw_representation_is_valid)(test_valid_representations[i]); if (!cte->expect_op_int_errors_only(cte, true, "==", is_valid, "valid representation (new) (i = %d)", i)) { failure = true; break; } } i++; } cte->expect_op_int(cte, false, "==", failure, "valid representations"); } /* Test: validating invalid representations. */ { int i = 0; bool failure = false; while (NULL != test_invalid_representations[i]) { { const bool is_valid = (bool) LIBCW_TEST_FUT(cw_check_representation)(test_invalid_representations[i]); if (!cte->expect_op_int_errors_only(cte, false, "==", is_valid, "invalid representation (old) (i = %d)", i)) { failure = true; break; } } { const bool is_valid = LIBCW_TEST_FUT(cw_representation_is_valid)(test_invalid_representations[i]); if (!cte->expect_op_int_errors_only(cte, false, "==", is_valid, "invalid representation (new) (i = %d)", i)) { failure = true; break; } } i++; } cte->expect_op_int(cte, false, "==", failure, "invalid representations"); } cte->print_test_footer(cte, __func__); return 0; } unixcw-3.6.0/src/libcw/tests/libcw_data_tests.h0000644000175000017500000000212314000344554016470 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_DATA_TESTS_H_ #define _LIBCW_DATA_TESTS_H_ #include "test_framework.h" int test_cw_representation_to_hash_internal(cw_test_executor_t * cte); int test_cw_representation_to_character_internal(cw_test_executor_t * cte); int test_cw_representation_to_character_internal_speed_gain(cw_test_executor_t * cte); cwt_retv test_data_main_table_get_count(cw_test_executor_t * cte); cwt_retv test_data_main_table_get_contents(cw_test_executor_t * cte); cwt_retv test_data_main_table_get_representation_len_max(cw_test_executor_t * cte); cwt_retv test_data_main_table_lookups(cw_test_executor_t * cte); int test_prosign_lookups_internal(cw_test_executor_t * cte); int test_phonetic_lookups_internal(cw_test_executor_t * cte); int test_validate_character_internal(cw_test_executor_t * cte); int test_validate_string_internal(cw_test_executor_t * cte); int test_validate_representation_internal(cw_test_executor_t * cte); #endif /* #ifndef _LIBCW_DATA_TESTS_H_ */ unixcw-3.6.0/src/libcw/tests/libcw_debug_tests.h0000644000175000017500000000051314000344554016646 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_DEBUG_TESTS_H_ #define _LIBCW_DEBUG_TESTS_H_ #include "test_framework.h" int test_cw_debug_flags_internal(cw_test_executor_t * cte); #endif /* #ifndef _LIBCW_DEBUG_TESTS_H_ */ unixcw-3.6.0/src/libcw/tests/test_framework_tools.c0000644000175000017500000001645614000344554017442 00000000000000/* * Copyright (C) 2001-2006 Simon Baldwin (simon_baldwin@yahoo.com) * Copyright (C) 2011-2021 Kamil Ignacak (acerion@wp.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include "test_framework_tools.h" #include "test_framework.h" static void resource_meas_do_measurement(resource_meas * meas); static void * resouce_meas_thread(void * arg); void * resouce_meas_thread(void * arg) { resource_meas * meas = (resource_meas *) arg; while (1) { resource_meas_do_measurement(meas); usleep(1000 * meas->meas_interval_msecs); } return NULL; } void resource_meas_start(resource_meas * meas, int meas_interval_msecs) { memset(meas, 0, sizeof (*meas)); meas->meas_interval_msecs = meas_interval_msecs; pthread_mutex_init(&meas->mutex, NULL); pthread_attr_init(&meas->thread_attr); pthread_create(&meas->thread_id, &meas->thread_attr, resouce_meas_thread, meas); } void resource_meas_stop(resource_meas * meas) { pthread_mutex_lock(&meas->mutex); pthread_cancel(meas->thread_id); pthread_attr_destroy(&meas->thread_attr); pthread_mutex_unlock(&meas->mutex); pthread_mutex_destroy(&meas->mutex); } int resource_meas_get_current_cpu_usage(resource_meas * meas) { pthread_mutex_lock(&meas->mutex); const int cpu_usage = meas->current_cpu_usage; pthread_mutex_unlock(&meas->mutex); return cpu_usage; } int resource_meas_get_maximal_cpu_usage(resource_meas * meas) { pthread_mutex_lock(&meas->mutex); const int cpu_usage = meas->maximal_cpu_usage; pthread_mutex_unlock(&meas->mutex); return cpu_usage; } void resource_meas_do_measurement(resource_meas * meas) { getrusage(RUSAGE_SELF, &meas->rusage_curr); timersub(&meas->rusage_curr.ru_utime, &meas->rusage_prev.ru_utime, &meas->user_cpu_diff); timersub(&meas->rusage_curr.ru_stime, &meas->rusage_prev.ru_stime, &meas->sys_cpu_diff); timeradd(&meas->user_cpu_diff, &meas->sys_cpu_diff, &meas->summary_cpu_usage); gettimeofday(&meas->timestamp_curr, NULL); timersub(&meas->timestamp_curr, &meas->timestamp_prev, &meas->timestamp_diff); meas->resource_usage = meas->summary_cpu_usage.tv_sec * 1000000 + meas->summary_cpu_usage.tv_usec; meas->meas_duration = meas->timestamp_diff.tv_sec * 1000000 + meas->timestamp_diff.tv_usec; meas->rusage_prev = meas->rusage_curr; meas->timestamp_prev = meas->timestamp_curr; pthread_mutex_lock(&meas->mutex); { meas->current_cpu_usage = meas->resource_usage * 100.0 / (meas->meas_duration * 1.0); #if 0 fprintf(stderr, "Curr = "CWTEST_CPU_FMT", usage = %.3f, duration = %ld\n", meas->current_cpu_usage, meas->resource_usage, meas->meas_duration); #endif if (meas->current_cpu_usage > meas->maximal_cpu_usage) { meas->maximal_cpu_usage = meas->current_cpu_usage; } /* Log the error "live" during test execution. This will allow to pinpoint the faulty code faster. */ if (meas->current_cpu_usage > LIBCW_TEST_MEAS_CPU_OK_THRESHOLD_PERCENT) { fprintf(stderr, "[EE] High current CPU usage: "CWTEST_CPU_FMT"\n", meas->current_cpu_usage); } } pthread_mutex_unlock(&meas->mutex); #if 0 fprintf(stderr, "user = %d.%d, system = %d.%d, total = %d.%d\n", user_cpu_diff.tv_sec, user_cpu_diff.tv_usec, sys_cpu_diff.tv_sec, sys_cpu_diff.tv_usec, summary_cpu_usage.tv_sec, summary_cpu_usage.tv_usec); #endif return; } void cwtest_param_ranger_init(cwtest_param_ranger_t * ranger, int min, int max, int step, int initial_value) { ranger->range_min = min; ranger->range_max = max; ranger->step = step; ranger->previous_value = initial_value; ranger->plateau_length = 0; if (initial_value == ranger->range_max) { ranger->direction = cwtest_param_ranger_direction_down; /* We can't go up, we are already at max. */ } else { ranger->direction = cwtest_param_ranger_direction_up; } } bool cwtest_param_ranger_get_next(cwtest_param_ranger_t * ranger, int * new_value) { if (ranger->interval_sec) { /* Generate new value only after specific time interval has passed since last value was returned. */ const time_t now_timestamp = time(NULL); if (now_timestamp < ranger->previous_timestamp + ranger->interval_sec) { /* Don't generate new value yet. */ return false; } else { ranger->previous_timestamp = now_timestamp; /* Go to code that will calculate new value. */ } } int val = 0; if (ranger->direction == cwtest_param_ranger_direction_up) { val = ranger->previous_value + ranger->step; if (val >= ranger->range_max) { val = ranger->range_max; ranger->direction = cwtest_param_ranger_direction_down; /* Starting with next call, start returning decreasing values. */ if (0 != ranger->plateau_length) { fprintf(stderr, "[DD] Entering 'maximum' plateau, value = %d\n", ranger->range_max); ranger->direction |= cwtest_param_ranger_direction_plateau; ranger->plateau_remaining = ranger->plateau_length; } } } else if (ranger->direction == cwtest_param_ranger_direction_down) { val = ranger->previous_value - ranger->step; if (val <= ranger->range_min) { val = ranger->range_min; ranger->direction = cwtest_param_ranger_direction_up; /* Starting with next call, start returning increasing values. */ if (0 != ranger->plateau_length) { fprintf(stderr, "[DD] Entering 'minimum' plateau, value = %d\n", ranger->range_min); ranger->direction |= cwtest_param_ranger_direction_plateau; ranger->plateau_remaining = ranger->plateau_length; } } } else if (ranger->direction & cwtest_param_ranger_direction_plateau) { /* Will return the same value as previously. */ val = ranger->previous_value; if (ranger->plateau_remaining > 0) { fprintf(stderr, "[DD] On plateau, remaining %d\n", ranger->plateau_remaining); ranger->plateau_remaining--; } else { /* Leave the plateau. Bit indicating direction up or direction down will be read and used in next function call. */ fprintf(stderr, "[DD] Leaving plateau\n"); ranger->direction &= (~cwtest_param_ranger_direction_plateau); } } else { fprintf(stderr, "[EE] Unhandled direction %02x\n", ranger->direction); return false; } ranger->previous_value = val; fprintf(stderr, "[DD] Returning new parameter value %d\n", val); *new_value = val; return true; } void cwtest_param_ranger_set_interval_sec(cwtest_param_ranger_t * ranger, time_t interval_sec) { if (interval_sec) { ranger->previous_timestamp = time(NULL); ranger->interval_sec = interval_sec; } else { ranger->previous_timestamp = 0; ranger->interval_sec = 0; } } void cwtest_param_ranger_set_plateau_length(cwtest_param_ranger_t * ranger, int plateau_length) { if (plateau_length) { ranger->plateau_length = plateau_length; } else { ranger->plateau_length = 0; } } unixcw-3.6.0/src/libcw/tests/README0000644000175000017500000000370414000344554013672 00000000000000This directory contains test code for libcw library. The tests in this directory can be considered as unit tests, but this may not be a 100% accurate description of the tests. There are two main types of test functions: A. test functions testing existing public API from libcw.h. The API is called "legacy API" because in the future I would like to replace it with something better. This set also includes a single function that verifies that a fix of one specific bug, implemented some time ago, is still working. B. test functions testing new (future) public API and internal functions of libcw that are used to implement both legacy API and new API. The future public API is not official yet, it's not stable nor fully specified. There is a main program that calls these test functions. The test program uses a tiny custom test framework to execute test functions described in points A and B. The tests are separated into topics (e.g. generator, tone queue, receiver, etc). Big part of the test functions is executed few times: once per every supported and available sound system (e.g. OSS, ALSA, PulseAudio). Testing the code with different sound systems is necessary because behaviour of some low level functions may depend on how a sound system handles received PCM frames. -------- In addition to the unit tests, the following Quality Assurance measures are implemented: 1. TO BE DONE: test coverage reports (gcov), 2. TO BE DONE: static code analysis with cppcheck (for both production code and test code), 3. TO BE DONE: static code analysis with clang-tidy (for both production code and test code), 4. TO BE DONE: compilation with c++ compilers (g++ and clang) (for both production code and test code), 5. TO BE DONE: compilation with -Wall -Werror -pedantic -Wextra (other) by default (for both production code and test code), 6. TO BE DESCRIBED: valgrind, 7. PARTIALLY DONE, TO BE DESCRIBED: compilation and tests on different platforms. unixcw-3.6.0/src/libcw/tests/test_framework_tools.h0000644000175000017500000001762014000344554017441 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_TEST_FRAMEWORK_TOOLS_H_ #define _LIBCW_TEST_FRAMEWORK_TOOLS_H_ #include #include #include #include #include /* Format to be used when printing CPU usage with printf(). */ #define CWTEST_CPU_FMT "%03d%%" typedef struct { /* At what intervals the measurement should be taken. */ int meas_interval_msecs; struct rusage rusage_prev; struct rusage rusage_curr; struct timeval timestamp_prev; struct timeval timestamp_curr; struct timeval user_cpu_diff; /* User CPU time used. */ struct timeval sys_cpu_diff; /* System CPU time used. */ struct timeval summary_cpu_usage; /* User + System CPU time used. */ struct timeval timestamp_diff; suseconds_t resource_usage; /* At what interval the last two measurements were really taken. */ suseconds_t meas_duration; pthread_mutex_t mutex; pthread_attr_t thread_attr; pthread_t thread_id; int current_cpu_usage; /* Last calculated value of CPU usage. */ int maximal_cpu_usage; /* Maximum detected during measurements run. */ } resource_meas; /** @brief Start measurement process This function also resets to zero 'max resource usage' field in @param meas, so that a new value can be calculated during new measurement. @param meas_interval_msecs - at what intervals the measurements should be taken */ void resource_meas_start(resource_meas * meas, int meas_interval_msecs); void resource_meas_stop(resource_meas * meas); /** @brief Get current CPU usage The value may change from one measurement to another - may rise and fall. */ int resource_meas_get_current_cpu_usage(resource_meas * meas); /** @brief Get maximal CPU usage calculated since measurement has been started This function returns the highest value detected since measurement was started with resource_meas_start(). The value may be steady or may go up. The value is reset to zero each time a resource_meas_start() function is called. */ int resource_meas_get_maximal_cpu_usage(resource_meas * meas); /** Direction in which values returned by calls to _get_next() will go: will they increase, will they decrease or will they stay on constant level (plateau) for few calls to _get_next(). Using bits to mark direction. Once we reach plateau, we have to remember in which direction to go once we get off from the plateau. */ typedef enum cwtest_param_ranger_direction { cwtest_param_ranger_direction_up = 0x01, cwtest_param_ranger_direction_down = 0x02, cwtest_param_ranger_direction_plateau = 0x04 } cwtest_param_ranger_direction; /** Object for obtaining varying values of some integer parameter from specified range on each call to _get_next() function. Currently the returned values can change linearly up and down between min and max. Possibly in the future the ranger will support modes other than linear: random, sine, or other. */ typedef struct cwtest_param_ranger_t { /* Minimal value of parameter values generated. */ int range_min; /* Maximal value of parameter values generated. */ int range_max; /* By how much the returned value changes on each successful call to _get_next(). */ int step; /* Internal helper variable. Value returned by previous call to _get_next(), used in calculating new value returned by _get_next(). */ int previous_value; /* Internal helper variable. In linear generation method: flag that dictates if values returned by _get_next() are linearly increasing or decreasing. */ cwtest_param_ranger_direction direction; /* Internal helper variable. Timestamp at which previous new value was returned by _get_next() */ time_t previous_timestamp; /* Time interval at which new values are calculated and returned by _get_next(). If time between previous_timestamp and current time is less than interval_sec, _get_next() will return false. */ time_t interval_sec; /* When parameter reaches minimum or maximum value, how many next calls to _get_next() should return the same min/max value (how many calls to _get_next() should stay on the plateau)? Set to zero to disable this feature. Value is zero by default. This is a bit fuzzy parameter, with possible off-by-one error. Don't set it to 1 or 2 or such small value and expect _get_next() to return min/max values exactly 1 or 2 times. Set it to 10 or 20, and expect approximately 10 or approximately 20 calls to _get_next() to return min/max. Unit: times (successful calls to _get_next() that return min/max). */ int plateau_length; /* Internal helper variable. If plateau_length is non-zero, how many next calls to _get_next() should return previous (minimal or maximal) value? */ int plateau_remaining; } cwtest_param_ranger_t; /** @brief Initialize @p ranger @param ranger ranger to initialize @param min minimal value of range of values returned by _get_next(), e.g. CW_SPEED_MIN @param max maximal value of range of values returned by _get_next(), e.g. CW_SPEED_MAX @param step by how much the returned value changes on each successful call to _get_next(), e.g. CW_SPEED_STEP or 4 @param initial_value initial parameter value stored by @p ranger and used to calculate first value returned by _get_next(), e.g. value returned by cw_gen_get_speed() */ void cwtest_param_ranger_init(cwtest_param_ranger_t * ranger, int min, int max, int step, int initial_value); /** @brief Configure ranger to generate new value only if specific interval has passed since previous successful call to _get_next() If @p interval_sec is non-zero, calling _get_next() on @p ranger will return new value only if at least @p interval_sec seconds passed since last successful call. If you have some control loop executed every 100ms, and you want to be able to operate on @p ranger in this loop, but want to get values less frequently than every 100ms, you can configure desired time interval for @p ranger with this function. The calls to _get_next() will then return success and new value each @p interval_sec. Pass zero value of @p interval_sec to disable this feature for @p ranger. @param ranger @param interval_sec at what intervals the call to _get_next() will return true and will return next value of parameter */ void cwtest_param_ranger_set_interval_sec(cwtest_param_ranger_t * ranger, time_t interval_sec); /** @brief Configure plateau length for ranger When value calculated by ranger reaches min or max, decide how many following calls to _get_next() will return the same value equal to min or max. The values returned by _get_next() will stay on that 'plateau' for approximately @p plateau_length calls. After approximately @p plateau_length calls, the values returned by _get_next() will leave the plateau and will start to change again. Pass zero value of @p plateau_length to disable this feature for @p ranger. @param ranger @param plateau_length how many calls to _get_next() will return non-changing min or max value, when the min/max is reached */ void cwtest_param_ranger_set_plateau_length(cwtest_param_ranger_t * ranger, int plateau_length); /** @brief Get next value from @p ranger On successful call function returns true, and new value is returned through @p new_value. Otherwise false is returned. If @param ranger is configured to use intervals (with cwtest_param_ranger_set_interval_sec()), only calls that are separated by at least given time interval will return true. Otherwise each call to _get_next() will be successful. @return true if ranger has returned new value through @p new_value @return false otherwise */ bool cwtest_param_ranger_get_next(cwtest_param_ranger_t * ranger, int * new_value); #endif /* #ifndef _LIBCW_TEST_FRAMEWORK_TOOLS_H_ */ unixcw-3.6.0/src/libcw/tests/libcw_utils_tests.h0000644000175000017500000000110614000344554016717 00000000000000/* This file is a part of unixcw project. unixcw project is covered by GNU General Public License, version 2 or later. */ #ifndef _LIBCW_UTILS_TESTS_H_ #define _LIBCW_UTILS_TESTS_H_ #include "test_framework.h" int test_cw_timestamp_compare_internal(cw_test_executor_t * cte); int test_cw_timestamp_validate_internal(cw_test_executor_t * cte); int test_cw_usecs_to_timespec_internal(cw_test_executor_t * cte); int test_cw_version_internal(cw_test_executor_t * cte); int test_cw_license_internal(cw_test_executor_t * cte); #endif /* #ifndef _LIBCW_UTILS_TESTS_H_ */ unixcw-3.6.0/src/libcw/Doxyfile0000644000175000017500000030500714000344554013357 00000000000000# Doxyfile 1.8.7 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "libcw" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = 6.3.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # new page for each member. If set to NO, the documentation of a member will be # part of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # locally in source files will be included in the documentation. If set to NO # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # todo list. This list is created by putting \todo commands in the # documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # test list. This list is created by putting \test commands in the # documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. # ENABLED_SECTIONS = ENABLED_SECTIONS = LIBCW_INTERNAL # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES the list # will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. Do not use file names with spaces, bibtex cannot handle them. See # also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = ./ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = *.py # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler ( hhc.exe). If non-empty # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated ( # YES) or that it should be included in the master .chm file ( NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( # YES) or a normal table of contents ( NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 1 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /