Commit d7a3a5ac79404403268d59d6df184a66e3e3b5a5

Authored by Georg Hopp
1 parent 8a20eca3

finalize argument handling

... ... @@ -25,6 +25,10 @@ PROGRAM := accountmanager.sh
25 25
26 26 all: doc
27 27
  28 +README_new.md: doc/accountmanager.1
  29 + groff -D utf8 -mandoc -Thtml "$<" |\
  30 + pandoc -f html -t markdown_strict >"$@"
  31 +
28 32 .PHONY: clean install doc
29 33
30 34 doc:
... ...
... ... @@ -14,7 +14,6 @@ Database management functions:
14 14
15 15 Account management functions:
16 16 - **amngradd** − add an account credential to the database
17   - - **amngrcreate** − create a new account credential to the database
18 17 - **amngrcrypt** − crypt the given data with GnuPG
19 18 - **amngrdelete** − delete an account
20 19 - **amngrgen** − generate a encrypted random passphrase
... ... @@ -23,7 +22,6 @@ Account management functions:
23 22 - **amngrgetpass** − copy active password of account to X clipboard
24 23 - **amngrgetuser** − copy active username of account to X clipboard
25 24 - **amngrid** − write the database id of a given account name to stdout
26   - - **amngrlist** − list all accounts
27 25 - **amngrrename** − rename an account
28 26 - **amngrsearch** − pattern search accounts
29 27
... ... @@ -33,39 +31,35 @@ source **${PATH}/accountmanager.sh**
33 31
34 32 **random**
35 33
36   -**rand\_printable** \[*len*\]
  34 +**rand\_printable** \[**-h**\] \[*len*\]
37 35
38   -**amngrdbinit** \[*dbfile*\]
  36 +**amngrdbinit** \[**-h**\] \[*dbfile*\]
39 37
40   -**amngrdbdestroy** \[*dbfile*\]
  38 +**amngrdbdestroy** \[**-h**\] \[*dbfile*\]
41 39
42   -**amngradd** **-u** *username* **-p** *password* \[**-d** *description*\]
43   -\[**-D** *dbfile*\] \[**-r** *recipient*\] *account*
  40 +**amngradd** \[**-h**\] \[**-d** *description*\] \[**-D** *dbfile*\]
  41 +\[**-r** *recipient*\] *account* \[*username* \[*password*\]\]
44 42
45   -**amngrcreate** **-u** *username* \[**-d** *description*\] \[**-D**
46   -*dbfile*\] \[**-r** *recipient*\] *account*
  43 +**amngrcrypt** \[**-h**\] \[**-r** *recipient*\] *password*
47 44
48   -**amngrcrypt** \[**-r** *recipient*\] *password*
  45 +**amngrdelete** \[**-h**\] \[**-D** *dbfile*\] *account*
49 46
50   -**amngrdelete** *account*
  47 +**amngrgen** \[**-h**\] \[**-r** *recipient*\] \[*len*\]
51 48
52   -**amngrgen** \[**-r** *recipient*\] \[*len*\]
  49 +**amngrgetoldpass** \[**-h**\] \[**-D** *dbfile*\] *account*
53 50
54   -**amngrgetoldpass** *account*
  51 +**amngrgetolduser** \[**-h**\] \[**-c**\] \[**-D** *dbfile*\] *account*
55 52
56   -**amngrgetolduser** *account*
  53 +**amngrgetpass** \[**-h**\] \[**-D** *dbfile*\] *account* \[*state*\]
57 54
58   -**amngrgetpass** *account*
  55 +**amngrgetuser** \[**-h**\] \[**-c**\] \[**-D** *dbfile*\] *account* \[*state*\]
59 56
60   -**amngrgetuser** *account*
  57 +**amngrid** \[**-h**\] \[**-D** *dbfile*\] *account*
61 58
62   -**amngrid** *account*
  59 +**amngrrename** \[**-h**\] \[**-D** *dbfile*\] *old\_account* *new\_account*
63 60
64   -**amngrlist** \[**-s** *separator*\]
65   -
66   -**amngrrename** *old\_account new\_account*
67   -
68   -**amngrsearch** \[**-s** *separator*\] *pattern*
  61 +**amngrsearch** \[**-h**\] \[**-D** *dbfile*\] \[**-s** *separator*\]
  62 +\[*pattern*\]
69 63
70 64 ## DESCRIPTION
71 65
... ... @@ -84,55 +78,58 @@ pair.
84 78
85 79 Takes no arguments and connect a non blocking random source to stdout.
86 80
87   -**rand\_printable** \[*len*\]
  81 +**rand\_printable** \[**-h**\] \[*len*\]
88 82
89 83 Uses **random** to write a string of random printable characters to
90 84 stdout. All control characters ASCII-0 to ASCII-37 as well as ASCII-177
91 85 to 255, single and double quotes are filtered. The single and double
92 86 quotes are filtered to prevent problems when they are used as string
93 87 separators after shell expansion as in the SQL here documents used to
94   -communicate with the SQLite database.
  88 +communicate with the SQLite database.
95 89 The optional *len* argument specifies the string length to be written
96 90 and defaults to 512.
97 91
98   -**amngrdbinit** \[*dbfile*\]
  92 +**amngrdbinit** \[**-h**\] \[*dbfile*\]
99 93
100 94 Create the SQLite database file. If the optional *dbfile* argument is
101 95 given it specifies the fill path to the file to use, else the value of
102 96 **$AMNGRDB** environment variable is used.
103 97
104   -**amngrdbdestroy** \[*dbfile*\]
  98 +**amngrdbdestroy** \[**-h**\] \[*dbfile*\]
105 99
106 100 Deletes the SQLite database file. If the optional *dbfile* argument is
107 101 given it specifies the fill path to the file to use, else the value of
108 102 **$AMNGRDB** environment variable is used.
109 103
110   -**amngradd -u** *username* **-p** *password* \[**-d** *description*\]
111   -\[**-D** *dbfile*\] \[**-r** *recipient*\] *account*
  104 +**amngradd** \[**-h**\] \[**-d** *description*\] \[**-D** *dbfile*\]
  105 +\[**-r** *recipient*\] *account* \[*username* \[*password*\]\]
