Line | Count | Source (jump to first uncovered line) |
1 | | /* Read and parse the .netrc file to get hosts, accounts, and passwords. |
2 | | Copyright (C) 1996, 2007-2011, 2015, 2018-2023 Free Software |
3 | | Foundation, Inc. |
4 | | |
5 | | This file is part of GNU Wget. |
6 | | |
7 | | GNU Wget is free software; you can redistribute it and/or modify |
8 | | it under the terms of the GNU General Public License as published by |
9 | | the Free Software Foundation; either version 3 of the License, or |
10 | | (at your option) any later version. |
11 | | |
12 | | GNU Wget is distributed in the hope that it will be useful, |
13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | GNU General Public License for more details. |
16 | | |
17 | | You should have received a copy of the GNU General Public License |
18 | | along with Wget. If not, see <http://www.gnu.org/licenses/>. |
19 | | |
20 | | Additional permission under GNU GPL version 3 section 7 |
21 | | |
22 | | If you modify this program, or any covered work, by linking or |
23 | | combining it with the OpenSSL project's OpenSSL library (or a |
24 | | modified version of that library), containing parts covered by the |
25 | | terms of the OpenSSL or SSLeay licenses, the Free Software Foundation |
26 | | grants you additional permission to convey the resulting work. |
27 | | Corresponding Source for a non-source form of such a combination |
28 | | shall include the source code for the parts of OpenSSL used as well |
29 | | as that of the covered work. */ |
30 | | |
31 | | /* This file used to be kept in synch with the code in Fetchmail, but |
32 | | the latter has diverged since. */ |
33 | | |
34 | | #include "wget.h" |
35 | | |
36 | | #include <stdio.h> |
37 | | #include <stdlib.h> |
38 | | #include <string.h> |
39 | | #include <errno.h> |
40 | | |
41 | | #include "utils.h" |
42 | | #include "netrc.h" |
43 | | #include "init.h" |
44 | | |
45 | | #ifdef WINDOWS |
46 | | # define NETRC_FILE_NAME "_netrc" |
47 | | #else |
48 | 0 | # define NETRC_FILE_NAME ".netrc" |
49 | | #endif |
50 | | |
51 | | typedef struct _acc_t |
52 | | { |
53 | | char *host; /* NULL if this is the default machine |
54 | | entry. */ |
55 | | char *acc; |
56 | | char *passwd; /* NULL if there is no password. */ |
57 | | struct _acc_t *next; |
58 | | } acc_t; |
59 | | |
60 | | static acc_t *parse_netrc (const char *); |
61 | | static acc_t *parse_netrc_fp (const char *, FILE *); |
62 | | |
63 | | static acc_t *netrc_list; |
64 | | static int processed_netrc; |
65 | | |
66 | | #if defined DEBUG_MALLOC || defined TESTING |
67 | | static void free_netrc(acc_t *); |
68 | | |
69 | | void |
70 | | netrc_cleanup (void) |
71 | 1.66k | { |
72 | 1.66k | free_netrc (netrc_list); |
73 | 1.66k | processed_netrc = 0; |
74 | 1.66k | } |
75 | | #endif |
76 | | |
77 | | /* Return the correct user and password, given the host, user (as |
78 | | given in the URL), and password (as given in the URL). May return |
79 | | NULL. |
80 | | |
81 | | If SLACK_DEFAULT is set, allow looking for a "default" account. |
82 | | You will typically turn it off for HTTP. */ |
83 | | void |
84 | | search_netrc (const char *host, const char **acc, const char **passwd, |
85 | | int slack_default, FILE *fp_netrc) |
86 | 1.66k | { |
87 | 1.66k | acc_t *l; |
88 | | |
89 | 1.66k | if (!opt.netrc) |
90 | 0 | return; |
91 | | /* Find ~/.netrc. */ |
92 | 1.66k | if (!processed_netrc) |
93 | 1.66k | { |
94 | | #ifdef __VMS |
95 | | |
96 | | int err; |
97 | | struct stat buf; |
98 | | char *path = "SYS$LOGIN:.netrc"; |
99 | | |
100 | | netrc_list = NULL; |
101 | | processed_netrc = 1; |
102 | | |
103 | | err = stat (path, &buf); |
104 | | if (err == 0) |
105 | | netrc_list = parse_netrc (path); |
106 | | |
107 | | #else /* def __VMS */ |
108 | | |
109 | 1.66k | netrc_list = NULL; |
110 | 1.66k | processed_netrc = 1; |
111 | | |
112 | 1.66k | if (fp_netrc) |
113 | 1.66k | netrc_list = parse_netrc_fp (".netrc", fp_netrc); |
114 | 0 | else if (opt.homedir) |
115 | 0 | { |
116 | 0 | struct stat buf; |
117 | 0 | char *path = aprintf ("%s/%s", opt.homedir, NETRC_FILE_NAME); |
118 | 0 | if (stat (path, &buf) == 0) |
119 | 0 | netrc_list = parse_netrc (path); |
120 | 0 | xfree (path); |
121 | 0 | } |
122 | | |
123 | 1.66k | #endif /* def __VMS [else] */ |
124 | 1.66k | } |
125 | | /* If nothing to do... */ |
126 | 1.66k | if (!netrc_list) |
127 | 1.57k | return; |
128 | | /* Acc and password found; all OK. */ |
129 | 89 | if (*acc && *passwd) |
130 | 0 | return; |
131 | | /* Some data not given -- try finding the host. */ |
132 | 2.33k | for (l = netrc_list; l; l = l->next) |
133 | 2.24k | { |
134 | 2.24k | if (!l->host) |
135 | 1.75k | continue; |
136 | 495 | else if (!strcasecmp (l->host, host)) |
137 | 2 | break; |
138 | 2.24k | } |
139 | 89 | if (l) |
140 | 2 | { |
141 | 2 | if (*acc) |
142 | 0 | { |
143 | | /* Looking for password in .netrc. */ |
144 | 0 | if (!strcmp (l->acc, *acc)) |
145 | 0 | *passwd = l->passwd; /* usernames match; password OK */ |
146 | 0 | else |
147 | 0 | *passwd = NULL; /* usernames don't match */ |
148 | 0 | } |
149 | 2 | else /* NOT *acc */ |
150 | 2 | { |
151 | | /* If password was given, use it. The account is l->acc. */ |
152 | 2 | *acc = l->acc; |
153 | 2 | if (l->passwd) |
154 | 1 | *passwd = l->passwd; |
155 | 2 | } |
156 | 2 | return; |
157 | 2 | } |
158 | 87 | else |
159 | 87 | { |
160 | 87 | if (!slack_default) |
161 | 0 | return; |
162 | 87 | if (*acc) |
163 | 0 | return; |
164 | | /* Try looking for the default account. */ |
165 | 543 | for (l = netrc_list; l; l = l->next) |
166 | 508 | if (!l->host) |
167 | 52 | break; |
168 | 87 | if (!l) |
169 | 35 | return; |
170 | 52 | *acc = l->acc; |
171 | 52 | if (!*passwd) |
172 | 52 | *passwd = l->passwd; |
173 | 52 | return; |
174 | 87 | } |
175 | 89 | } |
176 | | |
177 | | |
178 | | #ifdef STANDALONE |
179 | | |
180 | | /* Normally, these functions would be defined by your package. */ |
181 | | # define xmalloc malloc |
182 | | # define xfree(p) do { free ((void *) (p)); p = NULL; } while (0) |
183 | | # define xstrdup strdup |
184 | | |
185 | | # define xrealloc realloc |
186 | | |
187 | | #endif /* STANDALONE */ |
188 | | |
189 | | /* Maybe add NEWENTRY to the account information list, LIST. NEWENTRY is |
190 | | set to a ready-to-use acc_t, in any event. */ |
191 | | static void |
192 | | maybe_add_to_list (acc_t **newentry, acc_t **list) |
193 | 5.14k | { |
194 | 5.14k | acc_t *a, *l; |
195 | 5.14k | a = *newentry; |
196 | 5.14k | l = *list; |
197 | | |
198 | | /* We need an account name in order to add the entry to the list. */ |
199 | 5.14k | if (a && ! a->acc) |
200 | 1.23k | { |
201 | | /* Free any allocated space. */ |
202 | 1.23k | xfree (a->host); |
203 | 1.23k | xfree (a->acc); |
204 | 1.23k | xfree (a->passwd); |
205 | 1.23k | } |
206 | 3.91k | else |
207 | 3.91k | { |
208 | 3.91k | if (a) |
209 | 2.24k | { |
210 | | /* Add the current machine into our list. */ |
211 | 2.24k | a->next = l; |
212 | 2.24k | l = a; |
213 | 2.24k | } |
214 | | |
215 | | /* Allocate a new acc_t structure. */ |
216 | 3.91k | a = xmalloc (sizeof (acc_t)); |
217 | 3.91k | } |
218 | | |
219 | | /* Zero the structure, so that it is ready to use. */ |
220 | 5.14k | memset (a, 0, sizeof(*a)); |
221 | | |
222 | | /* Return the new pointers. */ |
223 | 5.14k | *newentry = a; |
224 | 5.14k | *list = l; |
225 | 5.14k | return; |
226 | 5.14k | } |
227 | | |
228 | | /* Helper function for the parser, shifts contents of |
229 | | null-terminated string once character to the left. |
230 | | Used in processing \ and " constructs in the netrc file */ |
231 | | static void |
232 | | shift_left(char *string) |
233 | 1.28k | { |
234 | 1.28k | char *p; |
235 | | |
236 | 62.6k | for (p=string; *p; ++p) |
237 | 61.3k | *p = *(p+1); |
238 | 1.28k | } |
239 | | |
240 | | /* Parse a .netrc file (as described in the ftp(1) manual page). */ |
241 | | static acc_t * |
242 | | parse_netrc_fp (const char *path, FILE *fp) |
243 | 1.66k | { |
244 | 1.66k | char *line = NULL, *p, *tok; |
245 | 1.66k | const char *premature_token = NULL; |
246 | 1.66k | acc_t *current = NULL, *retval = NULL; |
247 | 1.66k | int ln = 0, qmark; |
248 | 1.66k | size_t bufsize = 0; |
249 | | |
250 | | /* The latest token we've seen in the file. */ |
251 | 1.66k | enum |
252 | 1.66k | { |
253 | 1.66k | tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password, tok_port, tok_force |
254 | 1.66k | } last_token = tok_nothing; |
255 | | |
256 | | /* While there are lines in the file... */ |
257 | 15.9k | while (getline (&line, &bufsize, fp) > 0) |
258 | 14.3k | { |
259 | 14.3k | ln ++; |
260 | | |
261 | | /* Parse the line. */ |
262 | 14.3k | p = line; |
263 | 14.3k | qmark = 0; |
264 | | |
265 | | /* Skip leading whitespace. */ |
266 | 15.5k | while (*p && c_isspace (*p)) |
267 | 1.24k | p ++; |
268 | | |
269 | | /* If the line is empty, then end any macro definition. */ |
270 | 14.3k | if (last_token == tok_macdef && !*p) |
271 | | /* End of macro if the line is empty. */ |
272 | 383 | last_token = tok_nothing; |
273 | | |
274 | | /* If we are defining macros, then skip parsing the line. */ |
275 | 35.1k | while (*p && last_token != tok_macdef) |
276 | 21.3k | { |
277 | | /* Skip any whitespace. */ |
278 | 23.1k | while (*p && c_isspace (*p)) |
279 | 1.79k | p ++; |
280 | | |
281 | | /* Discard end-of-line comments; also, stop processing if |
282 | | the above `while' merely skipped trailing whitespace. */ |
283 | 21.3k | if (*p == '#' || !*p) |
284 | 491 | break; |
285 | | |
286 | | /* If the token starts with quotation mark, note this fact, |
287 | | and squash the quotation character */ |
288 | 20.8k | if (*p == '"'){ |
289 | 500 | qmark = 1; |
290 | 500 | shift_left (p); |
291 | 500 | } |
292 | | |
293 | 20.8k | tok = p; |
294 | | |
295 | | /* Find the end of the token, handling quotes and escapes. */ |
296 | 100k | while (*p && (qmark ? *p != '"' : !c_isspace (*p))){ |
297 | 79.5k | if (*p == '\\') |
298 | 288 | shift_left (p); |
299 | 79.5k | p ++; |
300 | 79.5k | } |
301 | | |
302 | | /* If field was quoted, squash the trailing quotation mark |
303 | | and reset qmark flag. */ |
304 | 20.8k | if (qmark) |
305 | 500 | { |
306 | 500 | shift_left (p); |
307 | 500 | qmark = 0; |
308 | 500 | } |
309 | | |
310 | | /* Null-terminate the token, if it isn't already. */ |
311 | 20.8k | if (*p) |
312 | 19.6k | *p ++ = '\0'; |
313 | | |
314 | 20.8k | switch (last_token) |
315 | 20.8k | { |
316 | 3.22k | case tok_login: |
317 | 3.22k | if (current) |
318 | 2.63k | { |
319 | 2.63k | xfree (current->acc); |
320 | 2.63k | current->acc = xstrdup (tok); |
321 | 2.63k | } |
322 | 583 | else |
323 | 583 | premature_token = "login"; |
324 | 3.22k | break; |
325 | | |
326 | 1.15k | case tok_machine: |
327 | | /* Start a new machine entry. */ |
328 | 1.15k | maybe_add_to_list (¤t, &retval); |
329 | 1.15k | current->host = xstrdup (tok); |
330 | 1.15k | break; |
331 | | |
332 | 1.10k | case tok_password: |
333 | 1.10k | if (current) |
334 | 442 | { |
335 | 442 | xfree (current->passwd); |
336 | 442 | current->passwd = xstrdup (tok); |
337 | 442 | } |
338 | 664 | else |
339 | 664 | premature_token = "password"; |
340 | 1.10k | break; |
341 | | |
342 | | /* We handle most of tok_macdef above. */ |
343 | 0 | case tok_macdef: |
344 | 0 | if (!current) |
345 | 0 | premature_token = "macdef"; |
346 | 0 | break; |
347 | | |
348 | | /* We don't handle the account keyword at all. */ |
349 | 687 | case tok_account: |
350 | 687 | if (!current) |
351 | 453 | premature_token = "account"; |
352 | 687 | break; |
353 | | |
354 | | /* We don't handle the port keyword at all. */ |
355 | 699 | case tok_port: |
356 | 699 | if (!current) |
357 | 448 | premature_token = "port"; |
358 | 699 | break; |
359 | | |
360 | | /* We don't handle the force keyword at all. */ |
361 | 636 | case tok_force: |
362 | 636 | if (!current) |
363 | 381 | premature_token = "force"; |
364 | 636 | break; |
365 | | |
366 | | /* We handle tok_nothing below this switch. */ |
367 | 13.3k | case tok_nothing: |
368 | 13.3k | break; |
369 | 20.8k | } |
370 | | |
371 | 20.8k | if (premature_token) |
372 | 2.52k | { |
373 | 2.52k | fprintf (stderr, _("\ |
374 | 2.52k | %s: %s:%d: warning: %s token appears before any machine name\n"), |
375 | 2.52k | exec_name, path, ln, quote (premature_token)); |
376 | 2.52k | premature_token = NULL; |
377 | 2.52k | } |
378 | | |
379 | 20.8k | if (last_token != tok_nothing) |
380 | | /* We got a value, so reset the token state. */ |
381 | 7.50k | last_token = tok_nothing; |
382 | 13.3k | else |
383 | 13.3k | { |
384 | | /* Fetch the next token. */ |
385 | 13.3k | if (!strcmp (tok, "account")) |
386 | 695 | last_token = tok_account; |
387 | | |
388 | 12.6k | else if (!strcmp (tok, "default")) |
389 | 2.32k | maybe_add_to_list (¤t, &retval); |
390 | | |
391 | | /* fetchmail compatibility, "user" is an alias for "login" */ |
392 | 10.3k | else if (!strcmp (tok, "login") || !strcmp (tok, "user")) |
393 | 3.23k | last_token = tok_login; |
394 | | |
395 | 7.08k | else if (!strcmp (tok, "macdef")) |
396 | 399 | last_token = tok_macdef; |
397 | | |
398 | 6.68k | else if (!strcmp (tok, "machine")) |
399 | 1.16k | last_token = tok_machine; |
400 | | |
401 | | /* fetchmail compatibility, "passwd" is an alias for "password" */ |
402 | 5.52k | else if (!strcmp (tok, "password") || !strcmp (tok, "passwd")) |
403 | 1.12k | last_token = tok_password; |
404 | | |
405 | | /* GNU extensions 'port' and 'force', not operational |
406 | | * see https://www.gnu.org/software/emacs/manual/html_node/gnus/NNTP.html#index-nntp_002dauthinfo_002dfunction-2003 |
407 | | * see https://savannah.gnu.org/bugs/index.php?52066 |
408 | | */ |
409 | 4.40k | else if (!strcmp (tok, "port")) |
410 | 707 | last_token = tok_port; |
411 | | |
412 | 3.69k | else if (!strcmp (tok, "force")) |
413 | 644 | last_token = tok_force; |
414 | | |
415 | 3.05k | else |
416 | 3.05k | fprintf (stderr, _("%s: %s:%d: unknown token \"%s\"\n"), |
417 | 3.05k | exec_name, path, ln, tok); |
418 | 13.3k | } |
419 | 20.8k | } |
420 | 14.3k | } |
421 | | |
422 | 1.66k | xfree (line); |
423 | | |
424 | | /* Finalize the last machine entry we found. */ |
425 | 1.66k | maybe_add_to_list (¤t, &retval); |
426 | 1.66k | xfree (current); |
427 | | |
428 | | /* Reverse the order of the list so that it appears in file order. */ |
429 | 1.66k | current = retval; |
430 | 1.66k | retval = NULL; |
431 | 3.91k | while (current) |
432 | 2.24k | { |
433 | 2.24k | acc_t *saved_reference; |
434 | | |
435 | | /* Change the direction of the pointers. */ |
436 | 2.24k | saved_reference = current->next; |
437 | 2.24k | current->next = retval; |
438 | | |
439 | | /* Advance to the next node. */ |
440 | 2.24k | retval = current; |
441 | 2.24k | current = saved_reference; |
442 | 2.24k | } |
443 | | |
444 | 1.66k | return retval; |
445 | 1.66k | } |
446 | | |
447 | | static acc_t * |
448 | | parse_netrc (const char *path) |
449 | 0 | { |
450 | 0 | FILE *fp; |
451 | 0 | acc_t *acc; |
452 | |
|
453 | 0 | fp = fopen (path, "r"); |
454 | 0 | if (!fp) |
455 | 0 | { |
456 | 0 | fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name, |
457 | 0 | path, strerror (errno)); |
458 | 0 | return NULL; |
459 | 0 | } |
460 | | |
461 | 0 | acc = parse_netrc_fp (path, fp); |
462 | 0 | fclose(fp); |
463 | |
|
464 | 0 | return acc; |
465 | 0 | } |
466 | | |
467 | | #if defined DEBUG_MALLOC || defined TESTING |
468 | | /* Free a netrc list. */ |
469 | | static void |
470 | | free_netrc(acc_t *l) |
471 | 1.66k | { |
472 | 1.66k | acc_t *t; |
473 | | |
474 | 3.91k | while (l) |
475 | 2.24k | { |
476 | 2.24k | t = l->next; |
477 | 2.24k | xfree (l->acc); |
478 | 2.24k | xfree (l->passwd); |
479 | 2.24k | xfree (l->host); |
480 | 2.24k | xfree (l); |
481 | 2.24k | l = t; |
482 | 2.24k | } |
483 | 1.66k | } |
484 | | #endif |
485 | | |
486 | | #ifdef TESTING |
487 | | #include "../tests/unit-tests.h" |
488 | | const char * |
489 | | test_parse_netrc(void) |
490 | 0 | { |
491 | 0 | #ifdef HAVE_FMEMOPEN |
492 | 0 | static const struct test { |
493 | 0 | const char *pw_in; |
494 | 0 | const char *pw_expected; |
495 | 0 | } tests[] = { |
496 | 0 | { "a\\b", "ab" }, |
497 | 0 | { "a\\\\b", "a\\b" }, |
498 | 0 | { "\"a\\\\b\"", "a\\b" }, |
499 | 0 | { "\"a\\\"b\"", "a\"b" }, |
500 | 0 | { "a\"b", "a\"b" }, |
501 | 0 | { "a\\\\\\\\b", "a\\\\b" }, |
502 | 0 | { "a\\\\", "a\\" }, |
503 | 0 | { "\"a\\\\\"", "a\\" }, |
504 | 0 | { "a\\", "a" }, |
505 | 0 | { "\"a b\"", "a b" }, |
506 | 0 | { "a b", "a" }, |
507 | 0 | }; |
508 | 0 | unsigned i; |
509 | 0 | static char errmsg[128]; |
510 | |
|
511 | 0 | for (i = 0; i < countof(tests); ++i) |
512 | 0 | { |
513 | 0 | const struct test *t = &tests[i]; |
514 | 0 | char netrc[128]; |
515 | 0 | FILE *fp; |
516 | 0 | acc_t *acc; |
517 | 0 | int n; |
518 | |
|
519 | 0 | n = snprintf (netrc, sizeof(netrc), "machine localhost\n\tlogin me\n\tpassword %s", t->pw_in); |
520 | 0 | mu_assert ("test_parse_netrc: failed to fmemopen() netrc", (fp = fmemopen(netrc, n, "r")) != NULL); |
521 | | |
522 | 0 | acc = parse_netrc_fp ("memory", fp); |
523 | 0 | fclose(fp); |
524 | |
|
525 | 0 | if (strcmp(acc->passwd, t->pw_expected)) |
526 | 0 | { |
527 | 0 | snprintf(errmsg, sizeof(errmsg), "test_parse_netrc: wrong result [%u]. Expected '%s', got '%s'", |
528 | 0 | i, t->pw_expected, acc->passwd); |
529 | 0 | free_netrc(acc); |
530 | 0 | return errmsg; |
531 | 0 | } |
532 | | |
533 | 0 | free_netrc(acc); |
534 | 0 | } |
535 | | |
536 | 0 | #endif // HAVE_FMEMOPEN |
537 | 0 | return NULL; |
538 | 0 | } |
539 | | #endif |
540 | | |
541 | | #ifdef STANDALONE |
542 | | #include <sys/types.h> |
543 | | #include <sys/stat.h> |
544 | | #include "exits.h" |
545 | | |
546 | | const char *program_argstring = NULL; /* Needed by warc.c */ |
547 | | |
548 | | int |
549 | | main (int argc, char **argv) |
550 | | { |
551 | | struct stat sb; |
552 | | char *program_name, *file, *target; |
553 | | acc_t *head, *a; |
554 | | |
555 | | if (argc < 2 || argc > 3) |
556 | | { |
557 | | fprintf (stderr, _("Usage: %s NETRC [HOSTNAME]\n"), argv[0]); |
558 | | exit (WGET_EXIT_GENERIC_ERROR); |
559 | | } |
560 | | |
561 | | program_name = argv[0]; |
562 | | file = argv[1]; |
563 | | target = argv[2]; |
564 | | |
565 | | #ifdef ENABLE_NLS |
566 | | /* Set the current locale. */ |
567 | | setlocale (LC_ALL, ""); |
568 | | /* Set the text message domain. */ |
569 | | bindtextdomain ("wget", LOCALEDIR); |
570 | | textdomain ("wget"); |
571 | | #endif /* ENABLE_NLS */ |
572 | | |
573 | | if (stat (file, &sb)) |
574 | | { |
575 | | fprintf (stderr, _("%s: cannot stat %s: %s\n"), argv[0], file, |
576 | | strerror (errno)); |
577 | | exit (WGET_EXIT_GENERIC_ERROR); |
578 | | } |
579 | | |
580 | | head = parse_netrc (file); |
581 | | a = head; |
582 | | while (a) |
583 | | { |
584 | | /* Skip if we have a target and this isn't it. */ |
585 | | if (target && a->host && strcmp (target, a->host)) |
586 | | { |
587 | | a = a->next; |
588 | | continue; |
589 | | } |
590 | | |
591 | | if (!target) |
592 | | { |
593 | | /* Print the host name if we have no target. */ |
594 | | if (a->host) |
595 | | fputs (a->host, stdout); |
596 | | else |
597 | | fputs ("DEFAULT", stdout); |
598 | | |
599 | | fputc (' ', stdout); |
600 | | } |
601 | | |
602 | | /* Print the account name. */ |
603 | | fputs (a->acc, stdout); |
604 | | |
605 | | if (a->passwd) |
606 | | { |
607 | | /* Print the password, if there is any. */ |
608 | | fputc (' ', stdout); |
609 | | fputs (a->passwd, stdout); |
610 | | } |
611 | | |
612 | | fputc ('\n', stdout); |
613 | | |
614 | | /* Exit if we found the target. */ |
615 | | if (target) |
616 | | exit (WGET_EXIT_SUCCESS); |
617 | | a = a->next; |
618 | | } |
619 | | |
620 | | /* Exit with failure if we had a target, success otherwise. */ |
621 | | if (target) |
622 | | exit (WGET_EXIT_GENERIC_ERROR); |
623 | | |
624 | | exit (WGET_EXIT_SUCCESS); |
625 | | } |
626 | | #endif /* STANDALONE */ |