sl.c revision 178826
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#ifdef HAVE_CONFIG_H
35#include <config.h>
36RCSID("$Id: sl.c 21160 2007-06-18 22:58:21Z lha $");
37#endif
38
39#include "sl_locl.h"
40#include <setjmp.h>
41
42static void
43mandoc_template(SL_cmd *cmds,
44		const char *extra_string)
45{
46    SL_cmd *c, *prev;
47    char timestr[64], cmd[64];
48    const char *p;
49    time_t t;
50
51    printf(".\\\" Things to fix:\n");
52    printf(".\\\"   * correct section, and operating system\n");
53    printf(".\\\"   * remove Op from mandatory flags\n");
54    printf(".\\\"   * use better macros for arguments (like .Pa for files)\n");
55    printf(".\\\"\n");
56    t = time(NULL);
57    strftime(timestr, sizeof(timestr), "%b %d, %Y", localtime(&t));
58    printf(".Dd %s\n", timestr);
59    p = strrchr(getprogname(), '/');
60    if(p) p++; else p = getprogname();
61    strncpy(cmd, p, sizeof(cmd));
62    cmd[sizeof(cmd)-1] = '\0';
63    strupr(cmd);
64
65    printf(".Dt %s SECTION\n", cmd);
66    printf(".Os OPERATING_SYSTEM\n");
67    printf(".Sh NAME\n");
68    printf(".Nm %s\n", p);
69    printf(".Nd\n");
70    printf("in search of a description\n");
71    printf(".Sh SYNOPSIS\n");
72    printf(".Nm\n");
73    for(c = cmds; c->name; ++c) {
74/*	if (c->func == NULL)
75	    continue; */
76	printf(".Op Fl %s", c->name);
77	printf("\n");
78
79    }
80    if (extra_string && *extra_string)
81	printf (".Ar %s\n", extra_string);
82    printf(".Sh DESCRIPTION\n");
83    printf("Supported options:\n");
84    printf(".Bl -tag -width Ds\n");
85    prev = NULL;
86    for(c = cmds; c->name; ++c) {
87	if (c->func) {
88	    if (prev)
89		printf ("\n%s\n", prev->usage);
90
91	    printf (".It Fl %s", c->name);
92	    prev = c;
93	} else
94	    printf (", %s\n", c->name);
95    }
96    if (prev)
97	printf ("\n%s\n", prev->usage);
98
99    printf(".El\n");
100    printf(".\\\".Sh ENVIRONMENT\n");
101    printf(".\\\".Sh FILES\n");
102    printf(".\\\".Sh EXAMPLES\n");
103    printf(".\\\".Sh DIAGNOSTICS\n");
104    printf(".\\\".Sh SEE ALSO\n");
105    printf(".\\\".Sh STANDARDS\n");
106    printf(".\\\".Sh HISTORY\n");
107    printf(".\\\".Sh AUTHORS\n");
108    printf(".\\\".Sh BUGS\n");
109}
110
111SL_cmd *
112sl_match (SL_cmd *cmds, char *cmd, int exactp)
113{
114    SL_cmd *c, *current = NULL, *partial_cmd = NULL;
115    int partial_match = 0;
116
117    for (c = cmds; c->name; ++c) {
118	if (c->func)
119	    current = c;
120	if (strcmp (cmd, c->name) == 0)
121	    return current;
122	else if (strncmp (cmd, c->name, strlen(cmd)) == 0 &&
123		 partial_cmd != current) {
124	    ++partial_match;
125	    partial_cmd = current;
126	}
127    }
128    if (partial_match == 1 && !exactp)
129	return partial_cmd;
130    else
131	return NULL;
132}
133
134void
135sl_help (SL_cmd *cmds, int argc, char **argv)
136{
137    SL_cmd *c, *prev_c;
138
139    if (getenv("SLMANDOC")) {
140	mandoc_template(cmds, NULL);
141	return;
142    }
143
144    if (argc == 1) {
145	prev_c = NULL;
146	for (c = cmds; c->name; ++c) {
147	    if (c->func) {
148		if(prev_c)
149		    printf ("\n\t%s%s", prev_c->usage ? prev_c->usage : "",
150			    prev_c->usage ? "\n" : "");
151		prev_c = c;
152		printf ("%s", c->name);
153	    } else
154		printf (", %s", c->name);
155	}
156	if(prev_c)
157	    printf ("\n\t%s%s", prev_c->usage ? prev_c->usage : "",
158		    prev_c->usage ? "\n" : "");
159    } else {
160	c = sl_match (cmds, argv[1], 0);
161	if (c == NULL)
162	    printf ("No such command: %s. "
163		    "Try \"help\" for a list of all commands\n",
164		    argv[1]);
165	else {
166	    printf ("%s\t%s\n", c->name, c->usage);
167	    if(c->help && *c->help)
168		printf ("%s\n", c->help);
169	    if((++c)->name && c->func == NULL) {
170		printf ("Synonyms:");
171		while (c->name && c->func == NULL)
172		    printf ("\t%s", (c++)->name);
173		printf ("\n");
174	    }
175	}
176    }
177}
178
179#ifdef HAVE_READLINE
180
181char *readline(char *prompt);
182void add_history(char *p);
183
184#else
185
186static char *
187readline(char *prompt)
188{
189    char buf[BUFSIZ];
190    printf ("%s", prompt);
191    fflush (stdout);
192    if(fgets(buf, sizeof(buf), stdin) == NULL)
193	return NULL;
194    buf[strcspn(buf, "\r\n")] = '\0';
195    return strdup(buf);
196}
197
198static void
199add_history(char *p)
200{
201}
202
203#endif
204
205int
206sl_command(SL_cmd *cmds, int argc, char **argv)
207{
208    SL_cmd *c;
209    c = sl_match (cmds, argv[0], 0);
210    if (c == NULL)
211	return -1;
212    return (*c->func)(argc, argv);
213}
214
215struct sl_data {
216    int max_count;
217    char **ptr;
218};
219
220int
221sl_make_argv(char *line, int *ret_argc, char ***ret_argv)
222{
223    char *p, *begining;
224    int argc, nargv;
225    char **argv;
226    int quote = 0;
227
228    nargv = 10;
229    argv = malloc(nargv * sizeof(*argv));
230    if(argv == NULL)
231	return ENOMEM;
232    argc = 0;
233
234    p = line;
235
236    while(isspace((unsigned char)*p))
237	p++;
238    begining = p;
239
240    while (1) {
241	if (*p == '\0') {
242	    ;
243	} else if (*p == '"') {
244	    quote = !quote;
245	    memmove(&p[0], &p[1], strlen(&p[1]) + 1);
246	    continue;
247	} else if (*p == '\\') {
248	    if (p[1] == '\0')
249		goto failed;
250	    memmove(&p[0], &p[1], strlen(&p[1]) + 1);
251	    p += 2;
252	    continue;
253	} else if (quote || !isspace((unsigned char)*p)) {
254	    p++;
255	    continue;
256	} else
257	    *p++ = '\0';
258	if (quote)
259	    goto failed;
260	if(argc == nargv - 1) {
261	    char **tmp;
262	    nargv *= 2;
263	    tmp = realloc (argv, nargv * sizeof(*argv));
264	    if (tmp == NULL) {
265		free(argv);
266		return ENOMEM;
267	    }
268	    argv = tmp;
269	}
270	argv[argc++] = begining;
271	while(isspace((unsigned char)*p))
272	    p++;
273	if (*p == '\0')
274	    break;
275	begining = p;
276    }
277    argv[argc] = NULL;
278    *ret_argc = argc;
279    *ret_argv = argv;
280    return 0;
281failed:
282    free(argv);
283    return ERANGE;
284}
285
286static jmp_buf sl_jmp;
287
288static void sl_sigint(int sig)
289{
290    longjmp(sl_jmp, 1);
291}
292
293static char *sl_readline(const char *prompt)
294{
295    char *s;
296    void (*old)(int);
297    old = signal(SIGINT, sl_sigint);
298    if(setjmp(sl_jmp))
299	printf("\n");
300    s = readline(rk_UNCONST(prompt));
301    signal(SIGINT, old);
302    return s;
303}
304
305/* return values:
306 * 0 on success,
307 * -1 on fatal error,
308 * -2 if EOF, or
309 * return value of command */
310int
311sl_command_loop(SL_cmd *cmds, const char *prompt, void **data)
312{
313    int ret = 0;
314    char *buf;
315    int argc;
316    char **argv;
317
318    ret = 0;
319    buf = sl_readline(prompt);
320    if(buf == NULL)
321	return -2;
322
323    if(*buf)
324	add_history(buf);
325    ret = sl_make_argv(buf, &argc, &argv);
326    if(ret) {
327	fprintf(stderr, "sl_loop: out of memory\n");
328	free(buf);
329	return -1;
330    }
331    if (argc >= 1) {
332	ret = sl_command(cmds, argc, argv);
333	if(ret == -1) {
334	    printf ("Unrecognized command: %s\n", argv[0]);
335	    ret = 0;
336	}
337    }
338    free(buf);
339    free(argv);
340    return ret;
341}
342
343int
344sl_loop(SL_cmd *cmds, const char *prompt)
345{
346    void *data = NULL;
347    int ret;
348    while((ret = sl_command_loop(cmds, prompt, &data)) >= 0)
349	;
350    return ret;
351}
352
353void
354sl_apropos (SL_cmd *cmd, const char *topic)
355{
356    for (; cmd->name != NULL; ++cmd)
357        if (cmd->usage != NULL && strstr(cmd->usage, topic) != NULL)
358	    printf ("%-20s%s\n", cmd->name, cmd->usage);
359}
360
361/*
362 * Help to be used with slc.
363 */
364
365void
366sl_slc_help (SL_cmd *cmds, int argc, char **argv)
367{
368    if(argc == 0) {
369	sl_help(cmds, 1, argv - 1 /* XXX */);
370    } else {
371	SL_cmd *c = sl_match (cmds, argv[0], 0);
372 	if(c == NULL) {
373	    fprintf (stderr, "No such command: %s. "
374		     "Try \"help\" for a list of commands\n",
375		     argv[0]);
376	} else {
377	    if(c->func) {
378		char *fake[] = { NULL, "--help", NULL };
379		fake[0] = argv[0];
380		(*c->func)(2, fake);
381		fprintf(stderr, "\n");
382	    }
383	    if(c->help && *c->help)
384		fprintf (stderr, "%s\n", c->help);
385	    if((++c)->name && c->func == NULL) {
386		int f = 0;
387		fprintf (stderr, "Synonyms:");
388		while (c->name && c->func == NULL) {
389		    fprintf (stderr, "%s%s", f ? ", " : " ", (c++)->name);
390		    f = 1;
391		}
392		fprintf (stderr, "\n");
393	    }
394	}
395    }
396}
397