FIXlib

Table of Contents

1 Introduction

This paper is part of my Commonplace Book; it descripes how I use local fixlibs.

A fixlib is local to a directory. It's first job is to collect local functions needing repair. It may be the first place a function is stored.

$ aNewfunction () { ... ; }
$ declare -f aNewfunction   | tee -a fixlib

The fixlib may be edited by your favorite editor. Occasionally, it is prudent to back it up. Here is the only backup you'll ever need.

$ backuplib fixlib

The preferred home for local functions is in a library named for its intended purpose. One of my common tasks these days, being "retired" is to manage our family investments. So in a HOME/Family/invest directory you'll find an investlib. When functions in the local fixlib achieve a certain stability ( I haven't found the need to repair or extend them), then they should be transferred to the investlib.

1.1 function usage

That's the first step on the way to a function practice. There are two other destinations for a function: frequent use on the command line, a small utility, and a tool for repeated use in other functions. Of these latter, I'm also discovering two classes of function: those which support shell function development in general, and those which support another tool which may or not be used in any local application. That function tree looks like:

  • local functions
  • global functions
    • command line utilities
    • widely used functions
      • function development practice
      • other supporting tools

In the remainder of this paper, let's look at the various path's a function may take. Before jumping off, it's necessary the description to this point is the ideal. There are likely many fixlib in your account at any time, and it's possible there's a global function which may have been pulled down into more than one fixlib. So, we'll include a few functions to identify where these might be.

How does it happen, that a function may be in multiple places at once? There's noting from preventing a quick fix happening in any working directory:

declare -f someGlobalFunction | tee -a fixlib

Some time later, after hopefully upward-compatible fixes to the function, you can find out where it belongs to put it back:

ifh someGlobalFunction

This is the first function we've seen here in the examples. Here are it's component parts:

1.2 supporting code

ihf () 
{ 
    : ~ function 
    : reports first instance of linked library names
    quietly whf $1 | xargs ls -i | awk ' !p[$1] ++  { print $2 }'
}
whf () 
{ 
    : ~ function
    : reports names of function libraries where function is defined
    : USES funclocn -- returns the list of active function libraries
    echo $* 1>&2;
    xwh "$1" $(funclocn) | sed 's/ .*//; s/:.*//' | tr A-Z a-z | uniq
}
quietly () 
{ 
    : ~ command ... -- gobbles stderr from command
    $@ 2> /dev/null
}
xwh () 
{ 
    patn="$1";
    shift;
    echo $# ... $1 $2 $3 ... 1>&2;
    grep "^$patn[[:blank:]][[:blank:]]*()" $*;
    grep "^function $patn *#*.*$" $*
}

1.3 use example

$ quietly foreach do_whf whf ihf xwh

whf ./fixlib /users/applemcg/.profile
ihf ./fixlib /users/applemcg/dropbox/git/backash/bin/cmdlib
xwh /users/applemcg/.profile

From this, we see that the whf function is defined in two places, the local fixlib and my home .profile. That means, if the function is updated in the fixlib, it should be returned to the .profile. We will have to decide, in a separate exercise, if there are other intances of whf out for a fix. Similarly for ihf. However xwh is only found in the .profile.

For the moment, we won't consider that any of these functions may be in another fixlib.

If we can guarantee that functions in this local fixlib aren't defined in another fixlib, then regardless of whether or not they've been changed here, then the copies here, disregarding other copies may be returned to the proper library. Here's the first example:

source fixlib
declare -f whf | tee -a /users/applemcg/.profile

After repeating the process for each function, they may be removed from the fixlib.

functions fixlib | egrep -v '^(whf|ihf|xwh)$' > .x
. fixlib; declare -f $(< .x) | tee .l
mv .l fixlib

2 Capturing functions

Following the outline of the various uses of a function, here's how to capture them in their proper resting place. Recall this may change as the function becomes more widely useful.

2.1 locally

We've already seen this but let's refesh it here for completeness:

declare -f newFunction | tee -a fixlib

The question arises: how to avoid multiple copies of the same function? We'll answer this after the outline.

2.2 globally

Recall, there are two classes here as well: a command line utility or a function widely used as sub-function. First, the command line:

declare -f cmdLineUtility | tee -a $(which cmdlib)

In this case, I maintain a library cmdlib, stored on my PATH, which is included by a source cmdlib in my .profile.

Alternatively, a function may be destined for a widely-used shell programming feature or as an addition to a existing publicly available tool. Since any of these are also found on my path, storing them is the same, with the exception of the library name.

2.2.1 programming

In practice, I actually have two of these. One called auxlib, the other known as programlib. The auxlib is more rigorously controlled, the programlib, is an alternate to the cmdlib, but assigned the same way.

declare -f report_feature | tee -a $(which programlib)

