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