155682Smarkm/*
2178825Sdfr * Copyright (c) 1995 - 2006 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#ifdef HAVE_CONFIG_H
3555682Smarkm#include <config.h>
36178825SdfrRCSID("$Id: sl.c 21160 2007-06-18 22:58:21Z lha $");
3755682Smarkm#endif
3855682Smarkm
3955682Smarkm#include "sl_locl.h"
4072445Sassar#include <setjmp.h>
4155682Smarkm
4272445Sassarstatic void
4372445Sassarmandoc_template(SL_cmd *cmds,
4472445Sassar		const char *extra_string)
4572445Sassar{
4672445Sassar    SL_cmd *c, *prev;
4772445Sassar    char timestr[64], cmd[64];
4872445Sassar    const char *p;
4972445Sassar    time_t t;
5072445Sassar
5172445Sassar    printf(".\\\" Things to fix:\n");
5272445Sassar    printf(".\\\"   * correct section, and operating system\n");
5372445Sassar    printf(".\\\"   * remove Op from mandatory flags\n");
5472445Sassar    printf(".\\\"   * use better macros for arguments (like .Pa for files)\n");
5572445Sassar    printf(".\\\"\n");
5672445Sassar    t = time(NULL);
5772445Sassar    strftime(timestr, sizeof(timestr), "%b %d, %Y", localtime(&t));
5872445Sassar    printf(".Dd %s\n", timestr);
5978527Sassar    p = strrchr(getprogname(), '/');
6078527Sassar    if(p) p++; else p = getprogname();
6172445Sassar    strncpy(cmd, p, sizeof(cmd));
6272445Sassar    cmd[sizeof(cmd)-1] = '\0';
6372445Sassar    strupr(cmd);
6472445Sassar
6572445Sassar    printf(".Dt %s SECTION\n", cmd);
6672445Sassar    printf(".Os OPERATING_SYSTEM\n");
6772445Sassar    printf(".Sh NAME\n");
6872445Sassar    printf(".Nm %s\n", p);
6972445Sassar    printf(".Nd\n");
7072445Sassar    printf("in search of a description\n");
7172445Sassar    printf(".Sh SYNOPSIS\n");
7272445Sassar    printf(".Nm\n");
7372445Sassar    for(c = cmds; c->name; ++c) {
7472445Sassar/*	if (c->func == NULL)
7572445Sassar	    continue; */
7672445Sassar	printf(".Op Fl %s", c->name);
7772445Sassar	printf("\n");
7872445Sassar
7972445Sassar    }
8072445Sassar    if (extra_string && *extra_string)
8172445Sassar	printf (".Ar %s\n", extra_string);
8272445Sassar    printf(".Sh DESCRIPTION\n");
8372445Sassar    printf("Supported options:\n");
8472445Sassar    printf(".Bl -tag -width Ds\n");
8572445Sassar    prev = NULL;
8672445Sassar    for(c = cmds; c->name; ++c) {
8772445Sassar	if (c->func) {
8872445Sassar	    if (prev)
8972445Sassar		printf ("\n%s\n", prev->usage);
9072445Sassar
9172445Sassar	    printf (".It Fl %s", c->name);
9272445Sassar	    prev = c;
9372445Sassar	} else
9472445Sassar	    printf (", %s\n", c->name);
9572445Sassar    }
9672445Sassar    if (prev)
9772445Sassar	printf ("\n%s\n", prev->usage);
9872445Sassar
9972445Sassar    printf(".El\n");
10072445Sassar    printf(".\\\".Sh ENVIRONMENT\n");
10172445Sassar    printf(".\\\".Sh FILES\n");
10272445Sassar    printf(".\\\".Sh EXAMPLES\n");
10372445Sassar    printf(".\\\".Sh DIAGNOSTICS\n");
10472445Sassar    printf(".\\\".Sh SEE ALSO\n");
10572445Sassar    printf(".\\\".Sh STANDARDS\n");
10672445Sassar    printf(".\\\".Sh HISTORY\n");
10772445Sassar    printf(".\\\".Sh AUTHORS\n");
10872445Sassar    printf(".\\\".Sh BUGS\n");
10972445Sassar}
11072445Sassar
111178825SdfrSL_cmd *
11255682Smarkmsl_match (SL_cmd *cmds, char *cmd, int exactp)
11355682Smarkm{
11455682Smarkm    SL_cmd *c, *current = NULL, *partial_cmd = NULL;
11555682Smarkm    int partial_match = 0;
11655682Smarkm
11755682Smarkm    for (c = cmds; c->name; ++c) {
11855682Smarkm	if (c->func)
11955682Smarkm	    current = c;
12055682Smarkm	if (strcmp (cmd, c->name) == 0)
12155682Smarkm	    return current;
12255682Smarkm	else if (strncmp (cmd, c->name, strlen(cmd)) == 0 &&
12355682Smarkm		 partial_cmd != current) {
12455682Smarkm	    ++partial_match;
12555682Smarkm	    partial_cmd = current;
12655682Smarkm	}
12755682Smarkm    }
12855682Smarkm    if (partial_match == 1 && !exactp)
12955682Smarkm	return partial_cmd;
13055682Smarkm    else
13155682Smarkm	return NULL;
13255682Smarkm}
13355682Smarkm
13455682Smarkmvoid
13555682Smarkmsl_help (SL_cmd *cmds, int argc, char **argv)
13655682Smarkm{
13755682Smarkm    SL_cmd *c, *prev_c;
13855682Smarkm
13972445Sassar    if (getenv("SLMANDOC")) {
14072445Sassar	mandoc_template(cmds, NULL);
14172445Sassar	return;
14272445Sassar    }
14372445Sassar
14455682Smarkm    if (argc == 1) {
14555682Smarkm	prev_c = NULL;
14655682Smarkm	for (c = cmds; c->name; ++c) {
14755682Smarkm	    if (c->func) {
14855682Smarkm		if(prev_c)
14955682Smarkm		    printf ("\n\t%s%s", prev_c->usage ? prev_c->usage : "",
15055682Smarkm			    prev_c->usage ? "\n" : "");
15155682Smarkm		prev_c = c;
15255682Smarkm		printf ("%s", c->name);
15355682Smarkm	    } else
15455682Smarkm		printf (", %s", c->name);
15555682Smarkm	}
15655682Smarkm	if(prev_c)
15755682Smarkm	    printf ("\n\t%s%s", prev_c->usage ? prev_c->usage : "",
15855682Smarkm		    prev_c->usage ? "\n" : "");
15955682Smarkm    } else {
16055682Smarkm	c = sl_match (cmds, argv[1], 0);
16155682Smarkm	if (c == NULL)
16255682Smarkm	    printf ("No such command: %s. "
16355682Smarkm		    "Try \"help\" for a list of all commands\n",
16455682Smarkm		    argv[1]);
16555682Smarkm	else {
16655682Smarkm	    printf ("%s\t%s\n", c->name, c->usage);
16755682Smarkm	    if(c->help && *c->help)
16855682Smarkm		printf ("%s\n", c->help);
16955682Smarkm	    if((++c)->name && c->func == NULL) {
17055682Smarkm		printf ("Synonyms:");
17155682Smarkm		while (c->name && c->func == NULL)
17255682Smarkm		    printf ("\t%s", (c++)->name);
17355682Smarkm		printf ("\n");
17455682Smarkm	    }
17555682Smarkm	}
17655682Smarkm    }
17755682Smarkm}
17855682Smarkm
17955682Smarkm#ifdef HAVE_READLINE
18055682Smarkm
18155682Smarkmchar *readline(char *prompt);
18255682Smarkmvoid add_history(char *p);
18355682Smarkm
18455682Smarkm#else
18555682Smarkm
18655682Smarkmstatic char *
18755682Smarkmreadline(char *prompt)
18855682Smarkm{
18955682Smarkm    char buf[BUFSIZ];
19055682Smarkm    printf ("%s", prompt);
19155682Smarkm    fflush (stdout);
19255682Smarkm    if(fgets(buf, sizeof(buf), stdin) == NULL)
19355682Smarkm	return NULL;
194178825Sdfr    buf[strcspn(buf, "\r\n")] = '\0';
19555682Smarkm    return strdup(buf);
19655682Smarkm}
19755682Smarkm
19855682Smarkmstatic void
19955682Smarkmadd_history(char *p)
20055682Smarkm{
20155682Smarkm}
20255682Smarkm
20355682Smarkm#endif
20455682Smarkm
20555682Smarkmint
20655682Smarkmsl_command(SL_cmd *cmds, int argc, char **argv)
20755682Smarkm{
20855682Smarkm    SL_cmd *c;
20955682Smarkm    c = sl_match (cmds, argv[0], 0);
21055682Smarkm    if (c == NULL)
21155682Smarkm	return -1;
21255682Smarkm    return (*c->func)(argc, argv);
21355682Smarkm}
21455682Smarkm
21555682Smarkmstruct sl_data {
21655682Smarkm    int max_count;
21755682Smarkm    char **ptr;
21855682Smarkm};
21955682Smarkm
22055682Smarkmint
22155682Smarkmsl_make_argv(char *line, int *ret_argc, char ***ret_argv)
22255682Smarkm{
223178825Sdfr    char *p, *begining;
22455682Smarkm    int argc, nargv;
22555682Smarkm    char **argv;
226178825Sdfr    int quote = 0;
22755682Smarkm
22855682Smarkm    nargv = 10;
22955682Smarkm    argv = malloc(nargv * sizeof(*argv));
23055682Smarkm    if(argv == NULL)
23155682Smarkm	return ENOMEM;
23255682Smarkm    argc = 0;
23355682Smarkm
234178825Sdfr    p = line;
235178825Sdfr
236178825Sdfr    while(isspace((unsigned char)*p))
237178825Sdfr	p++;
238178825Sdfr    begining = p;
239178825Sdfr
240178825Sdfr    while (1) {
241178825Sdfr	if (*p == '\0') {
242178825Sdfr	    ;
243178825Sdfr	} else if (*p == '"') {
244178825Sdfr	    quote = !quote;
245178825Sdfr	    memmove(&p[0], &p[1], strlen(&p[1]) + 1);
246178825Sdfr	    continue;
247178825Sdfr	} else if (*p == '\\') {
248178825Sdfr	    if (p[1] == '\0')
249178825Sdfr		goto failed;
250178825Sdfr	    memmove(&p[0], &p[1], strlen(&p[1]) + 1);
251178825Sdfr	    p += 2;
252178825Sdfr	    continue;
253178825Sdfr	} else if (quote || !isspace((unsigned char)*p)) {
254178825Sdfr	    p++;
255178825Sdfr	    continue;
256178825Sdfr	} else
257178825Sdfr	    *p++ = '\0';
258178825Sdfr	if (quote)
259178825Sdfr	    goto failed;
26055682Smarkm	if(argc == nargv - 1) {
26155682Smarkm	    char **tmp;
26255682Smarkm	    nargv *= 2;
26355682Smarkm	    tmp = realloc (argv, nargv * sizeof(*argv));
26455682Smarkm	    if (tmp == NULL) {
26555682Smarkm		free(argv);
26655682Smarkm		return ENOMEM;
26755682Smarkm	    }
26855682Smarkm	    argv = tmp;
26955682Smarkm	}
270178825Sdfr	argv[argc++] = begining;
271178825Sdfr	while(isspace((unsigned char)*p))
272178825Sdfr	    p++;
273178825Sdfr	if (*p == '\0')
274178825Sdfr	    break;
275178825Sdfr	begining = p;
27655682Smarkm    }
27755682Smarkm    argv[argc] = NULL;
27855682Smarkm    *ret_argc = argc;
27955682Smarkm    *ret_argv = argv;
28055682Smarkm    return 0;
281178825Sdfrfailed:
282178825Sdfr    free(argv);
283178825Sdfr    return ERANGE;
28455682Smarkm}
28555682Smarkm
28672445Sassarstatic jmp_buf sl_jmp;
28772445Sassar
28872445Sassarstatic void sl_sigint(int sig)
28972445Sassar{
29072445Sassar    longjmp(sl_jmp, 1);
29172445Sassar}
29272445Sassar
29372445Sassarstatic char *sl_readline(const char *prompt)
29472445Sassar{
29572445Sassar    char *s;
29672445Sassar    void (*old)(int);
29772445Sassar    old = signal(SIGINT, sl_sigint);
29872445Sassar    if(setjmp(sl_jmp))
29972445Sassar	printf("\n");
300178825Sdfr    s = readline(rk_UNCONST(prompt));
30172445Sassar    signal(SIGINT, old);
30272445Sassar    return s;
30372445Sassar}
30472445Sassar
305178825Sdfr/* return values:
306178825Sdfr * 0 on success,
307178825Sdfr * -1 on fatal error,
308178825Sdfr * -2 if EOF, or
309178825Sdfr * return value of command */
31055682Smarkmint
31172445Sassarsl_command_loop(SL_cmd *cmds, const char *prompt, void **data)
31255682Smarkm{
31355682Smarkm    int ret = 0;
31455682Smarkm    char *buf;
31555682Smarkm    int argc;
31655682Smarkm    char **argv;
31755682Smarkm
31855682Smarkm    ret = 0;
31972445Sassar    buf = sl_readline(prompt);
32055682Smarkm    if(buf == NULL)
321178825Sdfr	return -2;
32255682Smarkm
32355682Smarkm    if(*buf)
32455682Smarkm	add_history(buf);
32555682Smarkm    ret = sl_make_argv(buf, &argc, &argv);
32655682Smarkm    if(ret) {
32755682Smarkm	fprintf(stderr, "sl_loop: out of memory\n");
32855682Smarkm	free(buf);
32955682Smarkm	return -1;
33055682Smarkm    }
33155682Smarkm    if (argc >= 1) {
33255682Smarkm	ret = sl_command(cmds, argc, argv);
33355682Smarkm	if(ret == -1) {
33455682Smarkm	    printf ("Unrecognized command: %s\n", argv[0]);
33555682Smarkm	    ret = 0;
33655682Smarkm	}
33755682Smarkm    }
33855682Smarkm    free(buf);
33955682Smarkm    free(argv);
34055682Smarkm    return ret;
34155682Smarkm}
34255682Smarkm
34355682Smarkmint
34472445Sassarsl_loop(SL_cmd *cmds, const char *prompt)
34555682Smarkm{
34655682Smarkm    void *data = NULL;
34755682Smarkm    int ret;
348178825Sdfr    while((ret = sl_command_loop(cmds, prompt, &data)) >= 0)
34955682Smarkm	;
35055682Smarkm    return ret;
35155682Smarkm}
35272445Sassar
35372445Sassarvoid
35472445Sassarsl_apropos (SL_cmd *cmd, const char *topic)
35572445Sassar{
35672445Sassar    for (; cmd->name != NULL; ++cmd)
35772445Sassar        if (cmd->usage != NULL && strstr(cmd->usage, topic) != NULL)
35872445Sassar	    printf ("%-20s%s\n", cmd->name, cmd->usage);
35972445Sassar}
360178825Sdfr
361178825Sdfr/*
362178825Sdfr * Help to be used with slc.
363178825Sdfr */
364178825Sdfr
365178825Sdfrvoid
366178825Sdfrsl_slc_help (SL_cmd *cmds, int argc, char **argv)
367178825Sdfr{
368178825Sdfr    if(argc == 0) {
369178825Sdfr	sl_help(cmds, 1, argv - 1 /* XXX */);
370178825Sdfr    } else {
371178825Sdfr	SL_cmd *c = sl_match (cmds, argv[0], 0);
372178825Sdfr 	if(c == NULL) {
373178825Sdfr	    fprintf (stderr, "No such command: %s. "
374178825Sdfr		     "Try \"help\" for a list of commands\n",
375178825Sdfr		     argv[0]);
376178825Sdfr	} else {
377178825Sdfr	    if(c->func) {
378178825Sdfr		char *fake[] = { NULL, "--help", NULL };
379178825Sdfr		fake[0] = argv[0];
380178825Sdfr		(*c->func)(2, fake);
381178825Sdfr		fprintf(stderr, "\n");
382178825Sdfr	    }
383178825Sdfr	    if(c->help && *c->help)
384178825Sdfr		fprintf (stderr, "%s\n", c->help);
385178825Sdfr	    if((++c)->name && c->func == NULL) {
386178825Sdfr		int f = 0;
387178825Sdfr		fprintf (stderr, "Synonyms:");
388178825Sdfr		while (c->name && c->func == NULL) {
389178825Sdfr		    fprintf (stderr, "%s%s", f ? ", " : " ", (c++)->name);
390178825Sdfr		    f = 1;
391178825Sdfr		}
392178825Sdfr		fprintf (stderr, "\n");
393178825Sdfr	    }
394178825Sdfr	}
395178825Sdfr    }
396178825Sdfr}
397