112 106
113 107 Adds an account credential and marks it as active. If the account
114 108 already exist, the credential (*username* and *password*) is added to
115 109 that account and the previously added credential is marked as old. If
116 110 there was another even older credential that was already marked as old
117   -this will be marked as inactive and this becomes inaccessible with this
118   -tools (except for **amngrdelete**).
119   -The necessary option **-u** specifies the username to be stored with
120   -this credential pair.
121   -The also necessary option **-p** specifies the password to be stored
122   -and the length of *password* is not limited at all.
123   -With option **-d** one can add a *description* to the account entry.
124   -One can specify the *dbfile* with the option **-D** if that option is
125   -not given the **$AMNGRDB** environment variable is used.
126   -
127   -**amngrcreate -u** *username* \[**-d** *description*\] \[**-D**
128   -*dbfile*\] \[**-r** *recipient*\] *account*
129   -
130   -This will create a password with **amngrgen** and use that to add an
131   -credential via **amngradd**. (See there for options description)
132   -The added password will be copied to the X clipboard with
133   -**amngrgetpass** for further use.
134   -
135   -**amngrcrypt** \[**-r** *recipient*\] *password*
  111 +this will be marked as inactive and thus becomes inaccessible with this
  112 +tools (except for **amngrdelete**).
  113 +
  114 +The *username* and the *account* name must not be longer than 128
  115 +characters. The *password* is not limited in its length at all. If
  116 +*password* is omitted one will be generated with **amngrgen**.
  117 +
  118 +The *username* may also be omitted if there was a previously added
  119 +credentials pair for this account. In that case the previous username is
  120 +taken. It is a failure to provide a *password* without a *username*.
  121 +There is no way for the script to detect this condition and you will end
  122 +up with a credential where the username is the password and the password
  123 +was generated.
  124 +
  125 +Option **-d** adds a *description* to the account entry and option
  126 +**-D** specifys the *dbfile* to use. If that option is not given the
  127 +**$AMNGRDB** environment variable is used.
  128 +
  129 +If a password was generated with this call it will be stored in the X
  130 +clipboard.
  131 +
  132 +**amngrcrypt** \[**-h**\] \[**-r** *recipient*\] *password*
136 133
137 134 Crypt the given plain text *password* with GnuPG and write it to
138 135 stdout.
... ... @@ -140,58 +137,59 @@ The option **-r** specifies the recipient to use with the call to
140 137 **gpg**. If it is not given the value of the **$AMNGRID** environment
141 138 variable is used.
142 139
143   -**amngrdelete** *account*
  140 +**amngrdelete** \[**-h**\] \[**-D** *dbfile*\] *account*
144 141
145 142 Remove the *account* and all credential associated to it.
146 143
147   -**amngrgen** \[**-r** *receipient*\] \[*len*\]
  144 +**amngrgen** \[**-h**\] \[**-r** *receipient*\] \[*len*\]
148 145
149 146 Generate a password with **rand\_printable** and encrypt it via
150 147 **amngrcrypt**. By default the password will be 10 characters long. That
151 148 can be modified by the optional *len* argument.
152 149
153   -**amngrgetoldpass** *account*
  150 +**amngrgetoldpass** \[**-h**\] \[**-D** *dbfile*\] *account*
154 151
155 152 Read and decrypt the password associated with *account* that is flagged
156 153 as old and store it into the X clipboard.
157 154
158   -**amngrgetolduser** *account*
  155 +**amngrgetolduser** \[**-h**\] \[**-c**\] \[**-D** *dbfile*\] *account*
159 156
160 157 Read and decrypt the username associated with *account* that is flagged
161 158 as old and store it into the X clipboard.
162 159
163   -**amngrgetpass** *account*
  160 +**amngrgetpass** \[**-h**\] \[**-D** *dbfile*\] *account* \[*state*\]
164 161
165 162 Read and decrypt the password associated with *account* that is flagged
166   -as active and store it into the X clipboard.
  163 +as active and store it into the X clipboard. The *state* argument might
  164 +be either 1 or 2 where 1 means get the current credential and 2 means
  165 +get the old credential. It defaults to 1.
167 166
168   -**amngrgetuser** *account*
  167 +**amngrgetuser** \[**-h**\] \[**-c**\] \[**-D** *dbfile*\] *account* \[*state*\]
169 168
170 169 Read and decrypt the username associated with *account* that is flagged
171   -as active and store it into the X clipboard.
  170 +as active and store it into the X clipboard. The *state* argument might
  171 +be either 1 or 2 where 1 means get the current credential and 2 means
  172 +get the old credential. It defaults to 1.
172 173
173   -**amngrid** *account*
  174 +**amngrid** \[**-h**\] \[**-D** *dbfile*\] *account*
174 175
175 176 (This is primarily for internal use.) Get the database id associated to
176 177 the given *account* argument.
177 178
178   -**amngrlist** \[**-s** *separator*\]
179   -
180   -List all accounts currently stored within the database the output will
181   -contain the name of the account, the username and the description
182   -delimited by a *separator* string that can be specified with the **-s**
183   -option. If the option is omitted the separator is " =&gt; ". Each row is
184   -one account.
185   -
186   -**amngrrename** *old\_account new\_account*
  179 +**amngrrename** \[**-h**\] \[**-D** *dbfile*\] *old\_account
  180 +new\_account*
187 181
188 182 Rename the account specified by *old\_account* to *new\_account*.
189 183
190   -**amngrsearch** \[**-s** *separator*\] *pattern*
  184 +**amngrsearch** \[**-h**\] \[**-D** *dbfile*\] \[**-s** *separator*\]
  185 +\[*pattern*\]
191 186
192   -List accounts where *pattern* exists in either the account name, the
193   -username or the account description. The output is like the one
194   -described with **amngrlist**.
  187 +List all accounts where *pattern* exists in either the account name, the
  188 +username or the account description. The output will contain the name of
  189 +the account, the username and the description delimited by a *separator*
  190 +string that can be specified with the **-s** option. If the option is
  191 +omitted the separator is " => ". Each row is one account.
  192 +If *pattern* is omitted all accounts will be listed.
195 193
196 194 ## OPTIONS
197 195
... ... @@ -199,10 +197,17 @@ The options are consistent over all sub commands. However not all sub
199 197 commands use all options and some sub commands take arguments that other
200 198 get per option. (See **DESCRIPTION**)
201 199
  200 +**-h** − Write a short usage information.
  201 +
202 202 **-u** − The login username of the credential for the account.
203 203
204 204 **-p** − The plain text password of the credential for the account.
205 205
  206 +**-c** − By default this scripts stores the username into the X clipboard
  207 + whith **ambgrgetuser** and **amngrgetolduser**. When **-c** is given
  208 + the username is written to stdout. The password is never written to
  209 + stdout.
  210 +
