blob: d21281db0ac504d59610e3ea7b82bc9feb354c39 [file] [log] [blame]
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -05001# composure - by erichs
Erich Smith635c3802012-05-08 23:48:01 -04002# light-hearted functions for intuitive shell programming
Erich Smitha8fcf9f2012-04-27 15:07:04 -04003
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -05004# install: source this script in your ~/.profile or ~/.${SHELL}rc script
Erich Smitha8fcf9f2012-04-27 15:07:04 -04005
6# latest source available at http://git.io/composure
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -05007# known to work on bash, zsh, and ksh93
Erich Smitha8fcf9f2012-04-27 15:07:04 -04008
Erich Smith635c3802012-05-08 23:48:01 -04009# 'plumbing' functions
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -050010
Erich Smith635c3802012-05-08 23:48:01 -040011composure_keywords ()
Erich Smitha8fcf9f2012-04-27 15:07:04 -040012{
Erich Smith635c3802012-05-08 23:48:01 -040013 echo "about author example group param version"
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -050014}
15
16letterpress ()
17{
Erich Smith635c3802012-05-08 23:48:01 -040018 typeset rightcol="$1" leftcol="${2:- }"
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -050019
Erich Smith635c3802012-05-08 23:48:01 -040020 if [ -z "$rightcol" ]; then
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -050021 return
22 fi
23
Erich Smith635c3802012-05-08 23:48:01 -040024 printf "%-20s%s\n" "$leftcol" "$rightcol"
Erich Smitha8fcf9f2012-04-27 15:07:04 -040025}
26
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -050027transcribe ()
Erich Smitha8fcf9f2012-04-27 15:07:04 -040028{
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -050029 typeset func=$1
30 typeset file=$2
31 typeset operation="$3"
Erich Smitha8fcf9f2012-04-27 15:07:04 -040032
Travis Swicegood3e7c0fb2012-05-04 16:30:22 -050033 if git --version >/dev/null 2>&1; then
34 if [ -d ~/.composure ]; then
35 (
36 cd ~/.composure
37 if git rev-parse 2>/dev/null; then
38 if [ ! -f $file ]; then
39 printf "%s\n" "Oops! Couldn't find $file to version it for you..."
40 return
41 fi
42 cp $file ~/.composure/$func.inc
43 git add --all .
44 git commit -m "$operation $func"
45 fi
46 )
47 else
48 if [ "$USE_COMPOSURE_REPO" = "0" ]; then
49 return # if you say so...
50 fi
51 printf "%s\n" "I see you don't have a ~/.composure repo..."
52 typeset input
53 typeset valid=0
54 while [ $valid != 1 ]; do
55 printf "\n%s" 'would you like to create one? y/n: '
56 read input
57 case $input in
58 y|yes|Y|Yes|YES)
59 (
60 echo 'creating git repository for your functions...'
61 mkdir ~/.composure
62 cd ~/.composure
63 git init
64 echo "composure stores your function definitions here" > README.txt
65 git add README.txt
66 git commit -m 'initial commit'
67 )
68 # if at first you don't succeed...
69 transcribe "$func" "$file" "$operation"
70 valid=1
71 ;;
72 n|no|N|No|NO)
73 printf "%s\n" "ok. add 'export USE_COMPOSURE_REPO=0' to your startup script to disable this message."
74 valid=1
75 ;;
76 *)
77 printf "%s\n" "sorry, didn't get that..."
78 ;;
79 esac
80 done
81 fi
Erich Smitha8fcf9f2012-04-27 15:07:04 -040082 fi
Erich Smitha8fcf9f2012-04-27 15:07:04 -040083}
84
Erich Smith635c3802012-05-08 23:48:01 -040085transcribe ()
86{
87 typeset func=$1
88 typeset file=$2
89 typeset operation="$3"
90
91 if git --version >/dev/null 2>&1; then
92 if [ -d ~/.composure ]; then
93 (
94 cd ~/.composure
95 if git rev-parse 2>/dev/null; then
96 if [ ! -f $file ]; then
97 printf "%s\n" "Oops! Couldn't find $file to version it for you..."
98 return
99 fi
100 cp $file ~/.composure/$func.inc
101 git add --all .
102 git commit -m "$operation $func"
103 fi
104 )
105 else
106 if [ "$USE_COMPOSURE_REPO" = "0" ]; then
107 return # if you say so...
108 fi
109 printf "%s\n" "I see you don't have a ~/.composure repo..."
110 typeset input
111 typeset valid=0
112 while [ $valid != 1 ]; do
113 printf "\n%s" 'would you like to create one? y/n: '
114 read input
115 case $input in
116 y|yes|Y|Yes|YES)
117 (
118 echo 'creating git repository for your functions...'
119 mkdir ~/.composure
120 cd ~/.composure
121 git init
122 echo "composure stores your function definitions here" > README.txt
123 git add README.txt
124 git commit -m 'initial commit'
125 )
126 # if at first you don't succeed...
127 transcribe "$func" "$file" "$operation"
128 valid=1
129 ;;
130 n|no|N|No|NO)
131 printf "%s\n" "ok. add 'export USE_COMPOSURE_REPO=0' to your startup script to disable this message."
132 valid=1
133 ;;
134 *)
135 printf "%s\n" "sorry, didn't get that..."
136 ;;
137 esac
138 done
139 fi
140 fi
141}
142
143typeset_functions ()
144{
145 # unfortunately, there does not seem to be a easy, portable way to list just the
146 # names of the defined shell functions...
147
148 # here's a hack I modified from a StackOverflow post:
149 # we loop over the ps listing for the current process ($$), and print the last column (CMD)
150 # stripping any leading hyphens bash sometimes throws in there
151
152 typeset x ans
153 typeset this=$(for x in $(ps -p $$); do ans=$x; done; printf "%s\n" $ans | sed 's/^-*//')
154 typeset shell=$(basename $this) # e.g. /bin/bash => bash
155 case "$shell" in
156 bash)
157 typeset -F | awk '{print $3}'
158 ;;
159 *)
160 # trim everything following '()' in ksh
161 typeset +f | sed 's/().*$//'
162 ;;
163 esac
164}
165
166
167# bootstrap metadata keywords for porcelain functions
168for f in $(composure_keywords)
169do
170 eval "$f() { :; }"
171done
172unset f
173
174
175# 'porcelain' functions
176
177cite ()
178{
179 about creates one or more meta keywords for use in your functions
180 param one or more keywords
181 example '$ cite url username'
182 example '$ url http://somewhere.com'
183 example '$ username alice'
184 group composure
185
186 # this is the storage half of the 'metadata' system:
187 # we create dynamic metadata keywords with function wrappers around
188 # the NOP command, ':'
189
190 # anything following a keyword will get parsed as a positional
191 # parameter, but stay resident in the ENV. As opposed to shell
192 # comments, '#', which do not get parsed and are not available
193 # at runtime.
194
195 # a BIG caveat--your metadata must be roughly parsable: do not use
196 # contractions, and consider single or double quoting if it contains
197 # non-alphanumeric characters
198
199 if [ -z "$1" ]; then
200 printf '%s\n' 'missing parameter(s)'
201 reference cite
202 return
203 fi
204
205 typeset keyword
206 for keyword in $*; do
207 eval "$keyword() { :; }"
208 done
209}
210
211draft ()
212{
213 about wraps command from history into a new function, default is last command
214 param 1: name to give function
215 param 2: optional history line number
216 example '$ ls'
217 example '$ draft list'
218 example '$ draft newfunc 1120 # wraps command at history line 1120 in newfunc()'
219 group composure
220
221 typeset func=$1
222 typeset num=$2
223 typeset cmd
224
225 if [ -z "$func" ]; then
226 printf '%s\n' 'missing parameter(s)'
227 reference draft
228 return
229 fi
230
231 if [ -z "$num" ]; then
232 cmd=$(fc -ln -1 | head -1 | sed 's/^[[:blank:]]*//')
233 else
234 # parse command from history line number
235 cmd=$(eval "history | grep '^[[:blank:]]*$num' | head -1" | sed 's/^[[:blank:][:digit:]]*//')
236 fi
237 eval "$func() { $cmd; }"
238 typeset file=$(mktemp /tmp/draft.XXXX)
239 typeset -f $func > $file
240 transcribe $func $file draft
241 rm $file 2>/dev/null
242}
243
244glossary ()
245{
246 about displays help summary for all functions, or summary for a group of functions
247 param 1: optional, group name
248 example '$ glossary'
249 example '$ glossary misc'
250 group composure
251
252 typeset targetgroup=${1:-}
253
254 for func in $(typeset_functions); do
255 typeset about="$(typeset -f $func | metafor about)"
256 if [ -n "$targetgroup" ]; then
257 typeset group="$(typeset -f $func | metafor group)"
258 if [ "$group" != "$targetgroup" ]; then
259 continue # skip non-matching groups, if specified
260 fi
261 fi
262 letterpress "$about" $func
263 done
264}
265
266metafor ()
267{
268 about prints function metadata associated with keyword
269 param 1: meta keyword
270 example '$ typeset -f glossary | metafor example'
271 group composure
272
273 typeset keyword=$1
274
275 if [ -z "$keyword" ]; then
276 printf '%s\n' 'missing parameter(s)'
277 reference metafor
278 return
279 fi
280
281 # this sed-fu is the retrieval half of the 'metadata' system:
282 # 'grep' for the metadata keyword, and then parse/filter the matching line
283
284 # strip ending ; # ignore thru keyword # print remainder # strip start/end quotes
285 sed -n "s/;$//;s/^[ ]*$keyword \([^([].*\)*$/\1/p" | sed "s/^['\"]*//;s/['\"]*$//"
286}
287
288reference ()
289{
290 about displays apidoc help for a specific function
291 param 1: function name
292 example '$ reference revise'
293 group composure
294
295 typeset func=$1
296 if [ -z "$func" ]; then
297 printf '%s\n' 'missing parameter(s)'
298 reference reference
299 return
300 fi
301
302 typeset line
303
304 typeset about="$(typeset -f $func | metafor about)"
305 letterpress "$about" $func
306
307 typeset author="$(typeset -f $func | metafor author)"
308 if [ -n "$author" ]; then
309 letterpress "$author" 'author:'
310 fi
311
312 typeset version="$(typeset -f $func | metafor version)"
313 if [ -n "$version" ]; then
314 letterpress "$version" 'version:'
315 fi
316
317 if [ -n "$(typeset -f $func | metafor param)" ]; then
318 printf "parameters:\n"
319 typeset -f $func | metafor param | while read line
320 do
321 letterpress "$line"
322 done
323 fi
324
325 if [ -n "$(typeset -f $func | metafor example)" ]; then
326 printf "examples:\n"
327 typeset -f $func | metafor example | while read line
328 do
329 letterpress "$line"
330 done
331 fi
332}
333
334revise ()
335{
336 about loads function into editor for revision
337 param 1: name of function
338 example '$ revise myfunction'
339 group composure
340
341 typeset func=$1
342 typeset temp=$(mktemp /tmp/revise.XXXX)
343
344 if [ -z "$func" ]; then
345 printf '%s\n' 'missing parameter(s)'
346 reference revise
347 return
348 fi
349
350 # populate tempfile...
351 if [ -f ~/.composure/$func.inc ]; then
352 # ...with contents of latest git revision...
353 cat ~/.composure/$func.inc >> $temp
354 else
355 # ...or from ENV if not previously versioned
356 typeset -f $func >> $temp
357 fi
358
359 if [ -z "$EDITOR" ]
360 then
361 typeset EDITOR=vi
362 fi
363
364 $EDITOR $temp
365 . $temp # source edited file
366
367 transcribe $func $temp revise
368 rm $temp
369}
370
371write ()
372{
373 about writes one or more composed function definitions to stdout
374 param one or more function names
375 example '$ write finddown foo'
376 example '$ write finddown'
377 group composure
378
379 if [ -z "$1" ]; then
380 printf '%s\n' 'missing parameter(s)'
381 reference write
382 return
383 fi
384
385# bootstrap metadata
386cat <<END
387for f in $(composure_keywords)
388do
389 eval "\$f() { :; }"
390done
391unset f
392END
393
394 # include cite() to enable custom keywords
395 typeset -f cite $*
396}
397
Erich Smitha8fcf9f2012-04-27 15:07:04 -0400398: <<EOF
399License: The MIT License
400
401Copyright © 2012 Erich Smith
402
403Permission is hereby granted, free of charge, to any person obtaining a copy of this
404software and associated documentation files (the "Software"), to deal in the Software
405without restriction, including without limitation the rights to use, copy, modify,
406merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
407permit persons to whom the Software is furnished to do so, subject to the following
408conditions:
409
410The above copyright notice and this permission notice shall be included in all copies
411or substantial portions of the Software.
412
413THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
414INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
415PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
416HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
417CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
418OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
419EOF