There's a pair of functions to identify where a function is used: sfuse and Set Functions USE, and fuse, just Functions USE, the latter reading from stdin. The former, sfuse displays the results of the set command, where all the function definitions and shell varialbes are displayed.

$ declare -f sfuse fuse

sfuse () 
{ 
    trace_call $*;
    set | fuse "$1"
}
fuse () 
{ 
  report_notargcount 1 $# pattern && return 1;
  : reconcile patterns to fun_from;
  p="$1";
  shift;
  cat ${*:--} | awk "

      \$1 ~ /^:$/ || \$1 ~ /^comment$/ { next }

      \$2 ~ /^\(\)$/     { f = \$1 }
            /^function / { f = \$2 }

      f != \"\" && ( \
           /[^a-zA-Z0-9_-]$p[^a-zA-Z0-9_-]/ || \
           /[^a-zA-Z0-9_-]$p$/ ) { 

          printf \"%s\t%s\n\", f, \$0 
      }
 "
}

$ sfuse fuse

fun_create	    fuse eval | grep '()' | sed 's/ *()/ ()/; s/ *\$/$/' | awk '$4 ~ /()/'
fun_maker	    set | fuse eval | grep ' () ' | field 1
nuse	    set | fuse $1 | grep -v ' () *$' | wc -l
sfuse	    set | fuse "$1"

So, the later usage: sfuse fuse shows the list of functions and the context for each instance of the fuse function. In the current scope, this shows that fuse is used by fun_create, fun_maker, nuse, and sfuse itself.

What does this tell us? That fuse belongs in programlib and not cmdlib

2.2.2 tool interfaces

It turns out, that sfuse is primarily a cmdlib tool, since I use it primarily on the command line. But it also nicely shows an exception to the rule which introduces the final component.

$ ff fun_bash obs_names

fun_bash () 
{ 
    report_notargcount 1 $# function name && return 1;
    trace_call $*;
    rdb_hdr bash context;
    sfuse $1
}
obs_names () 
{ 
    trace_call $*;
    sfuse OBSOLESCENT | field 1
}

$ fun_bash sfuse

bash     	context                        
----     	-------                        
fun_bash 	    sfuse $1                   
obs_names	    sfuse OBSOLESCENT | field 1

The exception here is that sfuse is indeed used in other functions. How did we find these? We used one of them, fun_bash to display the connection to another tool, in this case, the underutilized /RDB. In this case I used it to produce a small sample table of the sfuse output, folded into an /rdb table, where the field names are bash and context, listing the shell output in the table.

A partial list of tools with wrapper libraries:

git
github command line
mark
Markdown
pan
pandoc, swiss-army knife of text formatters
rdb
the Relational Data Base.
t
the twitter API
yaib
yahoo API lib

2.3 common practice

This top-level set of functions uses copies of functions found in the local fixlib in common with the current run-time version of the function: lib = which $1. In fun_toliblist, the functions are first copied to the existing library, and then, in fun_topreflib are removed from the default fixlib.

fun_golib () 
{
    : ~ libraryName
    : functions in fixlib are returned to their home library
    : and culled from the fixlib
    : USES report_ , functions, fun_starter, backup_lib, and flcomm

    local lib=$(which $1); shift
    report_notfile $lib /dev/null && return 1

    local fix=./fixlib
    local tmp=./.l

    : functions in BOTH in FIX and LIB, "comm -12 .."
    local lst=$(flcomm -12 $fix $lib)

    [[ -z "$lst" ]] && {
	comment no FIXLIB functions to $lib
	comment ===========================
	quietly foreach do_whf $(functions $fix)
	return
    }

    . $fix   # source FIXLIB, the FIXed versions

    fun_toliblist $lib $lst   

    comment =================

    fix_whf
}
fun_toliblist ()
{
    : ~ library function ...
    : append functions to library, back it up

    # report_notcalledby fix_golib && return 1

    lib=$1; shift
    tmp=./.l

    shell_write  $tmp declare -f $(functions $lib) $*
    shell_append $tmp fun_starter $lib 

    mv $tmp    $lib
    backup_lib $lib
    fun_preferlib $lib 
}
fun_preferlib ()
{
    : ~ library [fixlib]
    : functions unique to 2nd arg are retained,
    :  library is NOT updated
    report_notfile $1 && return 1
    lib=$1; shift
    frm=${2:-./fixlib}; shift
    report_notfile $frm && return 2

    tmp=./.l

    shell_write $tmp declare -f $(flcomm -23 $frm $lib)

    doit mv $tmp $frm
    backup_lib $frm
}
fun_whf   ()   { do_do whf;  quietly foreach do_whf $(functions fixlib); }
fix_only ()
{
    fun_whf | awk 'NF == 2 { print $1 }'
}

2.4

3 Code

Author: Marty

Created: 2016-06-08 Wed 17:45

Emacs 24.4.1 (Org mode 8.2.10)

Validate