206 211 **-d** − The description for the account.
207 212
208 213 **-D** − Select the database file to use instead of the one defined in the
... ... @@ -211,8 +216,8 @@ get per option. (See **DESCRIPTION**)
211 216 **-r** − Select a recipient id for GnuPG encryption to use instead of the one
212 217 defined in the **$AMNGRID** environment variable.
213 218
214   -**-s** − The column separator for the **amngrlist** and
215   - **amngrsearchcommands**.
  219 +**-s** − The column separator for the **amngrlist** and **amngrsearch**
  220 + commands.
216 221
217 222 ## ENVIRONMENT
218 223
... ... @@ -231,7 +236,8 @@ option.
231 236
232 237 **AMNGRPWLEN**
233 238
234   -The password length to be used when generating new passwords.
  239 +The password length to be used when generating new passwords. If not
  240 +specified this defaults to 10.
235 241
236 242 ## DEPENDENCIES
237 243
... ... @@ -257,7 +263,7 @@ To store the data within the X clipboard the **xclip** is needed.
257 263 - **awk**
258 264 - **basename**
259 265 - **cat**
260   - - **dnsdomainname**
  266 + - **dirname**
261 267 - **echo**
262 268 - **getopts**
263 269 - **gpg**
... ...
... ... @@ -7,15 +7,54 @@ AMNGRPWLEN="${AMNGRPWLEN:-10}"
7 7 alias random="cat /dev/urandom"
8 8
9 9 function rand_printable() {
10   - if ! [[ "${1}" =~ "^[0-9]+$" ]]
  10 + local OPT OPTARG OPTIND
  11 + local USAGE="$(printf "Usage: %s [-h] [len]" "$0")"
  12 +
  13 + while getopts h OPT
  14 + do
  15 + case $OPT in
  16 + h)
  17 + echo "${USAGE}"
  18 + return 0;;
  19 + ?)
  20 + echo "${USAGE}"
  21 + return 1;;
  22 + esac
  23 + done
  24 + shift $(($OPTIND-1))
  25 +
  26 + if ! [[ "${1}" =~ ^[0-9]*$ ]]
11 27 then
12   - printf "Usage: %s: [len]" "$0"
  28 + echo "${USAGE}"
  29 + return 1
13 30 fi
  31 +
14 32 echo -n "$(random | tr -dc ' !#-&(-~' | head -c${1:-512})"
15 33 }
16 34
17 35 function amngrdbinit() {
18   - local DB="${1:-${AMNGRDB}}"
  36 + local OPT OPTARG OPTIND DB
  37 + local USAGE="$(printf "Usage: %s [-h] [dbfile]" "$0")"
  38 +
  39 + while getopts h OPT
  40 + do
  41 + case $OPT in
  42 + h)
  43 + echo "${USAGE}"
  44 + return 0;;
  45 + ?)
  46 + echo "${USAGE}"
  47 + return 1;;
  48 + esac
  49 + done
  50 + shift $(($OPTIND-1))
  51 +
  52 + DB="${1:-${AMNGRDB}}"
  53 + if [ ! \( -d "$(dirname "${DB}")" \) ]
  54 + then
  55 + echo "${USAGE}"
  56 + return 1
  57 + fi
19 58
20 59 sqlite3 "${DB}" <<-EOD
21 60 PRAGMA foreign_keys = ON;
... ... @@ -39,43 +78,198 @@ function amngrdbinit() {
39 78 }
40 79
41 80 function amngrdbdestroy() {
42   - local DB="${1:-${AMNGRDB}}"
43   - local CHECK
  81 + local OPT OPTARG OPTIND DB CHECK
  82 + local USAGE="$(printf "Usage: %s [-h] [dbfile]" "$0")"
  83 +
  84 + while getopts h OPT
  85 + do
  86 + case $OPT in
  87 + h)
  88 + echo "${USAGE}"
  89 + return 0;;
  90 + ?)
  91 + echo "${USAGE}"
  92 + return 1;;
  93 + esac
  94 + done
  95 + shift $(($OPTIND-1))
44 96
  97 + DB="${1:-${AMNGRDB}}"
45 98 cat <<-EOT
46 99 WARNING: You are about to remove your account data. There is no way to
47   - recover from this. Are you really shure you want to do this?
  100 + recover from this. Are you really sure you want to do this?
48 101 EOT
49   - echo -n "[Yes|[No]]: " && read CHECK
  102 + printf "Really remove %s (Yes|[No]): " "${DB}" && read CHECK
50 103 CHECK="${CHECK:-No}"
51 104
52   - test "${CHECK}" == "Yes" && rm -f "${AMNGRDB}"
  105 + test "${CHECK}" == "Yes" && rm -f "${DB}"
53 106 }
54 107
55 108 function amngrid() {
56   - local ACCOUNT_NAME="${1}"
57   - echo "$(sqlite3 "${AMNGRDB}" <<-EOD
  109 + local OPT OPTARG OPTIND ACCOUNT_NAME DB
  110 + local USAGE="$(printf "Usage: %s [-h] [-D dbfile] account" "$0")"
  111 +
  112 + while getopts hD: OPT
  113 + do
  114 + case $OPT in
  115 + D)
  116 + DB="${OPTARG}";;
  117 + h)
  118 + echo "${USAGE}"
  119 + return 0;;
  120 + ?)
  121 + echo "${USAGE}"
  122 + return 1;;
  123 + esac
  124 + done
  125 + shift $(($OPTIND-1))
  126 +
  127 + ACCOUNT_NAME="${1}"
  128 + DB="${DB:-${AMNGRDB}}"
  129 +
  130 + if [ -z "${ACCOUNT_NAME}" ]
  131 + then
  132 + echo "${USAGE}"
  133 + return 1
  134 + fi
  135 +
  136 + echo "$(sqlite3 "${DB}" <<-EOD
