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> 36 RCSID("$Id: sl.c 21160 2007-06-18 22:58:21Z lha $"); 37 #endif 38 39 #include "sl_locl.h" 40 #include <setjmp.h> 41 42 static void 43 mandoc_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 111 SL_cmd * 112 sl_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 134 void 135 sl_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 181 char *readline(char *prompt); 182 void add_history(char *p); 183 184 #else 185 186 static char * 187 readline(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 198 static void 199 add_history(char *p) 200 { 201 } 202 203 #endif 204 205 int 206 sl_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 215 struct sl_data { 216 int max_count; 217 char **ptr; 218 }; 219 220 int 221 sl_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; 281 failed: 282 free(argv); 283 return ERANGE; 284 } 285 286 static jmp_buf sl_jmp; 287 288 static void sl_sigint(int sig) 289 { 290 longjmp(sl_jmp, 1); 291 } 292 293 static 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 */ 310 int 311 sl_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 343 int 344 sl_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 353 void 354 sl_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 365 void 366 sl_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