dossier - dossier - console collection manager
DIR Log
DIR Files
DIR Refs
DIR Tags
DIR README
DIR LICENSE
---
dossier (7949B)
---
1 #!/bin/sh
2
3 REPOSITORY="${HOME}/.collections"
4 mkdir -p "$REPOSITORY"
5 test -f "${REPOSITORY}/current" && . "${REPOSITORY}/current"
6
7 if [ -n "$CONTEXT" ]
8 then
9 REPO="${REPOSITORY}/${CONTEXT}/"
10 cd "$REPO"
11 test ! -d ".git" && git init
12 else
13 printf 'No current collection in use\n'
14 fi
15
16 # return if an item exists
17 # $1 identifier
18 exists() {
19 cd "$REPO"
20 ls */"$1" >/dev/null 2>&1
21 return $?
22 }
23
24 # displays the values of an identifier
25 # $1 identifier
26 show() {
27 cd "$REPO"
28 if exists "$1"
29 then
30 SEEN=0
31 for attribute in *
32 do
33 if [ -f "${attribute}/${1}" ]
34 then
35 if [ "$SEEN" -eq 0 ]
36 then
37 printf "%s:\n" "$1"
38 SEEN=1
39 fi
40 printf "%15s: %s\n" "$attribute" "$(cat "${attribute}/${1}")"
41 fi
42 done
43 exit 0
44 else
45 printf '%s is not in the library.\n' "$1"
46 exit 1
47 fi
48 }
49
50 # displays the list of values per attributes
51 show_attributes_values() {
52 cd "$REPO"
53 for attribute in *
54 do
55 printf "%s:\n" "$attribute"
56 awk '{ print }' "${attribute}"/* | sort | uniq | sed 's/^/ /'
57 done
58 exit 0
59 }
60
61 # import data from a csv
62 # $1 is the file to import
63 import_csv() {
64 git tag "Importing CSV"
65 awk -F "," '{
66 if(NR==1) {
67 for(i=1;i<=NF;i=i+1) {
68 headers[i] = $i
69 }
70 } else {
71 out = $1;
72 for(i=2;i<=NF;i=i+1) {
73 if(! match($i,/^""$/) && $i !="") {
74 out = out " "headers[i]" "$i;
75 }
76 }
77 if(out==$1) {
78 print "empty"
79 } else {
80 out=out "\n"
81 print out
82 system("dossier "out)
83 }
84 }
85 }' "$1"
86 }
87
88 # export the data in csv format "data","data","data"
89 # we assume it'll works with the dataset
90 export_csv() {
91 cd "$REPO"
92
93 # display header
94 printf '"identifier"'
95 find . -name '.git' -prune -o -type f -print | cut -d '/' -f 2 | sort | uniq | while read attr
96 do
97 printf ',"%s"' $attr
98 done
99 printf '\n'
100
101 # print database
102 find . -name '.git' -prune -o -type f -print | cut -d '/' -f 3 | sort | uniq | while read id
103 do
104 printf '"%s",' "$id"
105 FIRST=0
106 find . -name '.git' -prune -o -type f -print | cut -d '/' -f 2 | sort | uniq | while read attr
107 do
108 if [ "$FIRST" -eq 0 ]
109 then
110 FIRST=1
111 else
112 printf ","
113 fi
114 # for faster processing, we do not check existence of file before
115 awk '{ printf "\"%s\"",$0}' "${attr}/${id}" 2>/dev/null \
116 || printf '""'
117 done
118 printf '\n'
119 done
120 IFS=$OLDIFS
121 exit 0
122 }
123
124 # delete identifier from attributes
125 # $1 identifier
126 delete() {
127 cd "$REPO"
128 SEEN=0
129 for attribute in *
130 do
131 if [ -f "${attribute}/${1}" ]
132 then
133 git rm "${attribute}/${1}"
134 git commit -m "Delete ${attribute} ${1}" "${attribute}/${1}"
135 rmdir "$attribute" 2> /dev/null
136 SEEN=1
137 fi
138 done
139
140 # did we find it?
141 if [ "$SEEN" -eq 0 ]
142 then
143 printf "%s is not in the library!\n" "$1"
144 exit 1
145 else
146 exit 0
147 fi
148 }
149
150 # displays list of identifiers
151 show_list() {
152 cd "$REPO"
153 find . -name '.git' -prune -o -type f -print | cut -d '/' -f 3 | sort | uniq -c | \
154 awk '{ for(i=2;i<=NF;i=i+1) { printf "%s ", $i }
155 printf "(%i)\n", $1
156 }'
157 exit 0
158 }
159
160 # displays attributes used
161 show_attributes() {
162 cd "$REPO"
163 find . -name '.git' -prune -o -type f -print | cut -d '/' -f 2 | sort | uniq -c | \
164 awk '{ for(i=2;i<=NF;i=i+1) { printf "%s ", $i }
165 printf "(%i)\n", $1
166 }'
167 exit 0
168 }
169
170 # add/modify a value
171 # $@ identifier / attr / value / attr / value / ....
172 # shift to have attr / value again and again
173 add_value() {
174 cd "$REPO"
175 ID="$1"
176 shift
177
178 while [ "$#" -gt 1 ]
179 do
180 ATTRIBUTE="$1"
181 VALUE="$2"
182 shift 2
183
184 mkdir -p "$ATTRIBUTE"
185 printf '%s' "$VALUE" > "${ATTRIBUTE}/${ID}"
186 git add "${ATTRIBUTE}/${ID}"
187 git commit -m "Modify ${ATTRIBUTE} on ${ID}" "${ATTRIBUTE}/${ID}"
188 done
189 exit 0
190 }
191
192 # returns identifiers having attribute=value
193 # $1 attribute
194 # $2 value
195 search_value() {
196 cd "$REPO"
197 shift
198
199 RESULT=$(mktemp /tmp/dossier.XXXXXXXXXXXX)
200 COUNT=0
201 if [ -f "$RESULT" ]
202 then
203 while [ "$#" -gt 1 ]
204 do
205 ATTRIBUTE="$1"
206 NEGATE=""
207 if [ "${ATTRIBUTE#!}" -ne "${ATTRIBUTE}" ]
208 then
209 ATTRIBUTE="${ATTRIBUTE#!}"
210 NEGATE=v
211 fi
212 VALUE="$2"
213 shift 2
214 if [ ! -d "$ATTRIBUTE" ]
215 then
216 printf 'The attribute %s do not exists\n' "$ATTRIBUTE"
217 exit 5
218 fi
219 grep "-rl${NEGATE}" "$VALUE" "$ATTRIBUTE" | cut -d '/' -f 2 >> "$RESULT"
220 COUNT=$(( COUNT + 1 ))
221 done
222 sort "$RESULT" | uniq -c | \
223 awk -v count=$COUNT \
224 '{ if($1==count) {
225 $1="" # remove uniq result
226 gsub(/^[ ]+/,"",$0) # remove leading space due to uniq
227 print
228 }}'
229 else
230 printf 'Cannot create a temporary file in /tmp\n'
231 exit 6
232 fi
233
234 exit 0
235 }
236
237 # returns list of identifiers in a attribute
238 # $1 attribute
239 list() {
240 cd "$REPO"
241 grep -r . "$1" | cut -d '/' -f 2- | sort
242 exit 0
243 }
244
245 # displays usage
246 usage() {
247 printf '%s\n' \
248 "Get help" \
249 ": dossier help" \
250 "" \
251 "Export data as CSV" \
252 ": dossier export" \
253 "" \
254 "Import data from CSV" \
255 ": dossier import file.csv" \
256 "" \
257 "Show collections, register collections, switch current collection" \
258 ": dossier collections [register path name] [name]" \
259 "" \
260 "Show items and display informations about an item" \
261 ": dossier show [attributes] [identifier]" \
262 "" \
263 "Look at attributes, search items having some values" \
264 ": dossier search [attribute [value]] ... [attribute [value]] ..." \
265 "" \
266 "Add / Modify attributes values on an item" \
267 ": dossier identifier attribute value ... attribute value ..."
268 exit 0
269 }
270
271 switch() {
272 if [ ! -L "${REPOSITORY}/${1}" ]
273 then
274 printf 'Collection %s is not registered\n' "$1"
275 exit 9
276 else
277 printf 'Switching to collection %s\n' "$1"
278 printf 'CONTEXT=%s\n' $1 > "${REPOSITORY}/current"
279 exit $?
280 fi
281 }
282
283 collections() {
284 ls "$REPOSITORY" | grep -v 'current' | sed "s/^${CONTEXT}$/& */"
285 exit 0
286 }
287
288 # create symlink to register a collection
289 # $1 absolute path to collection
290 # $2 name of collection
291 register() {
292 if [ -d "$1" ]
293 then
294 if ! expr "$1" : '^/' >/dev/null
295 then
296 printf 'Aborting, the path of the collection must be an absolute path. %s is not valid\n' "$1"
297 fi
298 test -L "${REPOSITORY}/${2}" && rm "${REPOSITORY}/${2}"
299 ln -s "$1" "${REPOSITORY}/${2}"
300 printf "%s registered. Use it as default with\n\t%s collections %s\n" "$2" "$0" "$2"
301 exit 0
302 else
303 printf 'Aborting, %s is not a directory\n' "$2"
304 exit 8
305 fi
306 }
307
308
309 # commands not requiring a repository set
310 if [ "$1" = "help" ] ; then usage ; fi
311
312 if [ "$1" = "collections" ]; then
313 if [ "$#" -eq 1 ]; then collections ; fi
314 if [ "$#" -eq 2 ]; then switch "$2" ; fi
315 if [ "$2" = "register" ] && [ "$#" -eq 4 ]; then register "$3" "$4" ; fi
316 fi
317
318 # quit if CONTEXT is not set
319 test -n "$CONTEXT" || exit 10
320
321
322 if [ "$1" = "import" ] && [ -f "${OLDPWD}/${2}" ]; then import_csv "${OLDPWD}/${2}" ; fi
323 if [ "$1" = "export" ] ; then export_csv ; fi
324 if [ "$1" = "rm" ] && [ "$#" -eq 2 ] ; then delete "$2" ; fi
325
326 # dealing with identifiers
327 if [ "$1" = "show" ]
328 then
329 if [ "$#" -eq 1 ]; then show_list ; fi
330 if [ "$#" -eq 2 ]; then
331 if [ "$2" = "attributes" ]
332 then
333 show_attributes_values
334 else
335 show "$2"
336 fi
337 fi
338 fi
339
340 # dealing with attributes
341 if [ "$1" = "search" ];
342 then
343 if [ "$#" -eq 1 ]; then show_attributes ; fi
344 if [ "$#" -eq 2 ]; then list "$2" ; fi
345 if [ "$#" -ge 3 ]; then search_value "$@" ; fi
346 fi
347
348 if [ "$#" -ge 3 ]; then add_value "$@" ; fi
349
350 # no command, maybe it's an item, try it
351 if [ "$#" -eq 1 ] && exists "$1" ; then show "$1" ; fi
352
353 usage