58 137 SELECT id FROM account WHERE name='${ACCOUNT_NAME}';
59 138 EOD
60 139 )"
61 140 }
62 141
63 142 function amngrcrypt() {
64   - local PLAIN="${1}"
65   - echo -n "${PLAIN}" | gpg -aeqr "${AMNGRID}"
  143 + local OPT OPTARG OPTIND PLAIN RECIPIENT
  144 + local USAGE="$(printf "Usage: %s [-h] [-r recipient] password" "$0")"
  145 +
  146 + while getopts hr: OPT
  147 + do
  148 + case $OPT in
  149 + r)
  150 + RECIPIENT="${OPTARG}";;
  151 + h)
  152 + echo "${USAGE}"
  153 + return 0;;
  154 + ?)
  155 + echo "${USAGE}"
  156 + return 1;;
  157 + esac
  158 + done
  159 + shift $(($OPTIND-1))
  160 +
  161 + RECIPIENT="${RECIPIENT:-${AMNGRID}}"
  162 + PLAIN="${1}"
  163 +
  164 + if [ -z "${PLAIN}" ]
  165 + then
  166 + echo "${USAGE}"
  167 + return 1
  168 + fi
  169 +
  170 + echo -n "${PLAIN}" | gpg -aeqr "${RECIPIENT}"
66 171 }
67 172
68 173 function amngrgen() {
69   - local LEN="${0:-${AMNGRPWLEN}}"
70   - amngrcrypt "$(rand_printable "${LEN}")"
  174 + local OPT OPTARG OPTIND RECIPIENT
  175 + local USAGE="$(printf "Usage: %s [-h] [-r recipient] [len]" "$0")"
  176 +
  177 + while getopts hr: OPT
  178 + do
  179 + case $OPT in
  180 + r)
  181 + RECIPIENT="${OPTARG}";;
  182 + h)
  183 + echo "${USAGE}"
  184 + return 0;;
  185 + ?)
  186 + echo "${USAGE}"
  187 + return 1;;
  188 + esac
  189 + done
  190 + shift $(($OPTIND-1))
  191 +
  192 + local LEN="${1:-${AMNGRPWLEN}}"
  193 +
  194 + if [ "${RECIPIENT}" ]
  195 + then
  196 + amngrcrypt -r "${RECIPIENT}" "$(rand_printable "${LEN}")"
  197 + else
  198 + amngrcrypt "$(rand_printable "${LEN}")"
  199 + fi
71 200 }
72 201
73 202 function amngradd() {
  203 + local OPT OPTARG OPTIND DESCRIPTION RECIPIENT DB
  204 + local USAGE="$(cat <<-EOT
  205 + Usage: $0 [-h] [-d description] [-D dbfile] [-r recipient]
  206 + account [username [password]]
  207 + EOT
  208 + )"
  209 +
  210 + while getopts d:D:hr: OPT
  211 + do
  212 + case $OPT in
  213 + d)
  214 + DESCRIPTION="${OPTARG}";;
  215 + D)
  216 + DB="${OPTARG}";;
  217 + r)
  218 + RECIPIENT="${OPTARG}";;
  219 + h)
  220 + echo "${USAGE}"
  221 + return 0;;
  222 + ?)
  223 + echo "${USAGE}"
  224 + return 1;;
  225 + esac
  226 + done
  227 + shift $(($OPTIND-1))
  228 +
  229 + RECIPIENT="${RECIPIENT:-${AMNGRID}}"
  230 + DB="${DB:-${AMNGRDB}}"
  231 +
74 232 local ACCOUNT_NAME="${1}"
75   - local USER="${2}"
76   - local PASSWORD="$(amngrcrypt "${3}")"
77   - local DESCRIPTION="${4:-NO DESCRIPTION}"
78   - local ACCOUNT_ID="$(amngrid "${ACCOUNT_NAME}")"
  233 + DESCRIPTION="${DESCRIPTION:-NO DESCRIPTION}"
  234 +
  235 + if [ -z "${ACCOUNT_NAME}" ]
  236 + then
  237 + echo "No account name given."
  238 + echo "${USAGE}"
  239 + return 1
  240 + fi
  241 +
  242 + if [ "${2}" ]
  243 + then
  244 + USER="${2}"
  245 + else
  246 + USER="$(amngrgetuser -D "${DB}" -c "${ACCOUNT_NAME}")"
  247 + fi
  248 +
  249 + if [ -z "${USER}" ]
  250 + then
  251 + printf "Can't find current user for account %s\n" "${ACCOUNT_NAME}"
  252 + echo "${USAGE}"
  253 + return 2
  254 + fi
  255 +
  256 + if [ "${3}" ]
  257 + then
  258 + PASSWORD="$(amngrcrypt -r "${RECIPIENT}" "${3}")"
  259 + else
  260 + PASSWORD="$(amngrgen -r "${RECIPIENT}")"
  261 + echo -n "${PASSWORD}" | gpg -dq | xclip -i
  262 + fi
  263 +
  264 + if [ -z "${PASSWORD}" ]
  265 + then
  266 + printf "Failed to create encrypted password for account %s\n" \
  267 + "${ACCOUNT_NAME}"
  268 + echo "${USAGE}"
  269 + return 3
  270 + fi
  271 +
  272 + local ACCOUNT_ID="$(amngrid -D "${DB}" "${ACCOUNT_NAME}")"
79 273 local QUERY="$(cat <<-EOD
80 274 PRAGMA foreign_keys = ON;
81 275 BEGIN TRANSACTION;
... ... @@ -115,46 +309,100 @@ function amngradd() {
115 309 EOD
116 310 )"
117 311
118   - sqlite3 "${AMNGRDB}" "${QUERY}"
  312 + sqlite3 "${DB}" "${QUERY}"
119 313 }
120 314
121   -function amngrcreate() {
122   - local ACCOUNT_NAME="${1}"
123   - local USER="${2}"
124   - local DESCRIPTION="${3:-NO DESCRIPTION}"
125   - local PASSWORD="$(rand_printable 10)"
  315 +function amngrgetuser() {
  316 + local OPT OPTARG OPTIND DB
  317 + local STDOUT=0
  318 + local USAGE="$(cat <<-EOT
  319 + Usage: $0 [-h] [-c] [-D dbfile] account [state]
  320 + EOT
  321 + )"
126 322
127   - amngradd "${ACCOUNT_NAME}" "${USER}" "${PASSWORD}" "${DESCRIPTION}"
128   - amngrgetpass "${ACCOUNT_NAME}"
129   -}
  323 + while getopts hcD: OPT
  324 + do
  325 + case $OPT in
  326 + c)
  327 + STDOUT=1;;
  328 + D)
  329 + DB="${OPTARG}";;
  330 + h)
  331 + echo "${USAGE}"
  332 + return 0;;
  333 + ?)
  334 + echo "${USAGE}"
  335 + return 1;;
  336 + esac
  337 + done
  338 + shift $(($OPTIND-1))
