An app in this notation is a function library with no external references, except to bash features and widely available system commands.
The functions here collect all needed functions for libraries with exernal references to produce an app, such that the final collection has no external references. In the current practice, with reference to the SHELF Standard, a library name ends "lib", as in ftplib. The root of the library name defines the ftp family.
To produce an application from the library, library function names begin with the ftp_ prefix. To list the functions in the ftplib:
$ functions ftplib
ftp_doc
ftp_version
ftp_uploadlist
ftp_uploaded
ftp_reload
ftp_many
ftp_commands
ftp_mputs
ftp_env
ftp_make
ftp_show
ftp_set
ftp_help
$
$ ftp_env
FTP_USER=mcgowansorg
FTP_PORT=2222
FTP_SITE=ftp.mcgowans.org
$
In the example above, the ftp_env function extracts environment variables used by the ftplib and it's functions.
Code developed here is in the app family library. In this example, it produces the ftp_app, which looks like a library, with all the functions of the ftp library, plus any lower-level functions from other libraries to make a complete stand-along function library, again ftp_app. In order to turn this all-inclusive library into a full-fledged app, or a command line executable, another small library, Only One Sh-bang makes it's executable available as the universal command. In this case, the ftp file is linked the master executable, sh-bang.
This example shows the results of the linked ftp command:
$ ftp utilities
ftp_comment
ftp_files
ftp_functions
ftp_library
ftp_myname
ftp_needir
ftp_newest
ftp_notlib
ftp_report_notargcount
ftp_report_notfile
ftp_report_notpipe
ftp_report_usage
ftp_setenv
ftp_trace_call
ftp_trace_stderr
ftp_utilities
$
First, the command is ftp and it's argument is utilities. This feature may be available via the command line, as ftp_utilities
. But a user shouldn't count on it. The function is in the ftp_app library, which is not likely to be routinely sourced by a login profile. That distinguishes the app from the lib. An app sources a library with only the necessary functions for it's primary function.
Notice, in the prior list, there was no ftp_utilities function. But, these "new" functions ftp_comment, ftp_files, … are all created by one of two means. While the ftp_library function returns the same result of the prior functions ftplib
command, there was no prior ftp_utilities command. Functions of this sort may be created from existing functions.
For example, here are the functions ftp_library and ftp_functions:
$ declare -f ftp_library ftp_functions
ftp_library ()
{
ftp_functions $(which ftplib) | sort
}
ftp_functions ()
{
: date: 2017-05-29;
: date: 2017-08-10;
awk '$2 ~ /^[(][)]$/ && !printed[$1]++ { print $1 }' ${*:--}
}
$
In this example, ftp_functions is copied from a generally available, in some other library, the functions function. The particular function, ftp_library is readily produced by using the general functions, renamed for this library family as ftp_functions, extracting those names from the existing ftplib.
You can imagine how the ftp_utilities works, it compares, and subtracts the list of functions from the ftplib from the resulting ftp_app.
Worth mentioning at ths point, there may be other functions in other libraries whose names match the ftp_ family. This allows users to extend the features of an existing family. See Future Directions for thoughts on external functions.
A library is named {something}lib; it's companion app is {something}_app.
Since functions may come from mulitiple libraries:
a function may only be located in one library at any directory on the PATH ( there may be other stops on the path with the same library with many of the same funcitons )
no function should be defined in more than one library.
any number of apps will use a given utility function, but in the collection process, that function is re-named with the application's library preface. e.g. the home function may be renamed ftp_home.
A number of the fuctions are templates for each app family. For example, app_functions will become ftp_functions, etc..
app_utilties – first argument is an app family name. It returns all the function names from the application
app_library – uses app_functions, but returns the names from the library
app notlib – all those functions not in the library or application
app_untilities – those functions used by the _library, collected from other libraries.
A word about the functions:
_write_edit_script ()
{
local bas=$1
local lib=$2
local fam=$3
local needed=$4
comm -23 $bas <(functions $lib|sort -u) | tee $needed | awk -v fam=$fam '
{
printf "s/%s\\([ )]\\)/%s_%s\\1/g\n", $1, fam, $1
printf "s/%s$/%s_%s/g\n", $1, fam, $1
}
END {
printf "s/app_/%s_/g\n", fam
printf "s/%s_%s_/%s_/g\n", fam, fam, fam
printf "s/%s_%s_/%s_/g\n", fam, fam, fam
}
'
}
app_help ()
{
flcomm -2 $1 $2;
echo mv $1 $2;
wc $*;
echo _"to refresh, rm -f $4"
}
app_functions ()
{
: date: 2017-05-29;
: date: 2017-08-10;
: keep the app_ functions free of library functions.
case $# in
0)
[[ -p /dev/stdin ]] || {
echo try app_{library,app,utilties,notapp} 1&2
return 1
}
;;
*)
[[ -f $1 ]] || {
echo "$1 is NOT a file in app_functions" 1>&2
return 2
}
;;
esac;
awk '$2 ~ /^[(][)]$/ && !printed[$1]++ { print $1 }' ${*:--}
}
app_app () { app_functions $(which app_app); }
app_notapp ()
{
: date: 2018-04-29;
comm -23 <(set | app_functions | grep -i app_) <(app_app | tr ' ' '\n' |sort)
}
app_utilities ()
{
comm -23 <(app_app | sort) <(app_library)
}
function _write_lib ()
{
set $(basename ${1%lib})
local label=" : mfg: _write_lib $(date +%F);"
printf "%s_library ()\n{\n%s\n \${*:- echo} %s_{" $1 "$label" $1
local c=""
local g="${1}_"
for f in $(app_functions ${1}lib); do
printf "%s%s" $c ${f#$g}
c=","
done
printf "}\n}\n"
}
app_fmlib ()
{
: turn LIB into an APP;
: 1. collect functions used by FAMlib,;
: 2. write an edit script for 3.;
: 2. define them as FAM_function ...;
: 3. edit the lib functions to call redefined FAM_function;
: 4. announce differences between _NXT and existing _APP;
: todo: protect internal functions from being renamed, and;
: todo: repair failing xx_xx_ to xx_ replacement;
: date: 2018-04-30;
:;
set ${1:-ftp};
set ${1%lib};
local fam=$1;
local lib=$(which ${1}lib);
local app=${lib%lib}_app;
local nxt=${lib%lib}_nxt;
local bas=.$(basename $lib);
local needed=~/tmp/app.needed
local sedscript=~/tmp/app.sedscript
local presed=~/tmp/app.presed
[[ -f $lib ]] || {
echo No LIB: ${1}lib;
return 1
};
:;
source $lib;
: 1. app_fun collects list oppf used functions;
: these functions are supplied to each app;
: functions -- lists the functions in a library
: notapp -- returns list of family names _not_ in the application
: utilities -- returns list of re-labeled, subfunctions used
[[ -f $bas ]] || {
{
echo app_{functions,app,notapp,utilities} | tr ' ' '\n';
app_fun $(functions $lib)
} | sort -u > $bas
};
: 2. writes the edit script;
_write_edit_script $bas $lib $fam $needed > $sedscript
{
cat $lib
_write_lib $lib
declare -f $(< $needed)
} | tee $presed | sed -f $sedscript> $nxt;
:;
: --------- assure canonical format --;
:;
lib_crunch $nxt;
:;
[[ -f $app ]] || {
cp $nxt $app
};
app_help $nxt $app $lib $bas $presed
}
app_fun ()
{
: recursively w/for_nomar and app_uses, find all called functions;
: FIX tracing;
: FIX fuse finding functions behind ":"s.;
: date: 2016-11-10;
: date: 2018-05-19;
rm -f .funuses.*.*;
set $(app_nomar app_uses $* | tail -1);
shift;
echo $* | wpl | sort -u
}
app_uses ()
{
: Functions USE, or Function USES.;
: "who do I call".;
: date: 2017-08-16;
comm -12 <(sfg .) <(declare -f $* |wpl|sort -u)
}
app_nomar ()
{
: continue executing first arg as command,;
: until number of returned arguments is unchanged.;
: date: 2016-11-10;
: date: 2018-05-19;
rm -f $(needir $HOME/tmp)/.nomar.*.*;
local tmp=$HOME/tmp/.nomar;
while true; do
local num=$#;
set $1 $($*);
echo $* > $tmp.$num.out;
echo $*;
[[ $num -eq $# ]] && break;
echo NOMAR $# $* > /dev/tty;
read a;
done
}
As such, an app may have a function app_system which details the systems it may, or may not run on, such details providing:
An app_externfunction function might identify function names which match the family name, yet are not in the family_app.