Applications from Libraries
Table of Contents
1 Introduction
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.
2 For Example
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 5 for thoughts on external functions.
3 Features, Requirements
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.
4 Application Code
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:
- app_fmlib – the main function. Produces xxx_app from xxxlib.
- app_fun – produces the list of all functions used by its first LIBRARY argument. using app_nomar, to recursively collect the used functions
- app_uses – give a list of functions, returns the function names used by those functions
- app_nomar – an incredible bit of code, iterates with it's first argument, until no further items are added to the list of names returned.
_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 }
5 Future Directions
As such, an app may have a function app_system which details the systems it may, or may not run on, such details providing:
- inclusive or exclusive notice
- system names, with a fragment of the uname command with the maximum degree of information from the s, a, or v flags.
- human-readable text that doesn't interfere with machine-reading either above.
An app_externfunction function might identify function names which match the family name, yet are not in the family_app.
5.1 TODO a _library function should echo the names, not find them
6 References
- this file online: http://mcgowans.org/pubs/marty3/commonplace/software/appsFromFunctions.html
- the Only Sh-Bang You Will Ever Need
- except maybe for this one
- the SHELF Standard, see the online copy (above) for the link