130 339
131   -function amngrgetuser() {
132 340 local ACCOUNT_NAME="${1}"
133 341 local STATE="${2:-"1"}"
  342 + DB="${DB:-${AMNGRDB}}"
134 343
  344 + if [ -z "${ACCOUNT_NAME}" ]
  345 + then
  346 + echo "${USAGE}"
  347 + return 1
  348 + fi
135 349 test "${STATE}" != "1" -a "${STATE}" != "2" && STATE="1"
136 350
137   - sqlite3 "${AMNGRDB}" <<-EOD |\
138   - awk 'NR>1{print p}{p=$0}END{ORS="";print}' | xclip -i
139   - SELECT user FROM account
140   - JOIN account_cred ON account.id=account_cred.account_id
141   - JOIN cred ON cred.id=account_cred.cred_id
142   - WHERE name='${ACCOUNT_NAME}' AND state=${STATE};
143   - EOD
  351 + if [ $STDOUT -eq 0 ]
  352 + then
  353 + sqlite3 "${DB}" <<-EOD |\
  354 + awk 'NR>1{print p}{p=$0}END{ORS="";print}' | xclip -i
  355 + SELECT user FROM account
  356 + JOIN account_cred ON account.id=account_cred.account_id
  357 + JOIN cred ON cred.id=account_cred.cred_id
  358 + WHERE name='${ACCOUNT_NAME}' AND state=${STATE};
  359 + EOD
  360 + else
  361 + sqlite3 "${DB}" <<-EOD |\
  362 + awk 'NR>1{print p}{p=$0}END{ORS="";print}'
  363 + SELECT user FROM account
  364 + JOIN account_cred ON account.id=account_cred.account_id
  365 + JOIN cred ON cred.id=account_cred.cred_id
  366 + WHERE name='${ACCOUNT_NAME}' AND state=${STATE};
  367 + EOD
  368 + fi
144 369 }
145 370
146 371 function amngrgetolduser() {
147   - local ACCOUNT_NAME="${1}"
148   - amngrgetuser "${ACCOUNT_NAME}" "2"
  372 + amngrgetuser "$@" "2"
149 373 }
150 374
151 375 function amngrgetpass() {
  376 + local OPT OPTARG OPTIND DB
  377 + local USAGE="$(printf "Usage: %s [-h] [-D dbfile] account [state]" "$0")"
  378 +
  379 + while getopts hD: OPT
  380 + do
  381 + case $OPT in
  382 + D)
  383 + DB="${OPTARG}";;
  384 + h)
  385 + echo "${USAGE}"
  386 + return 0;;
  387 + ?)
  388 + echo "${USAGE}"
  389 + return 1;;
  390 + esac
  391 + done
  392 + shift $(($OPTIND-1))
  393 +
152 394 local ACCOUNT_NAME="${1}"
153 395 local STATE="${2:-1}"
  396 + DB="${DB:-${AMNGRDB}}"
154 397
  398 + if [ -z "${ACCOUNT_NAME}" ]
  399 + then
  400 + echo "${USAGE}"
  401 + return 1
  402 + fi
155 403 test "${STATE}" != "1" -a "${STATE}" != "2" && STATE="1"
156 404
157   - sqlite3 "${AMNGRDB}" <<-EOD |\
  405 + sqlite3 "${DB}" <<-EOD |\
158 406 awk 'NR>1{print p}{p=$0}END{ORS="";print}' | gpg -dq | xclip -i
159 407 SELECT pass FROM account
160 408 JOIN account_cred ON account.id=account_cred.account_id
... ... @@ -164,26 +412,76 @@ function amngrgetpass() {
164 412 }
165 413
166 414 function amngrgetoldpass() {
167   - local ACCOUNT_NAME="${1}"
168   - amngrgetpass "${ACCOUNT_NAME}" "2"
  415 + amngrgetpass "$@" "2"
169 416 }
170 417
171 418 function amngrrename() {
  419 + local OPT OPTARG OPTIND DB
  420 + local USAGE="$(cat <<-EOT
  421 + Usage: $0 [-h] [-D dbfile] old_account new_account
  422 + EOT
  423 + )"
  424 +
  425 + while getopts hD: OPT
  426 + do
  427 + case $OPT in
  428 + D)
  429 + DB="${OPTARG}";;
  430 + h)
  431 + echo "${USAGE}"
  432 + return 0;;
  433 + ?)
  434 + echo "${USAGE}"
  435 + return 1;;
  436 + esac
  437 + done
  438 + shift $(($OPTIND-1))
  439 +
172 440 local OLD_NAME="${1}"
173 441 local NEW_NAME="${2}"
  442 + DB="${DB:-${AMNGRDB}}"
174 443
175   - test -z "${OLD_NAME}" -o -z "${NEW_NAME}" && return 1
  444 + if [ -z "${OLD_NAME}" -o -z "${NEW_NAME}" ]
  445 + then
  446 + echo "${USAGE}"
  447 + return 1
  448 + fi
176 449
177   - sqlite3 "${AMNGRDB}" <<-EOD
  450 + sqlite3 "${DB}" <<-EOD
178 451 UPDATE account SET name='${NEW_NAME}'
179 452 WHERE name='${OLD_NAME}';
180 453 EOD
181 454 }
182 455
183 456 function amngrdelete() {
  457 + local OPT OPTARG OPTIND DB
  458 + local USAGE="$(printf "Usage: %s [-h] [-D dbfile] account" "$0")"
  459 +
  460 + while getopts hD: OPT
  461 + do
  462 + case $OPT in
  463 + D)
  464 + DB="${OPTARG}";;
  465 + h)
  466 + echo "${USAGE}"
  467 + return 0;;
  468 + ?)
  469 + echo "${USAGE}"
  470 + return 1;;
  471 + esac
  472 + done
  473 + shift $(($OPTIND-1))
  474 +
184 475 local ACCOUNT_NAME="${1}"
  476 + DB="${DB:-${AMNGRDB}}"
  477 +
  478 + if [ -z "${ACCOUNT_NAME}" ]
  479 + then
  480 + echo "${USAGE}"
  481 + return 1
  482 + fi
185 483
186   - sqlite3 "${AMNGRDB}" <<-EOD
  484 + sqlite3 "${DB}" <<-EOD
