xref: /openbsd-src/usr.bin/openssl/openssl.c (revision 6f05df2d9be0954bec42d51d943d77bd250fb664)
1 /* $OpenBSD: openssl.c,v 1.2 2014/10/22 13:54:03 jsing Exp $ */
2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3  * All rights reserved.
4  *
5  * This package is an SSL implementation written
6  * by Eric Young (eay@cryptsoft.com).
7  * The implementation was written so as to conform with Netscapes SSL.
8  *
9  * This library is free for commercial and non-commercial use as long as
10  * the following conditions are aheared to.  The following conditions
11  * apply to all code found in this distribution, be it the RC4, RSA,
12  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13  * included with this distribution is covered by the same copyright terms
14  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15  *
16  * Copyright remains Eric Young's, and as such any Copyright notices in
17  * the code are not to be removed.
18  * If this package is used in a product, Eric Young should be given attribution
19  * as the author of the parts of the library used.
20  * This can be in the form of a textual message at program startup or
21  * in documentation (online or textual) provided with the package.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. All advertising materials mentioning features or use of this software
32  *    must display the following acknowledgement:
33  *    "This product includes cryptographic software written by
34  *     Eric Young (eay@cryptsoft.com)"
35  *    The word 'cryptographic' can be left out if the rouines from the library
36  *    being used are not cryptographic related :-).
37  * 4. If you include any Windows specific code (or a derivative thereof) from
38  *    the apps directory (application code) you must include an acknowledgement:
39  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40  *
41  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  *
53  * The licence and distribution terms for any publically available version or
54  * derivative of this code cannot be changed.  i.e. this code cannot simply be
55  * copied and put under another distribution licence
56  * [including the GNU Public Licence.]
57  */
58 /* ====================================================================
59  * Copyright (c) 1998-2006 The OpenSSL Project.  All rights reserved.
60  *
61  * Redistribution and use in source and binary forms, with or without
62  * modification, are permitted provided that the following conditions
63  * are met:
64  *
65  * 1. Redistributions of source code must retain the above copyright
66  *    notice, this list of conditions and the following disclaimer.
67  *
68  * 2. Redistributions in binary form must reproduce the above copyright
69  *    notice, this list of conditions and the following disclaimer in
70  *    the documentation and/or other materials provided with the
71  *    distribution.
72  *
73  * 3. All advertising materials mentioning features or use of this
74  *    software must display the following acknowledgment:
75  *    "This product includes software developed by the OpenSSL Project
76  *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
77  *
78  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
79  *    endorse or promote products derived from this software without
80  *    prior written permission. For written permission, please contact
81  *    openssl-core@openssl.org.
82  *
83  * 5. Products derived from this software may not be called "OpenSSL"
84  *    nor may "OpenSSL" appear in their names without prior written
85  *    permission of the OpenSSL Project.
86  *
87  * 6. Redistributions of any form whatsoever must retain the following
88  *    acknowledgment:
89  *    "This product includes software developed by the OpenSSL Project
90  *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
91  *
92  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
93  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
94  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
95  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
96  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
97  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
98  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
99  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
100  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
101  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
102  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
103  * OF THE POSSIBILITY OF SUCH DAMAGE.
104  * ====================================================================
105  *
106  * This product includes cryptographic software written by Eric Young
107  * (eay@cryptsoft.com).  This product includes software written by Tim
108  * Hudson (tjh@cryptsoft.com).
109  *
110  */
111 
112 #include <err.h>
113 #include <signal.h>
114 #include <stdio.h>
115 #include <string.h>
116 #include <stdlib.h>
117 
118 #include "apps.h"
119 
120 #include <openssl/bio.h>
121 #include <openssl/conf.h>
122 #include <openssl/crypto.h>
123 #include <openssl/err.h>
124 #include <openssl/lhash.h>
125 #include <openssl/pem.h>
126 #include <openssl/ssl.h>
127 #include <openssl/x509.h>
128 
129 #ifndef OPENSSL_NO_ENGINE
130 #include <openssl/engine.h>
131 #endif
132 
133 #include "progs.h"
134 #include "s_apps.h"
135 
136 static void openssl_startup(void);
137 static void openssl_shutdown(void);
138 
139 /* The LHASH callbacks ("hash" & "cmp") have been replaced by functions with the
140  * base prototypes (we cast each variable inside the function to the required
141  * type of "FUNCTION*"). This removes the necessity for macro-generated wrapper
142  * functions. */
143 
144 static LHASH_OF(FUNCTION) *prog_init(void);
145 static int do_cmd(LHASH_OF(FUNCTION) *prog, int argc, char *argv[]);
146 static void list_pkey(BIO * out);
147 static void list_cipher(BIO * out);
148 static void list_md(BIO * out);
149 char *default_config_file = NULL;
150 
151 CONF *config = NULL;
152 BIO *bio_err = NULL;
153 
154 static void
155 lock_dbg_cb(int mode, int type, const char *file, int line)
156 {
157 	static int modes[CRYPTO_NUM_LOCKS];	/* = {0, 0, ... } */
158 	const char *errstr = NULL;
159 	int rw;
160 
161 	rw = mode & (CRYPTO_READ | CRYPTO_WRITE);
162 	if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE))) {
163 		errstr = "invalid mode";
164 		goto err;
165 	}
166 	if (type < 0 || type >= CRYPTO_NUM_LOCKS) {
167 		errstr = "type out of bounds";
168 		goto err;
169 	}
170 	if (mode & CRYPTO_LOCK) {
171 		if (modes[type]) {
172 			errstr = "already locked";
173 			/*
174 			 * must not happen in a single-threaded program
175 			 * (would deadlock)
176 			 */
177 			goto err;
178 		}
179 		modes[type] = rw;
180 	} else if (mode & CRYPTO_UNLOCK) {
181 		if (!modes[type]) {
182 			errstr = "not locked";
183 			goto err;
184 		}
185 		if (modes[type] != rw) {
186 			errstr = (rw == CRYPTO_READ) ?
187 			    "CRYPTO_r_unlock on write lock" :
188 			    "CRYPTO_w_unlock on read lock";
189 		}
190 		modes[type] = 0;
191 	} else {
192 		errstr = "invalid mode";
193 		goto err;
194 	}
195 
196 err:
197 	if (errstr) {
198 		/* we cannot use bio_err here */
199 		fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
200 		    errstr, mode, type, file, line);
201 	}
202 }
203 
204 static void
205 openssl_startup(void)
206 {
207 	signal(SIGPIPE, SIG_IGN);
208 
209 	CRYPTO_malloc_init();
210 	OpenSSL_add_all_algorithms();
211 	SSL_library_init();
212 	SSL_load_error_strings();
213 
214 #ifndef OPENSSL_NO_ENGINE
215 	ENGINE_load_builtin_engines();
216 #endif
217 
218 	setup_ui_method();
219 }
220 
221 static void
222 openssl_shutdown(void)
223 {
224 	CONF_modules_unload(1);
225 	destroy_ui_method();
226 	OBJ_cleanup();
227 	EVP_cleanup();
228 
229 #ifndef OPENSSL_NO_ENGINE
230 	ENGINE_cleanup();
231 #endif
232 
233 	CRYPTO_cleanup_all_ex_data();
234 	ERR_remove_thread_state(NULL);
235 	ERR_free_strings();
236 }
237 
238 int
239 main(int argc, char **argv)
240 {
241 	ARGS arg;
242 #define PROG_NAME_SIZE	39
243 	char pname[PROG_NAME_SIZE + 1];
244 	FUNCTION f, *fp;
245 	const char *prompt;
246 	char buf[1024];
247 	char *to_free = NULL;
248 	int n, i, ret = 0;
249 	char *p;
250 	LHASH_OF(FUNCTION) * prog = NULL;
251 	long errline;
252 
253 	arg.data = NULL;
254 	arg.count = 0;
255 
256 	bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
257 	if (bio_err == NULL) {
258 		fprintf(stderr, "openssl: failed to initialise bio_err\n");
259 		exit(1);
260 	}
261 
262 	CRYPTO_set_locking_callback(lock_dbg_cb);
263 
264 	openssl_startup();
265 
266 	/* Lets load up our environment a little */
267 	p = getenv("OPENSSL_CONF");
268 	if (p == NULL)
269 		p = getenv("SSLEAY_CONF");
270 	if (p == NULL) {
271 		p = to_free = make_config_name();
272 		if (p == NULL) {
273 			BIO_printf(bio_err, "error making config file name\n");
274 			goto end;
275 		}
276 	}
277 
278 	default_config_file = p;
279 
280 	config = NCONF_new(NULL);
281 	i = NCONF_load(config, p, &errline);
282 	if (i == 0) {
283 		if (ERR_GET_REASON(ERR_peek_last_error()) ==
284 		    CONF_R_NO_SUCH_FILE) {
285 			BIO_printf(bio_err,
286 			    "WARNING: can't open config file: %s\n", p);
287 			ERR_clear_error();
288 			NCONF_free(config);
289 			config = NULL;
290 		} else {
291 			ERR_print_errors(bio_err);
292 			NCONF_free(config);
293 			exit(1);
294 		}
295 	}
296 
297 	if (!load_config(bio_err, NULL)) {
298 		BIO_printf(bio_err, "failed to load configuration\n");
299 		goto end;
300 	}
301 
302 	prog = prog_init();
303 
304 	/* first check the program name */
305 	program_name(argv[0], pname, sizeof pname);
306 
307 	f.name = pname;
308 	fp = lh_FUNCTION_retrieve(prog, &f);
309 	if (fp != NULL) {
310 		argv[0] = pname;
311 		ret = fp->func(argc, argv);
312 		goto end;
313 	}
314 	/*
315 	 * ok, now check that there are not arguments, if there are, run with
316 	 * them, shifting the ssleay off the front
317 	 */
318 	if (argc != 1) {
319 		argc--;
320 		argv++;
321 		ret = do_cmd(prog, argc, argv);
322 		if (ret < 0)
323 			ret = 0;
324 		goto end;
325 	}
326 	/* ok, lets enter the old 'OpenSSL>' mode */
327 
328 	for (;;) {
329 		ret = 0;
330 		p = buf;
331 		n = sizeof buf;
332 		i = 0;
333 		for (;;) {
334 			p[0] = '\0';
335 			if (i++)
336 				prompt = ">";
337 			else
338 				prompt = "OpenSSL> ";
339 			fputs(prompt, stdout);
340 			fflush(stdout);
341 			if (!fgets(p, n, stdin))
342 				goto end;
343 			if (p[0] == '\0')
344 				goto end;
345 			i = strlen(p);
346 			if (i <= 1)
347 				break;
348 			if (p[i - 2] != '\\')
349 				break;
350 			i -= 2;
351 			p += i;
352 			n -= i;
353 		}
354 		if (!chopup_args(&arg, buf, &argc, &argv))
355 			break;
356 
357 		ret = do_cmd(prog, argc, argv);
358 		if (ret < 0) {
359 			ret = 0;
360 			goto end;
361 		}
362 		if (ret != 0)
363 			BIO_printf(bio_err, "error in %s\n", argv[0]);
364 		(void) BIO_flush(bio_err);
365 	}
366 	BIO_printf(bio_err, "bad exit\n");
367 	ret = 1;
368 
369 end:
370 	free(to_free);
371 
372 	if (config != NULL) {
373 		NCONF_free(config);
374 		config = NULL;
375 	}
376 	if (prog != NULL)
377 		lh_FUNCTION_free(prog);
378 	free(arg.data);
379 
380 	openssl_shutdown();
381 
382 	if (bio_err != NULL) {
383 		BIO_free(bio_err);
384 		bio_err = NULL;
385 	}
386 	return (ret);
387 }
388 
389 #define LIST_STANDARD_COMMANDS "list-standard-commands"
390 #define LIST_MESSAGE_DIGEST_COMMANDS "list-message-digest-commands"
391 #define LIST_MESSAGE_DIGEST_ALGORITHMS "list-message-digest-algorithms"
392 #define LIST_CIPHER_COMMANDS "list-cipher-commands"
393 #define LIST_CIPHER_ALGORITHMS "list-cipher-algorithms"
394 #define LIST_PUBLIC_KEY_ALGORITHMS "list-public-key-algorithms"
395 
396 
397 static int
398 do_cmd(LHASH_OF(FUNCTION) * prog, int argc, char *argv[])
399 {
400 	FUNCTION f, *fp;
401 	int i, ret = 1, tp, nl;
402 
403 	if ((argc <= 0) || (argv[0] == NULL)) {
404 		ret = 0;
405 		goto end;
406 	}
407 	f.name = argv[0];
408 	fp = lh_FUNCTION_retrieve(prog, &f);
409 	if (fp == NULL) {
410 		if (EVP_get_digestbyname(argv[0])) {
411 			f.type = FUNC_TYPE_MD;
412 			f.func = dgst_main;
413 			fp = &f;
414 		} else if (EVP_get_cipherbyname(argv[0])) {
415 			f.type = FUNC_TYPE_CIPHER;
416 			f.func = enc_main;
417 			fp = &f;
418 		}
419 	}
420 	if (fp != NULL) {
421 		ret = fp->func(argc, argv);
422 	} else if ((strncmp(argv[0], "no-", 3)) == 0) {
423 		BIO *bio_stdout = BIO_new_fp(stdout, BIO_NOCLOSE);
424 		f.name = argv[0] + 3;
425 		ret = (lh_FUNCTION_retrieve(prog, &f) != NULL);
426 		if (!ret)
427 			BIO_printf(bio_stdout, "%s\n", argv[0]);
428 		else
429 			BIO_printf(bio_stdout, "%s\n", argv[0] + 3);
430 		BIO_free_all(bio_stdout);
431 		goto end;
432 	} else if ((strcmp(argv[0], "quit") == 0) ||
433 	    (strcmp(argv[0], "q") == 0) ||
434 	    (strcmp(argv[0], "exit") == 0) ||
435 	    (strcmp(argv[0], "bye") == 0)) {
436 		ret = -1;
437 		goto end;
438 	} else if ((strcmp(argv[0], LIST_STANDARD_COMMANDS) == 0) ||
439 	    (strcmp(argv[0], LIST_MESSAGE_DIGEST_COMMANDS) == 0) ||
440 	    (strcmp(argv[0], LIST_MESSAGE_DIGEST_ALGORITHMS) == 0) ||
441 	    (strcmp(argv[0], LIST_CIPHER_COMMANDS) == 0) ||
442 	    (strcmp(argv[0], LIST_CIPHER_ALGORITHMS) == 0) ||
443 	    (strcmp(argv[0], LIST_PUBLIC_KEY_ALGORITHMS) == 0)) {
444 		int list_type;
445 		BIO *bio_stdout;
446 
447 		if (strcmp(argv[0], LIST_STANDARD_COMMANDS) == 0)
448 			list_type = FUNC_TYPE_GENERAL;
449 		else if (strcmp(argv[0], LIST_MESSAGE_DIGEST_COMMANDS) == 0)
450 			list_type = FUNC_TYPE_MD;
451 		else if (strcmp(argv[0], LIST_MESSAGE_DIGEST_ALGORITHMS) == 0)
452 			list_type = FUNC_TYPE_MD_ALG;
453 		else if (strcmp(argv[0], LIST_PUBLIC_KEY_ALGORITHMS) == 0)
454 			list_type = FUNC_TYPE_PKEY;
455 		else if (strcmp(argv[0], LIST_CIPHER_ALGORITHMS) == 0)
456 			list_type = FUNC_TYPE_CIPHER_ALG;
457 		else		/* strcmp(argv[0],LIST_CIPHER_COMMANDS) == 0 */
458 			list_type = FUNC_TYPE_CIPHER;
459 		bio_stdout = BIO_new_fp(stdout, BIO_NOCLOSE);
460 
461 		if (list_type == FUNC_TYPE_PKEY)
462 			list_pkey(bio_stdout);
463 		if (list_type == FUNC_TYPE_MD_ALG)
464 			list_md(bio_stdout);
465 		if (list_type == FUNC_TYPE_CIPHER_ALG)
466 			list_cipher(bio_stdout);
467 		else {
468 			for (fp = functions; fp->name != NULL; fp++)
469 				if (fp->type == list_type)
470 					BIO_printf(bio_stdout, "%s\n",
471 					    fp->name);
472 		}
473 		BIO_free_all(bio_stdout);
474 		ret = 0;
475 		goto end;
476 	} else {
477 		BIO_printf(bio_err,
478 		    "openssl:Error: '%s' is an invalid command.\n",
479 		    argv[0]);
480 		BIO_printf(bio_err, "\nStandard commands");
481 		i = 0;
482 		tp = 0;
483 		for (fp = functions; fp->name != NULL; fp++) {
484 			nl = 0;
485 #ifdef OPENSSL_NO_CAMELLIA
486 			if (((i++) % 5) == 0)
487 #else
488 			if (((i++) % 4) == 0)
489 #endif
490 			{
491 				BIO_printf(bio_err, "\n");
492 				nl = 1;
493 			}
494 			if (fp->type != tp) {
495 				tp = fp->type;
496 				if (!nl)
497 					BIO_printf(bio_err, "\n");
498 				if (tp == FUNC_TYPE_MD) {
499 					i = 1;
500 					BIO_printf(bio_err,
501 					    "\nMessage Digest commands (see the `dgst' command for more details)\n");
502 				} else if (tp == FUNC_TYPE_CIPHER) {
503 					i = 1;
504 					BIO_printf(bio_err, "\nCipher commands (see the `enc' command for more details)\n");
505 				}
506 			}
507 #ifdef OPENSSL_NO_CAMELLIA
508 			BIO_printf(bio_err, "%-15s", fp->name);
509 #else
510 			BIO_printf(bio_err, "%-18s", fp->name);
511 #endif
512 		}
513 		BIO_printf(bio_err, "\n\n");
514 		ret = 0;
515 	}
516 end:
517 	return (ret);
518 }
519 
520 static int
521 SortFnByName(const void *_f1, const void *_f2)
522 {
523 	const FUNCTION *f1 = _f1;
524 	const FUNCTION *f2 = _f2;
525 
526 	if (f1->type != f2->type)
527 		return f1->type - f2->type;
528 	return strcmp(f1->name, f2->name);
529 }
530 
531 static void
532 list_pkey(BIO * out)
533 {
534 	int i;
535 
536 	for (i = 0; i < EVP_PKEY_asn1_get_count(); i++) {
537 		const EVP_PKEY_ASN1_METHOD *ameth;
538 		int pkey_id, pkey_base_id, pkey_flags;
539 		const char *pinfo, *pem_str;
540 		ameth = EVP_PKEY_asn1_get0(i);
541 		EVP_PKEY_asn1_get0_info(&pkey_id, &pkey_base_id, &pkey_flags,
542 		    &pinfo, &pem_str, ameth);
543 		if (pkey_flags & ASN1_PKEY_ALIAS) {
544 			BIO_printf(out, "Name: %s\n",
545 			    OBJ_nid2ln(pkey_id));
546 			BIO_printf(out, "\tType: Alias to %s\n",
547 			    OBJ_nid2ln(pkey_base_id));
548 		} else {
549 			BIO_printf(out, "Name: %s\n", pinfo);
550 			BIO_printf(out, "\tType: %s Algorithm\n",
551 			    pkey_flags & ASN1_PKEY_DYNAMIC ?
552 			    "External" : "Builtin");
553 			BIO_printf(out, "\tOID: %s\n", OBJ_nid2ln(pkey_id));
554 			if (pem_str == NULL)
555 				pem_str = "(none)";
556 			BIO_printf(out, "\tPEM string: %s\n", pem_str);
557 		}
558 
559 	}
560 }
561 
562 static void
563 list_cipher_fn(const EVP_CIPHER * c, const char *from, const char *to,
564     void *arg)
565 {
566 	if (c)
567 		BIO_printf(arg, "%s\n", EVP_CIPHER_name(c));
568 	else {
569 		if (!from)
570 			from = "<undefined>";
571 		if (!to)
572 			to = "<undefined>";
573 		BIO_printf(arg, "%s => %s\n", from, to);
574 	}
575 }
576 
577 static void
578 list_cipher(BIO * out)
579 {
580 	EVP_CIPHER_do_all_sorted(list_cipher_fn, out);
581 }
582 
583 static void
584 list_md_fn(const EVP_MD * m, const char *from, const char *to, void *arg)
585 {
586 	if (m)
587 		BIO_printf(arg, "%s\n", EVP_MD_name(m));
588 	else {
589 		if (!from)
590 			from = "<undefined>";
591 		if (!to)
592 			to = "<undefined>";
593 		BIO_printf(arg, "%s => %s\n", from, to);
594 	}
595 }
596 
597 static void
598 list_md(BIO * out)
599 {
600 	EVP_MD_do_all_sorted(list_md_fn, out);
601 }
602 
603 static int
604 function_cmp(const FUNCTION * a, const FUNCTION * b)
605 {
606 	return strncmp(a->name, b->name, 8);
607 }
608 
609 static IMPLEMENT_LHASH_COMP_FN(function, FUNCTION)
610 
611 static unsigned long
612 function_hash(const FUNCTION * a)
613 {
614 	return lh_strhash(a->name);
615 }
616 
617 static IMPLEMENT_LHASH_HASH_FN(function, FUNCTION)
618 
619 static LHASH_OF(FUNCTION) *
620 prog_init(void)
621 {
622 	LHASH_OF(FUNCTION) * ret;
623 	FUNCTION *f;
624 	size_t i;
625 
626 	/* Purely so it looks nice when the user hits ? */
627 	for (i = 0, f = functions; f->name != NULL; ++f, ++i)
628 		;
629 	qsort(functions, i, sizeof *functions, SortFnByName);
630 
631 	if ((ret = lh_FUNCTION_new()) == NULL)
632 		return (NULL);
633 
634 	for (f = functions; f->name != NULL; f++)
635 		(void) lh_FUNCTION_insert(ret, f);
636 	return (ret);
637 }
638