1/*
2 * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <config.h>
35
36#include "sl_locl.h"
37#include <setjmp.h>
38
39static void
40mandoc_template(SL_cmd *cmds,
41		const char *extra_string)
42{
43    SL_cmd *c, *prev;
44    char timestr[64], cmd[64];
45    const char *p;
46    time_t t;
47
48    printf(".\\\" Things to fix:\n");
49    printf(".\\\"   * correct section, and operating system\n");
50    printf(".\\\"   * remove Op from mandatory flags\n");
51    printf(".\\\"   * use better macros for arguments (like .Pa for files)\n");
52    printf(".\\\"\n");
53    t = time(NULL);
54    strftime(timestr, sizeof(timestr), "%b %d, %Y", localtime(&t));
55    printf(".Dd %s\n", timestr);
56    p = strrchr(getprogname(), '/');
57    if(p) p++; else p = getprogname();
58    strncpy(cmd, p, sizeof(cmd));
59    cmd[sizeof(cmd)-1] = '\0';
60    strupr(cmd);
61
62    printf(".Dt %s SECTION\n", cmd);
63    printf(".Os OPERATING_SYSTEM\n");
64    printf(".Sh NAME\n");
65    printf(".Nm %s\n", p);
66    printf(".Nd\n");
67    printf("in search of a description\n");
68    printf(".Sh SYNOPSIS\n");
69    printf(".Nm\n");
70    for(c = cmds; c->name; ++c) {
71/*	if (c->func == NULL)
72	    continue; */
73	printf(".Op Fl %s", c->name);
74	printf("\n");
75
76    }
77    if (extra_string && *extra_string)
78	printf (".Ar %s\n", extra_string);
79    printf(".Sh DESCRIPTION\n");
80    printf("Supported options:\n");
81    printf(".Bl -tag -width Ds\n");
82    prev = NULL;
83    for(c = cmds; c->name; ++c) {
84	if (c->func) {
85	    if (prev)
86		printf ("\n%s\n", prev->usage);
87
88	    printf (".It Fl %s", c->name);
89	    prev = c;
90	} else
91	    printf (", %s\n", c->name);
92    }
93    if (prev)
94	printf ("\n%s\n", prev->usage);
95
96    printf(".El\n");
97    printf(".\\\".Sh ENVIRONMENT\n");
98    printf(".\\\".Sh FILES\n");
99    printf(".\\\".Sh EXAMPLES\n");
100    printf(".\\\".Sh DIAGNOSTICS\n");
101    printf(".\\\".Sh SEE ALSO\n");
102    printf(".\\\".Sh STANDARDS\n");
103    printf(".\\\".Sh HISTORY\n");
104    printf(".\\\".Sh AUTHORS\n");
105    printf(".\\\".Sh BUGS\n");
106}
107
108SL_cmd *
109sl_match (SL_cmd *cmds, char *cmd, int exactp)
110{
111    SL_cmd *c, *current = NULL, *partial_cmd = NULL;
112    int partial_match = 0;
113
114    for (c = cmds; c->name; ++c) {
115	if (c->func)
116	    current = c;
117	if (strcmp (cmd, c->name) == 0)
118	    return current;
119	else if (strncmp (cmd, c->name, strlen(cmd)) == 0 &&
120		 partial_cmd != current) {
121	    ++partial_match;
122	    partial_cmd = current;
123	}
124    }
125    if (partial_match == 1 && !exactp)
126	return partial_cmd;
127    else
128	return NULL;
129}
130
131void
132sl_help (SL_cmd *cmds, int argc, char **argv)
133{
134    SL_cmd *c, *prev_c;
135
136    if (getenv("SLMANDOC")) {
137	mandoc_template(cmds, NULL);
138	return;
139    }
140
141    if (argc == 1) {
142	prev_c = NULL;
143	for (c = cmds; c->name; ++c) {
144	    if (c->func) {
145		if(prev_c)
146		    printf ("\n\t%s%s", prev_c->usage ? prev_c->usage : "",
147			    prev_c->usage ? "\n" : "");
148		prev_c = c;
149		printf ("%s", c->name);
150	    } else
151		printf (", %s", c->name);
152	}
153	if(prev_c)
154	    printf ("\n\t%s%s", prev_c->usage ? prev_c->usage : "",
155		    prev_c->usage ? "\n" : "");
156    } else {
157	c = sl_match (cmds, argv[1], 0);
158	if (c == NULL)
159	    printf ("No such command: %s. "
160		    "Try \"help\" for a list of all commands\n",
161		    argv[1]);
162	else {
163	    printf ("%s\t%s\n", c->name, c->usage);
164	    if(c->help && *c->help)
165		printf ("%s\n", c->help);
166	    if((++c)->name && c->func == NULL) {
167		printf ("Synonyms:");
168		while (c->name && c->func == NULL)
169		    printf ("\t%s", (c++)->name);
170		printf ("\n");
171	    }
172	}
173    }
174}
175
176#ifdef HAVE_READLINE
177
178char *readline(char *prompt);
179void add_history(char *p);
180
181#else
182
183static char *
184readline(char *prompt)
185{
186    char buf[BUFSIZ];
187    printf ("%s", prompt);
188    fflush (stdout);
189    if(fgets(buf, sizeof(buf), stdin) == NULL)
190	return NULL;
191    buf[strcspn(buf, "\r\n")] = '\0';
192    return strdup(buf);
193}
194
195static void
196add_history(char *p)
197{
198}
199
200#endif
201
202int
203sl_command(SL_cmd *cmds, int argc, char **argv)
204{
205    SL_cmd *c;
206    c = sl_match (cmds, argv[0], 0);
207    if (c == NULL)
208	return -1;
209    return (*c->func)(argc, argv);
210}
211
212struct sl_data {
213    int max_count;
214    char **ptr;
215};
216
217int
218sl_make_argv(char *line, int *ret_argc, char ***ret_argv)
219{
220    char *p, *begining;
221    int argc, nargv;
222    char **argv;
223    int quote = 0;
224
225    nargv = 10;
226    argv = malloc(nargv * sizeof(*argv));
227    if(argv == NULL)
228	return ENOMEM;
229    argc = 0;
230
231    p = line;
232
233    while(isspace((unsigned char)*p))
234	p++;
235    begining = p;
236
237    while (1) {
238	if (*p == '\0') {
239	    ;
240	} else if (*p == '"') {
241	    quote = !quote;
242	    memmove(&p[0], &p[1], strlen(&p[1]) + 1);
243	    continue;
244	} else if (*p == '\\') {
245	    if (p[1] == '\0')
246		goto failed;
247	    memmove(&p[0], &p[1], strlen(&p[1]) + 1);
248	    p += 2;
249	    continue;
250	} else if (quote || !isspace((unsigned char)*p)) {
251	    p++;
252	    continue;
253	} else
254	    *p++ = '\0';
255	if (quote)
256	    goto failed;
257	if(argc == nargv - 1) {
258	    char **tmp;
259	    nargv *= 2;
260	    tmp = realloc (argv, nargv * sizeof(*argv));
261	    if (tmp == NULL) {
262		free(argv);
263		return ENOMEM;
264	    }
265	    argv = tmp;
266	}
267	argv[argc++] = begining;
268	while(isspace((unsigned char)*p))
269	    p++;
270	if (*p == '\0')
271	    break;
272	begining = p;
273    }
274    argv[argc] = NULL;
275    *ret_argc = argc;
276    *ret_argv = argv;
277    return 0;
278failed:
279    free(argv);
280    return ERANGE;
281}
282
283static jmp_buf sl_jmp;
284
285static void sl_sigint(int sig)
286{
287    longjmp(sl_jmp, 1);
288}
289
290static char *sl_readline(const char *prompt)
291{
292    char *s;
293    void (*old)(int);
294    old = signal(SIGINT, sl_sigint);
295    if(setjmp(sl_jmp))
296	printf("\n");
297    s = readline(rk_UNCONST(prompt));
298    signal(SIGINT, old);
299    return s;
300}
301
302/* return values:
303 * 0 on success,
304 * -1 on fatal error,
305 * -2 if EOF, or
306 * return value of command */
307int
308sl_command_loop(SL_cmd *cmds, const char *prompt, void **data)
309{
310    int ret = 0;
311    char *buf;
312    int argc;
313    char **argv;
314
315    buf = sl_readline(prompt);
316    if(buf == NULL)
317	return -2;
318
319    if(*buf)
320	add_history(buf);
321    ret = sl_make_argv(buf, &argc, &argv);
322    if(ret) {
323	fprintf(stderr, "sl_loop: out of memory\n");
324	free(buf);
325	return -1;
326    }
327    if (argc >= 1) {
328	ret = sl_command(cmds, argc, argv);
329	if(ret == -1) {
330	    printf ("Unrecognized command: %s\n", argv[0]);
331	    ret = 0;
332	}
333    }
334    free(buf);
335    free(argv);
336    return ret;
337}
338
339int
340sl_loop(SL_cmd *cmds, const char *prompt)
341{
342    void *data = NULL;
343    int ret;
344    while((ret = sl_command_loop(cmds, prompt, &data)) >= 0)
345	;
346    return ret;
347}
348
349void
350sl_apropos (SL_cmd *cmd, const char *topic)
351{
352    for (; cmd->name != NULL; ++cmd)
353        if (cmd->usage != NULL && strstr(cmd->usage, topic) != NULL)
354	    printf ("%-20s%s\n", cmd->name, cmd->usage);
355}
356
357/*
358 * Help to be used with slc.
359 */
360
361void
362sl_slc_help (SL_cmd *cmds, int argc, char **argv)
363{
364    if(argc == 0) {
365	sl_help(cmds, 1, argv - 1 /* XXX */);
366    } else {
367	SL_cmd *c = sl_match (cmds, argv[0], 0);
368 	if(c == NULL) {
369	    fprintf (stderr, "No such command: %s. "
370		     "Try \"help\" for a list of commands\n",
371		     argv[0]);
372	} else {
373	    if(c->func) {
374		static char help[] = "--help";
375		char *fake[3];
376		fake[0] = argv[0];
377		fake[1] = help;
378		fake[2] = NULL;
379		(*c->func)(2, fake);
380		fprintf(stderr, "\n");
381	    }
382	    if(c->help && *c->help)
383		fprintf (stderr, "%s\n", c->help);
384	    if((++c)->name && c->func == NULL) {
385		int f = 0;
386		fprintf (stderr, "Synonyms:");
387		while (c->name && c->func == NULL) {
388		    fprintf (stderr, "%s%s", f ? ", " : " ", (c++)->name);
389		    f = 1;
390		}
391		fprintf (stderr, "\n");
392	    }
393	}
394    }
395}
396