187 485 PRAGMA foreign_keys = ON;
188 486 BEGIN TRANSACTION;
189 487 DELETE FROM cred WHERE id IN (
... ... @@ -195,42 +493,35 @@ function amngrdelete() {
195 493 EOD
196 494 }
197 495
198   -function amngrlist() {
199   - local SEPARATOR="${1:-" => "}"
200   - sqlite3 -separator "${SEPARATOR}" "${AMNGRDB}" <<-EOD
201   - SELECT name, user, desc FROM account
202   - JOIN account_cred ON account.id=account_cred.account_id
203   - JOIN cred ON cred.id=account_cred.cred_id
204   - WHERE state=1;
205   - EOD
206   -}
207   -
208 496 function amngrsearch() {
209   - local PATTERN
210   - local DELIMITER=" => "
211   - local USAGE="$(printf "Usage: %s: [-d delimiter] pattern" "$0")"
  497 + local OPT OPTARG OPTIND SEPARATOR PATTERN
  498 + local USAGE="$(cat <<-EOT
  499 + Usage: $0 [-h] [-D dbfile] [-s separator] [pattern]
  500 + EOT
  501 + )"
212 502
213   - while getopts d: opt
  503 + while getopts hD:s: OPT
214 504 do
215   - case $opt in
216   - d)
217   - DELIMITER="${OPTARG}";;
  505 + case $OPT in
  506 + D)
  507 + DB="${OPTARG}";;
  508 + s)
  509 + SEPARATOR="${OPTARG}";;
  510 + h)
  511 + echo "${USAGE}"
  512 + return 0;;
218 513 ?)
219 514 echo "${USAGE}"
220   - exit 1;;
  515 + return 1;;
221 516 esac
222 517 done
223 518 shift $(($OPTIND-1))
224 519
225   - if [ $# -lt 2 ]
226   - then
227   - echo "${USAGE}"
228   - exit 1;;
229   - fi
230   -
231 520 PATTERN="${1}"
  521 + SEPARATOR="${SEPARATOR:-" => "}"
  522 + DB="${DB:-${AMNGRDB}}"
232 523
233   - sqlite3 -separator " => " "${AMNGRDB}" <<-EOD
  524 + sqlite3 -separator "${SEPARATOR}" "${DB}" <<-EOD
234 525 SELECT name, user, desc FROM account
235 526 JOIN account_cred ON account.id=account_cred.account_id
236 527 JOIN cred ON cred.id=account_cred.cred_id
... ... @@ -242,10 +533,10 @@ function amngrsearch() {
242 533
243 534 case "$(basename -- "$0")" in
244 535 random) random;;
245   - rand_printable) rand_printable;;
  536 + rand_printable) rand_printable "$@";;
246 537
247   - amngrdbinit) amngrdbinit;;
248   - amngrdbdestroy) amngrdbdestroy;;
  538 + amngrdbinit) amngrdbinit "$@";;
  539 + amngrdbdestroy) amngrdbdestroy "$@";;
249 540
250 541 amngradd) amngradd "$@";;
251 542 amngrcreate) amngrcreate "$@";;
... ... @@ -257,7 +548,7 @@ case "$(basename -- "$0")" in
257 548 amngrgetpass) amngrgetpass "$@";;
258 549 amngrgetuser) amngrgetuser "$@";;
259 550 amngrid) amngrid "$@";;
260   - amngrlist) amngrlist;;
  551 + amngrlist) amngrlist "$@";;
261 552 amngrrename) amngrrename "$@";;
262 553 amngrsearch) amngrsearch "$@";;
263 554
... ...
... ... @@ -15,7 +15,6 @@ Database management functions:
15 15
16 16 Account management functions:
17 17 amngradd \- add an account credential to the database
18   - amngrcreate \- create a new account credential to the database
19 18 amngrcrypt \- crypt the given data with GnuPG
20 19 amngrdelete \- delete an account
21 20 amngrgen \- generate a encrypted random passphrase
... ... @@ -24,7 +23,6 @@ Account management functions:
24 23 amngrgetpass \- copy active password of account to X clipboard
25 24 amngrgetuser \- copy active username of account to X clipboard
26 25 amngrid \- write the database id of a given account name to stdout
27   - amngrlist \- list all accounts
28 26 amngrrename \- rename an account
29 27 amngrsearch \- pattern search accounts
30 28 .SH SYNOPSIS
... ... @@ -34,75 +32,95 @@ source
34 32 .B random
35 33
36 34 .B rand_printable
  35 +.RB [ -h ]
37 36 .RI [ len ]
38 37
39 38 .B amngrdbinit
  39 +.RB [ -h ]
40 40 .RI [ dbfile ]
41 41
42 42 .B amngrdbdestroy
  43 +.RB [ -h ]
43 44 .RI [ dbfile ]
44 45
45   -.B amngradd -u
46   -.I username
47   -.B -p
48   -.I password
  46 +.B amngradd
  47 +.RB [ -h ]
49 48 .RB [ -d
50 49 .IR description ]
51 50 .RB [ -D
52 51 .IR dbfile ]
53 52 .RB [ -r
54 53 .IR recipient ]
55   -.I account
56   -
57   -.B amngrcreate -u
58   -.I username
59   -.RB [ -d
60   -.IR description ]
61   -.RB [ -D
62   -.IR dbfile ]
63   -.RB [ -r
64   -.IR recipient ]
65   -.I account
  54 +.I
  55 + account
  56 +.RI [ username
  57 +.RI [ password ]]
66 58
67 59 .B amngrcrypt
  60 +.RB [ -h ]
68 61 .RB [ -r
69 62 .IR recipient ]
70 63 .I password
71 64
72 65 .B amngrdelete
  66 +.RB [ -h ]
  67 +.RB [ -D
  68 +.IR dbfile ]
73 69 .I account
74 70
75 71 .B amngrgen
  72 +.RB [ -h ]
76 73 .RB [ -r
77 74 .IR recipient ]
78 75 .RI [ len ]
79 76
80 77 .B amngrgetoldpass
  78 +.RB [ -h ]
  79 +.RB [ -D
  80 +.IR dbfile ]
81 81 .I account
82 82
83 83 .B amngrgetolduser
  84 +.RB [ -h ]
  85 +.RB [ -c ]
  86 +.RB [ -D
  87 +.IR dbfile ]
84 88 .I account
85 89
86 90 .B amngrgetpass
  91 +.RB [ -h ]
  92 +.RB [ -D
  93 +.IR dbfile ]
87 94 .I account
  95 +.RI [ state ]
88 96
89 97 .B amngrgetuser
  98 +.RB [ -h ]
  99 +.RB [ -c ]
  100 +.RB [ -D
  101 +.IR dbfile ]
