This repository has been archived by the owner on Nov 9, 2017. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
rpg-list.sh
196 lines (175 loc) · 5.88 KB
/
rpg-list.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/bin/sh
# The `rpg-list` program compares installed packages to packages available
# in the remote repository. It's useful for determining the versions of
# packages and how they relate to same named packages available in the
# repository.
#
# This is somewhere between a plumbing and porcelain command. It's useful
# for building other programs and provides options for generating easily
# parseable output. However, it's also useful to humans and the default
# output is optimize for human consumption.
set -e
. rpg-sh-setup
ARGV="$@"
USAGE '${PROGNAME} [-u] [-a] [-p|-s] [<glob>...]
List installed and/or available packages.
Passing one or more <glob>s filters the list to matching packages.
Options
-a Include packages available but not installed
-l Long list format: show package status and version fields
-p Generate easily parseable output
-u Sync the package index with remote repository first'
sync=false
parsey=false
long=false
all=false
joiner=
while getopts alpus opt
do
case $opt in
a) all=true
joiner="-a2";;
l) long=true;;
u) sync=true;;
p) long=true;
parsey=true;;
?) helpthem;
exit 2;;
esac
done
shift $(( $OPTIND - 1 ))
# Sync the package index. Force the sync right now if we were given
# the `-u` arg; otherwise, maybe update it based on the configured stale
# time.
if $sync
then rpg-sync
else rpg-sync -s
fi
# Parsey Mode
# -----------
# The `-p` argument causes the output to be varied slightly. These variables
# control how output lines are formatted and what symbols they use. In
# parsey mode, simple alpha characters are used since those are a bit easier
# to `grep` / `sed` without escaping.
if $parsey
then st_outdate="o"
st_up2date="u"
st_missing="x"
st_format="%s %s %s %s\n"
else
st_up2date=" "
st_outdate="*"
st_missing="X"
st_format="%1s %-35s %-12s %-12s\n"
fi
# Package Selection / Glob Filter
# -------------------------------
# Default to matching all installed packages -- or all available packages
# when `-a` was given -- if no `<glob>`s were given.
[ "$*" ] || set -- '*'
# Build glob BREs for filtering the remote package list. The local package
# list is filtered by `rpg-package-index` so we don't need to worry about that
# side.
#
# If there's only one glob and it's `*`, don't do any `grep` nonsense,
# just throw a `cat` in there.
if [ "$*" = '*' ]
then remotefilter="cat"
else remotefilter="grep"
for glob in "$@"
do glob=$(
echo "$glob" |
sed -e 's@\*@[^ ]*@g' -e 's/\?/[^ ]/g'
)
remotefilter="$remotefilter -e '^$glob ' -e ' $glob\$'"
done
fi
# Main Pipeline
# -------------
# Kick off a pipeline by listing installed packages. The output from
# `rpg-package-index` looks something like this:
#
#
# RedCloth 4.2.3
# abstract 1.0.0
# actionmailer 2.3.5
# actionpack 2.3.5
# activerecord 2.3.5
# activeresource 2.3.5
# activesupport 2.3.5
# ansi 1.1.0
# builder 2.1.2
# classifier 1.3.1
# coffee-script 0.3.2
# ...
#
# So we have the `<package> <version>` pairs separated by whitespace,
# basically.
rpg-package-index -x "$@" |
# Okay ...
#
# This is going to blow your mind.
#
# Use `join(1)` to perform a relational join between the installed
# package list and the recent release list from the index. The recent
# release list looks nearly identical, format-wise, to the installed
# list.
#
# There's a few things needed for this to work properly. First, both
# files need to be sorted (as with `sort -b`) on the join field. Here,
# the join field is the `<package>` -- we don't need to specify it
# explicitly on the command line because it's the first field in both
# files.
#
# The stream text that comes out of `join(1)` looks like:
#
# <package> <installed-version> <available-version>
#
# Without any other options, `join(1)` performs an "inner join",
# excluding unpaired lines from output entirely. Which is great because
# that's exactly what we need to determine which packages are out of
# date.
#
# This is a really insanely fast operation because it need only take a
# single pass over each file. It simply walks through each file line by
# line and, because both files are sorted, knows immediately whether the
# lines intersect or are unpairable.
#
# Additional, we selectively enable some of `join(1)`'s other options (`-a`
# and `-e`) for achieving "outer joins" and "full joins" when querying
# against all remote packages.
join -a 1 $joiner \
-o 1.1,1.2,2.2,2.1 \
-e '-' \
- "$RPGINDEX/release-recent" |
# Grep out remote packages based on our globs. See the *Glob Filter* section
# above for more information.
/bin/sh -c "exec $remotefilter" |
# Grep out lines that don't match a package. Also, the regular expression
# is amazing.
grep -v '. - - .' |
# All that's left is to read the output from `join` and apply some light
# formatting.
if ! $long
then
if $all
then awk '$4 != "-" { print $4, $3; }'
else awk '$2 != "-" { print $1, $2; }'
fi
else
while read package curvers recvers pdup
do
test "$package" = '-' &&
package="$pdup"
if test "$recvers" = '-'
then sig="$st_missing"
elif test "$curvers" = "-"
then sig="$st_missing"
curvers="-"
elif test "$curvers" = "$recvers"
then sig="$st_up2date"
else sig="$st_outdate"
fi
printf "$st_format" "$sig" "$package" "$curvers" "$recvers"
done
fi