90 102 .I account
  103 +.RI [ state ]
91 104
92 105 .B amngrid
  106 +.RB [ -h ]
  107 +.RB [ -D
  108 +.IR dbfile ]
93 109 .I account
94 110
95   -.B amngrlist
96   -.RB [ -s
97   -.IR separator ]
98   -
99 111 .B amngrrename
  112 +.RB [ -h ]
  113 +.RB [ -D
  114 +.IR dbfile ]
100 115 .I old_account new_account
101 116
102 117 .B amngrsearch
  118 +.RB [ -h ]
  119 +.RB [ -D
  120 +.IR dbfile ]
103 121 .RB [ -s
104 122 .IR separator ]
105   -.I pattern
  123 +.RI [ pattern ]
106 124 .SH DESCRIPTION
107 125 This file can either be source into the current shell or used as a
108 126 standalone shell script via the provided symlinks. When used as standalone
... ... @@ -116,7 +134,7 @@ pair.
116 134 \fBrandom\fR
117 135 Takes no arguments and connect a non blocking random source to stdout.
118 136 .TP
119   -\fBrand_printable\fR [\fIlen\fR]
  137 +\fBrand_printable\fR [\fB-h\fR] [\fIlen\fR]
120 138 Uses \fBrandom\fR to write a string of random printable characters to
121 139 stdout. All control characters ASCII-0 to ASCII-37 as well as
122 140 ASCII-177 to 255, single and double quotes are filtered. The single
... ... @@ -126,100 +144,114 @@ documents used to communicate with the SQLite database.
126 144 The optional \fIlen\fR argument specifies the string length to be written
127 145 and defaults to 512.
128 146 .TP
129   -\fBamngrdbinit\fR [\fIdbfile\fR]
  147 +\fBamngrdbinit\fR [\fB-h\fR] [\fIdbfile\fR]
130 148 Create the SQLite database file. If the optional \fIdbfile\fR argument is
131 149 given it specifies the fill path to the file to use, else the value of
132 150 \fB$AMNGRDB\fR environment variable is used.
133 151 .TP
134   -\fBamngrdbdestroy\fR [\fIdbfile\fR]
  152 +\fBamngrdbdestroy\fR [\fB-h\fR] [\fIdbfile\fR]
135 153 Deletes the SQLite database file. If the optional \fIdbfile\fR argument
136 154 is given it specifies the fill path to the file to use, else the value
137 155 of \fB$AMNGRDB\fR environment variable is used.
138 156 .TP
139   -\fBamngradd\fR \fB-u\fR \fIusername\fR \fB-p\fR \fIpassword\fR \
140   -[\fB-d\fR \fIdescription\fR] [\fB-D\fR \fIdbfile\fR] \
141   -[\fB-r\fR \fIrecipient\fR] \fIaccount\fR
  157 +\fBamngradd\fR [\fB-h\fR] [\fB-d\fR \fIdescription\fR] \
  158 +[\fB-D\fR \fIdbfile\fR] [\fB-r\fR \fIrecipient\fR]
  159 +.TQ
  160 + \fIaccount\fR [\fIusername\fR [\fIpassword\fR]]
142 161 Adds an account credential and marks it as active. If the account
143 162 already exist, the credential (\fIusername\fR and \fIpassword\fR) is added to
144 163 that account and the previously added credential is marked as old. If
145 164 there was another even older credential that was already marked as old
146   -this will be marked as inactive and this becomes inaccessible with
  165 +this will be marked as inactive and thus becomes inaccessible with
147 166 this tools (except for \fBamngrdelete\fR).
148   - The necessary option \fB-u\fR specifies the username to be stored with
149   -this credential pair.
150   - The also necessary option \fB-p\fR specifies the password to be stored
151   -and the length of \fIpassword\fR is not limited at all.
152   - With option \fB-d\fR one can add a \fIdescription\fR to the account entry.
153   -One can specify the \fIdbfile\fR with the option \fB-D\fR if that option is
  167 + The \fIusername\fR and the \fIaccount\fR name must not be longer than 128
  168 +characters. The \fIpassword\fR is not limited in its length at all. If
  169 +\fIpassword\fR is omitted one will be generated with \fBamngrgen\fR. The
  170 +\fIusername\fR may also be omitted if there was a previously added credentials
  171 +pair for this account. In that case the previous username is taken. It is
  172 +a failure to provide a \fIpassword\fR without a \fIusername\fR. There is no
  173 +way for the script to detect this condition and you will end up with a
  174 +credential where the username is the password and the password was generated.
  175 + Option \fB-d\fR adds a \fIdescription\fR to the account entry and
  176 +option \fB-D\fR specifys the \fIdbfile\fR to use. If that option is
154 177 not given the \fB$AMNGRDB\fR environment variable is used.
  178 + If a password was generated with this call it will be stored in the
  179 +X clipboard.
155 180 .TP
156   -\fBamngrcreate\fR \fB-u\fR \fIusername\fR [\fB-d\fR \fIdescription\fR] \
157   -[\fB-D\fR \fIdbfile\fR] [\fB-r\fR \fIrecipient\fR] \fIaccount\fR
158   - This will create a password with \fBamngrgen\fR and use that to add an
159   -credential via \fBamngradd\fR. (See there for options description)
160   - The added password will be copied to the X clipboard with
161   -\fBamngrgetpass\fR for further use.
162   -.TP
163   -\fBamngrcrypt\fR [\fB-r\fR \fIrecipient\fR] \fIpassword\fR
  181 +\fBamngrcrypt\fR [\fB-h\fR] [\fB-r\fR \fIrecipient\fR] \fIpassword\fR
164 182 Crypt the given plain text \fIpassword\fR with GnuPG and write it to
165 183 stdout.
166 184 The option \fB-r\fR specifies the recipient to use with the call to
167 185 \fBgpg\fR. If it is not given the value of the \fB$AMNGRID\fR environment
168 186 variable is used.
169 187 .TP
170   -\fBamngrdelete\fR \fIaccount\fR
  188 +\fBamngrdelete\fR [\fB-h\fR] [\fB-D\fR \fIdbfile\fR] \fIaccount\fR
171 189 Remove the \fIaccount\fR and all credential associated to it.
172 190 .TP
173   -\fBamngrgen\fR [\fB-r\fR \fIreceipient\fR] [\fIlen\fR]
  191 +\fBamngrgen\fR [\fB-h\fR] [\fB-r\fR \fIreceipient\fR] [\fIlen\fR]
174 192 Generate a password with \fBrand_printable\fR and encrypt it via
175 193 \fBamngrcrypt\fR. By default the password will be 10 characters long. That
176 194 can be modified by the optional \fIlen\fR argument.
177 195 .TP
178   -\fBamngrgetoldpass\fR \fIaccount\fR
  196 +\fBamngrgetoldpass\fR [\fB-h\fR] [\fB-D\fR \fIdbfile\fR] \fIaccount\fR
179 197 Read and decrypt the password associated with \fIaccount\fR that is
180 198 flagged as old and store it into the X clipboard.
181 199 .TP
182   -\fBamngrgetolduser\fR \fIaccount\fR
  200 +\fBamngrgetolduser\fR [\fB-h\fR] [\fB-c\fR] [\fB-D\fR \fIdbfile\fR] \
  201 +\fIaccount\fR
183 202 Read and decrypt the username associated with \fIaccount\fR that is
184 203 flagged as old and store it into the X clipboard.
185 204 .TP
186   -\fBamngrgetpass\fR \fIaccount\fR
  205 +\fBamngrgetpass\fR [\fB-h\fR] [\fB-D\fR \fIdbfile\fR] \fIaccount\fR \
  206 +[\fIstate\fR]
187 207 Read and decrypt the password associated with \fIaccount\fR that is
188   -flagged as active and store it into the X clipboard.
  208 +flagged as active and store it into the X clipboard. The \fIstate\fR
  209 +argument might be either 1 or 2 where 1 means get the current credential
  210 +and 2 means get the old credential. It defaults to 1.
189 211 .TP
190   -\fBamngrgetuser\fR \fIaccount\fR
  212 +\fBamngrgetuser\fR [\fB-h\fR] [\fB-c\fR] [\fB-D\fR \fIdbfile\fR] \
  213 +\fIaccount\fR \
  214 +[\fIstate\fR]
191 215 Read and decrypt the username associated with \fIaccount\fR that is
192   -flagged as active and store it into the X clipboard.
  216 +flagged as active and store it into the X clipboard. The \fIstate\fR
  217 +argument might be either 1 or 2 where 1 means get the current credential
  218 +and 2 means get the old credential. It defaults to 1.
193 219 .TP
194   -\fBamngrid\fR \fIaccount\fR
  220 +\fBamngrid\fR [\fB-h\fR] [\fB-D\fR \fIdbfile\fR] \fIaccount\fR
195 221 (This is primarily for internal use.) Get the database id associated
196 222 to the given \fIaccount\fR argument.
197 223 .TP
198   -\fBamngrlist\fR [\fB-s\fR \fIseparator\fR]
199   - List all accounts currently stored within the database the output
200   -will contain the name of the account, the username and the description
201   -delimited by a \fIseparator\fR string that can be specified with the
202   -\fB-s\fR option. If the option is omitted the separator is " => ". Each
203   -row is one account.
204   -.TP
205   -\fBamngrrename\fR \fIold_account\fR \fInew_account\fR
  224 +\fBamngrrename\fR [\fB-h\fR] [\fB-D\fR \fIdbfile\fR] \fIold_account\fR \
  225 +\fInew_account\fR
206 226 Rename the account specified by \fIold_account\fR to \fInew_account\fR.
207 227 .TP
208   -\fBamngrsearch\fR [\fB-s\fR \fIseparator\fR] \fIpattern\fR
209   - List accounts where \fIpattern\fR exists in either the account name, the
210   -username or the account description. The output is like the one
211   -described with \fBamngrlist\fR.
  228 +\fBamngrsearch\fR [\fB-h\fR] [\fB-D\fR \fIdbfile\fR] \
  229 +[\fB-s\fR \fIseparator\fR] [\fIpattern\fR]
  230 + List all accounts where \fIpattern\fR exists in either the account name, the
  231 +username or the account description. The output will contain the name of the
  232 +account, the username and the description delimited by a \fIseparator\fR
  233 +string that can be specified with the \fB-s\fR option. If the option is
  234 +omitted the separator is " => ". Each row is one account.
  235 + If \fIpattern\fR is omitted all accounts will be listed.
212 236 .SH OPTIONS
213 237 The options are consistent over all sub commands. However not all sub commands
214 238 use all options and some sub commands take arguments that other get per option.
215 239 (See \fBDESCRIPTION\fR)
216 240 .TP
  241 +.B -h
  242 +Write a short usage information.
  243 +.TP
217 244 .B -u
218 245 The login username of the credential for the account.
219 246 .TP
220 247 .B -p
221 248 The plain text password of the credential for the account.
222 249 .TP
  250 +.B -c
  251 +By default this scripts stores the username into the X clipboard whith
  252 +\fBambgrgetuser\fR and \fBamngrgetolduser\fR. When \fB-c\fR is given
  253 +the username is written to stdout. The password is never written to stdout.
  254 +.TP
223 255 .B -d
224 256 The description for the account.
225 257 .TP
... ... @@ -232,7 +264,7 @@ Select a recipient id for GnuPG encryption to use instead of the one
232 264 defined in the \fB$AMNGRID\fR environment variable.
233 265 .TP
234 266 .B -s
235   -The column separator for the \fBamngrlist\fR and \fBamngrsearch\R commands.
  267 +The column separator for the \fBamngrlist\fR and \fBamngrsearch\fR commands.
236 268 .SH ENVIRONMENT
237 269 .TP
238 270 .B AMNGRDB
... ... @@ -246,7 +278,8 @@ both public and private key. Failure to do so will result in not decipherable
246 278 data. This can be overruled with the \fB-r\fR command line option.
247 279 .TP
248 280 .B AMNGRPWLEN
249   - The password length to be used when generating new passwords.
  281 + The password length to be used when generating new passwords. If not
  282 +specified this defaults to 10.
250 283 .SH DEPENDENCIES
251 284 A set of POSIX compliant shell utilities including a POSIX compliant shell
252 285 as well are needed to run this script.
... ... @@ -274,7 +307,7 @@ comes from the use of foreign key constraints.
274 307 .IP \[bu]
275 308 \fBcat\fR
276 309 .IP \[bu]
277   -\fBdnsdomainname\fR
  310 +\fBdirname\fR
278 311 .IP \[bu]
279 312 \fBecho\fR
280 313 .IP \[bu]
... ...
No preview for this file type
Please register or login to post a comment