155682Smarkm/* 255682Smarkm * Copyright (c) 1985, 1989, 1993, 1994 355682Smarkm * The Regents of the University of California. All rights reserved. 455682Smarkm * 555682Smarkm * Redistribution and use in source and binary forms, with or without 655682Smarkm * modification, are permitted provided that the following conditions 755682Smarkm * are met: 855682Smarkm * 1. Redistributions of source code must retain the above copyright 955682Smarkm * notice, this list of conditions and the following disclaimer. 1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1155682Smarkm * notice, this list of conditions and the following disclaimer in the 1255682Smarkm * documentation and/or other materials provided with the distribution. 1355682Smarkm * 3. All advertising materials mentioning features or use of this software 1455682Smarkm * must display the following acknowledgement: 1555682Smarkm * This product includes software developed by the University of 1655682Smarkm * California, Berkeley and its contributors. 1755682Smarkm * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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/* 3555682Smarkm * FTP User Program -- Command Interface. 3655682Smarkm */ 3755682Smarkm 3855682Smarkm#include "ftp_locl.h" 39102644Snectar#include <getarg.h> 4055682Smarkm 41233294SstasRCSID("$Id$"); 42102644Snectar 43102644Snectarstatic int help_flag; 44102644Snectarstatic int version_flag; 45102644Snectarstatic int debug_flag; 46102644Snectar 47102644Snectarstruct getargs getargs[] = { 48102644Snectar { NULL, 'd', arg_flag, &debug_flag, 49102644Snectar "debug", NULL }, 50102644Snectar { NULL, 'g', arg_negative_flag, &doglob, 51102644Snectar "disables globbing", NULL}, 52102644Snectar { NULL, 'i', arg_negative_flag, &interactive, 53102644Snectar "Turn off interactive prompting", NULL}, 54102644Snectar { NULL, 'l', arg_negative_flag, &lineedit, 55102644Snectar "Turn off line editing", NULL}, 56120945Snectar { NULL, 'n', arg_negative_flag, &autologin, 57120945Snectar "Turn off auto-login", NULL}, 58102644Snectar { NULL, 'p', arg_flag, &passivemode, 59102644Snectar "passive mode", NULL}, 60102644Snectar { NULL, 't', arg_counter, &trace, 61102644Snectar "Packet tracing", NULL}, 62127808Snectar#ifdef KRB5 63127808Snectar { "gss-bindings", 0, arg_negative_flag, &ftp_do_gss_bindings, 64178825Sdfr "Don't use GSS-API bindings", NULL}, 65178825Sdfr { "gss-delegate", 0, arg_negative_flag, &ftp_do_gss_delegate, 66178825Sdfr "Disable delegation of GSS-API credentials", NULL}, 67127808Snectar#endif 68102644Snectar { NULL, 'v', arg_counter, &verbose, 69102644Snectar "verbosity", NULL}, 70102644Snectar { NULL, 'K', arg_negative_flag, &use_kerberos, 71102644Snectar "Disable kerberos authentication", NULL}, 72178825Sdfr { "encrypt", 'x', arg_flag, &doencrypt, 73178825Sdfr "Encrypt command and data channel if possible" }, 74102644Snectar { "version", 0, arg_flag, &version_flag }, 75102644Snectar { "help", 'h', arg_flag, &help_flag }, 76102644Snectar}; 77102644Snectar 78102644Snectarstatic int num_args = sizeof(getargs) / sizeof(getargs[0]); 79102644Snectar 80102644Snectarstatic void 81102644Snectarusage(int ecode) 82102644Snectar{ 83102644Snectar arg_printusage(getargs, num_args, NULL, "[host [port]]"); 84102644Snectar exit(ecode); 85102644Snectar} 86102644Snectar 8755682Smarkmint 8855682Smarkmmain(int argc, char **argv) 8955682Smarkm{ 90102644Snectar int top; 9155682Smarkm struct passwd *pw = NULL; 9255682Smarkm char homedir[MaxPathLen]; 9355682Smarkm struct servent *sp; 94102644Snectar int optind = 0; 9555682Smarkm 9678527Sassar setprogname(argv[0]); 9755682Smarkm 9855682Smarkm sp = getservbyname("ftp", "tcp"); 9955682Smarkm if (sp == 0) 10055682Smarkm errx(1, "ftp/tcp: unknown service"); 10155682Smarkm doglob = 1; 10255682Smarkm interactive = 1; 10355682Smarkm autologin = 1; 10472445Sassar lineedit = 1; 10555682Smarkm passivemode = 0; /* passive mode not active */ 10672445Sassar use_kerberos = 1; 107127808Snectar#ifdef KRB5 108127808Snectar ftp_do_gss_bindings = 1; 109127808Snectar#endif 11055682Smarkm 111102644Snectar if(getarg(getargs, num_args, argc, argv, &optind)) 112102644Snectar usage(1); 113102644Snectar if(help_flag) 114102644Snectar usage(0); 115102644Snectar if(version_flag) { 116102644Snectar print_version(NULL); 117102644Snectar exit(0); 118102644Snectar } 11955682Smarkm 120102644Snectar if (debug_flag) { 121102644Snectar options |= SO_DEBUG; 122102644Snectar debug++; 123102644Snectar } 12455682Smarkm 12555682Smarkm argc -= optind; 12655682Smarkm argv += optind; 12755682Smarkm 12855682Smarkm fromatty = isatty(fileno(stdin)); 12955682Smarkm if (fromatty) 13055682Smarkm verbose++; 13155682Smarkm cpend = 0; /* no pending replies */ 13255682Smarkm proxy = 0; /* proxy not active */ 13355682Smarkm crflag = 1; /* strip c.r. on ascii gets */ 13455682Smarkm sendport = -1; /* not using ports */ 13555682Smarkm /* 13655682Smarkm * Set up the home directory in case we're globbing. 13755682Smarkm */ 13855682Smarkm pw = k_getpwuid(getuid()); 13955682Smarkm if (pw != NULL) { 14055682Smarkm strlcpy(homedir, pw->pw_dir, sizeof(homedir)); 14155682Smarkm home = homedir; 14255682Smarkm } 14355682Smarkm if (argc > 0) { 14455682Smarkm char *xargv[5]; 145233294Sstas 14655682Smarkm if (setjmp(toplevel)) 14755682Smarkm exit(0); 14855682Smarkm signal(SIGINT, intr); 14955682Smarkm signal(SIGPIPE, lostpeer); 15078527Sassar xargv[0] = (char*)getprogname(); 15155682Smarkm xargv[1] = argv[0]; 15255682Smarkm xargv[2] = argv[1]; 15355682Smarkm xargv[3] = argv[2]; 15455682Smarkm xargv[4] = NULL; 15555682Smarkm setpeer(argc+1, xargv); 15655682Smarkm } 15755682Smarkm if(setjmp(toplevel) == 0) 15855682Smarkm top = 1; 15955682Smarkm else 16055682Smarkm top = 0; 16155682Smarkm if (top) { 16255682Smarkm signal(SIGINT, intr); 16355682Smarkm signal(SIGPIPE, lostpeer); 16455682Smarkm } 16555682Smarkm for (;;) { 16655682Smarkm cmdscanner(top); 16755682Smarkm top = 1; 16855682Smarkm } 16955682Smarkm} 17055682Smarkm 17155682Smarkmvoid 17255682Smarkmintr(int sig) 17355682Smarkm{ 17455682Smarkm 17555682Smarkm longjmp(toplevel, 1); 17655682Smarkm} 17755682Smarkm 17855682Smarkm#ifndef SHUT_RDWR 17955682Smarkm#define SHUT_RDWR 2 18055682Smarkm#endif 18155682Smarkm 18255682SmarkmRETSIGTYPE 18355682Smarkmlostpeer(int sig) 18455682Smarkm{ 18555682Smarkm 18655682Smarkm if (connected) { 18755682Smarkm if (cout != NULL) { 18855682Smarkm shutdown(fileno(cout), SHUT_RDWR); 18955682Smarkm fclose(cout); 19055682Smarkm cout = NULL; 19155682Smarkm } 19255682Smarkm if (data >= 0) { 19355682Smarkm shutdown(data, SHUT_RDWR); 19455682Smarkm close(data); 19555682Smarkm data = -1; 19655682Smarkm } 19755682Smarkm connected = 0; 19855682Smarkm } 19955682Smarkm pswitch(1); 20055682Smarkm if (connected) { 20155682Smarkm if (cout != NULL) { 20255682Smarkm shutdown(fileno(cout), SHUT_RDWR); 20355682Smarkm fclose(cout); 20455682Smarkm cout = NULL; 20555682Smarkm } 20655682Smarkm connected = 0; 20755682Smarkm } 20855682Smarkm proxflag = 0; 20955682Smarkm pswitch(0); 21055682Smarkm sec_end(); 21155682Smarkm SIGRETURN(0); 21255682Smarkm} 21355682Smarkm 21455682Smarkm/* 21555682Smarkmchar * 21655682Smarkmtail(filename) 21755682Smarkm char *filename; 21855682Smarkm{ 21955682Smarkm char *s; 220233294Sstas 22155682Smarkm while (*filename) { 22255682Smarkm s = strrchr(filename, '/'); 22355682Smarkm if (s == NULL) 22455682Smarkm break; 22555682Smarkm if (s[1]) 22655682Smarkm return (s + 1); 22755682Smarkm *s = '\0'; 22855682Smarkm } 22955682Smarkm return (filename); 23055682Smarkm} 23155682Smarkm*/ 23255682Smarkm 23355682Smarkmstatic char * 23472445Sassarsimple_readline(char *prompt) 23555682Smarkm{ 23655682Smarkm char buf[BUFSIZ]; 23755682Smarkm printf ("%s", prompt); 23855682Smarkm fflush (stdout); 23955682Smarkm if(fgets(buf, sizeof(buf), stdin) == NULL) 24055682Smarkm return NULL; 24155682Smarkm if (buf[strlen(buf) - 1] == '\n') 24255682Smarkm buf[strlen(buf) - 1] = '\0'; 24355682Smarkm return strdup(buf); 24455682Smarkm} 24555682Smarkm 24672445Sassar#ifndef HAVE_READLINE 24772445Sassar 24872445Sassarstatic char * 24972445Sassarreadline(char *prompt) 25072445Sassar{ 25172445Sassar return simple_readline (prompt); 25272445Sassar} 25372445Sassar 25455682Smarkmstatic void 25555682Smarkmadd_history(char *p) 25655682Smarkm{ 25755682Smarkm} 25855682Smarkm 25955682Smarkm#else 26055682Smarkm 26155682Smarkm/* These should not really be here */ 26255682Smarkm 26355682Smarkmchar *readline(char *); 26455682Smarkmvoid add_history(char *); 26555682Smarkm 26655682Smarkm#endif 26755682Smarkm 26855682Smarkm/* 26955682Smarkm * Command parser. 27055682Smarkm */ 27155682Smarkmvoid 27255682Smarkmcmdscanner(int top) 27355682Smarkm{ 27455682Smarkm struct cmd *c; 27555682Smarkm int l; 27655682Smarkm 27755682Smarkm if (!top) 27855682Smarkm putchar('\n'); 27955682Smarkm for (;;) { 28055682Smarkm if (fromatty) { 28155682Smarkm char *p; 28272445Sassar if (lineedit) 28372445Sassar p = readline("ftp> "); 28472445Sassar else 28572445Sassar p = simple_readline("ftp> "); 28672445Sassar if(p == NULL) { 28772445Sassar printf("\n"); 28855682Smarkm quit(0, 0); 28972445Sassar } 29055682Smarkm strlcpy(line, p, sizeof(line)); 29172445Sassar if (lineedit) 29272445Sassar add_history(p); 29355682Smarkm free(p); 29455682Smarkm } else{ 29555682Smarkm if (fgets(line, sizeof line, stdin) == NULL) 29655682Smarkm quit(0, 0); 29755682Smarkm } 29855682Smarkm /* XXX will break on long lines */ 29955682Smarkm l = strlen(line); 30055682Smarkm if (l == 0) 30155682Smarkm break; 30255682Smarkm if (line[--l] == '\n') { 30355682Smarkm if (l == 0) 30455682Smarkm break; 30555682Smarkm line[l] = '\0'; 30655682Smarkm } else if (l == sizeof(line) - 2) { 30755682Smarkm printf("sorry, input line too long\n"); 30855682Smarkm while ((l = getchar()) != '\n' && l != EOF) 30955682Smarkm /* void */; 31055682Smarkm break; 31155682Smarkm } /* else it was a line without a newline */ 31255682Smarkm makeargv(); 31355682Smarkm if (margc == 0) { 31455682Smarkm continue; 31555682Smarkm } 31655682Smarkm c = getcmd(margv[0]); 31755682Smarkm if (c == (struct cmd *)-1) { 31855682Smarkm printf("?Ambiguous command\n"); 31955682Smarkm continue; 32055682Smarkm } 32155682Smarkm if (c == 0) { 32255682Smarkm printf("?Invalid command\n"); 32355682Smarkm continue; 32455682Smarkm } 32555682Smarkm if (c->c_conn && !connected) { 32655682Smarkm printf("Not connected.\n"); 32755682Smarkm continue; 32855682Smarkm } 32955682Smarkm (*c->c_handler)(margc, margv); 33055682Smarkm if (bell && c->c_bell) 33155682Smarkm putchar('\007'); 33255682Smarkm if (c->c_handler != help) 33355682Smarkm break; 33455682Smarkm } 33555682Smarkm signal(SIGINT, intr); 33655682Smarkm signal(SIGPIPE, lostpeer); 33755682Smarkm} 33855682Smarkm 33955682Smarkmstruct cmd * 34055682Smarkmgetcmd(char *name) 34155682Smarkm{ 34255682Smarkm char *p, *q; 34355682Smarkm struct cmd *c, *found; 34455682Smarkm int nmatches, longest; 34555682Smarkm 34655682Smarkm longest = 0; 34755682Smarkm nmatches = 0; 34855682Smarkm found = 0; 34955682Smarkm for (c = cmdtab; (p = c->c_name); c++) { 35055682Smarkm for (q = name; *q == *p++; q++) 35155682Smarkm if (*q == 0) /* exact match? */ 35255682Smarkm return (c); 35355682Smarkm if (!*q) { /* the name was a prefix */ 35455682Smarkm if (q - name > longest) { 35555682Smarkm longest = q - name; 35655682Smarkm nmatches = 1; 35755682Smarkm found = c; 35855682Smarkm } else if (q - name == longest) 35955682Smarkm nmatches++; 36055682Smarkm } 36155682Smarkm } 36255682Smarkm if (nmatches > 1) 36355682Smarkm return ((struct cmd *)-1); 36455682Smarkm return (found); 36555682Smarkm} 36655682Smarkm 36755682Smarkm/* 36855682Smarkm * Slice a string up into argc/argv. 36955682Smarkm */ 37055682Smarkm 37155682Smarkmint slrflag; 37255682Smarkm 37355682Smarkmvoid 37455682Smarkmmakeargv(void) 37555682Smarkm{ 37655682Smarkm char **argp; 37755682Smarkm 37855682Smarkm argp = margv; 37955682Smarkm stringbase = line; /* scan from first of buffer */ 38055682Smarkm argbase = argbuf; /* store from first of buffer */ 38155682Smarkm slrflag = 0; 38255682Smarkm for (margc = 0; ; margc++) { 38355682Smarkm /* Expand array if necessary */ 38455682Smarkm if (margc == margvlen) { 38555682Smarkm int i; 38655682Smarkm 38755682Smarkm margv = (margvlen == 0) 38855682Smarkm ? (char **)malloc(20 * sizeof(char *)) 38955682Smarkm : (char **)realloc(margv, 39055682Smarkm (margvlen + 20)*sizeof(char *)); 39155682Smarkm if (margv == NULL) 39255682Smarkm errx(1, "cannot realloc argv array"); 39355682Smarkm for(i = margvlen; i < margvlen + 20; ++i) 39455682Smarkm margv[i] = NULL; 39555682Smarkm margvlen += 20; 39655682Smarkm argp = margv + margc; 39755682Smarkm } 39855682Smarkm 39955682Smarkm if ((*argp++ = slurpstring()) == NULL) 40055682Smarkm break; 40155682Smarkm } 40255682Smarkm 40355682Smarkm} 40455682Smarkm 40555682Smarkm/* 40655682Smarkm * Parse string into argbuf; 40755682Smarkm * implemented with FSM to 40855682Smarkm * handle quoting and strings 40955682Smarkm */ 41055682Smarkmchar * 41155682Smarkmslurpstring(void) 41255682Smarkm{ 41355682Smarkm int got_one = 0; 41455682Smarkm char *sb = stringbase; 41555682Smarkm char *ap = argbase; 41655682Smarkm char *tmp = argbase; /* will return this if token found */ 41755682Smarkm 41855682Smarkm if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 41955682Smarkm switch (slrflag) { /* and $ as token for macro invoke */ 42055682Smarkm case 0: 42155682Smarkm slrflag++; 42255682Smarkm stringbase++; 42355682Smarkm return ((*sb == '!') ? "!" : "$"); 42455682Smarkm /* NOTREACHED */ 42555682Smarkm case 1: 42655682Smarkm slrflag++; 42755682Smarkm altarg = stringbase; 42855682Smarkm break; 42955682Smarkm default: 43055682Smarkm break; 43155682Smarkm } 43255682Smarkm } 43355682Smarkm 43455682SmarkmS0: 43555682Smarkm switch (*sb) { 43655682Smarkm 43755682Smarkm case '\0': 43855682Smarkm goto OUT; 43955682Smarkm 44055682Smarkm case ' ': 44155682Smarkm case '\t': 44255682Smarkm sb++; goto S0; 44355682Smarkm 44455682Smarkm default: 44555682Smarkm switch (slrflag) { 44655682Smarkm case 0: 44755682Smarkm slrflag++; 44855682Smarkm break; 44955682Smarkm case 1: 45055682Smarkm slrflag++; 45155682Smarkm altarg = sb; 45255682Smarkm break; 45355682Smarkm default: 45455682Smarkm break; 45555682Smarkm } 45655682Smarkm goto S1; 45755682Smarkm } 45855682Smarkm 45955682SmarkmS1: 46055682Smarkm switch (*sb) { 46155682Smarkm 46255682Smarkm case ' ': 46355682Smarkm case '\t': 46455682Smarkm case '\0': 46555682Smarkm goto OUT; /* end of token */ 46655682Smarkm 46755682Smarkm case '\\': 46855682Smarkm sb++; goto S2; /* slurp next character */ 46955682Smarkm 47055682Smarkm case '"': 47155682Smarkm sb++; goto S3; /* slurp quoted string */ 47255682Smarkm 47355682Smarkm default: 47455682Smarkm *ap++ = *sb++; /* add character to token */ 47555682Smarkm got_one = 1; 47655682Smarkm goto S1; 47755682Smarkm } 47855682Smarkm 47955682SmarkmS2: 48055682Smarkm switch (*sb) { 48155682Smarkm 48255682Smarkm case '\0': 48355682Smarkm goto OUT; 48455682Smarkm 48555682Smarkm default: 48655682Smarkm *ap++ = *sb++; 48755682Smarkm got_one = 1; 48855682Smarkm goto S1; 48955682Smarkm } 49055682Smarkm 49155682SmarkmS3: 49255682Smarkm switch (*sb) { 49355682Smarkm 49455682Smarkm case '\0': 49555682Smarkm goto OUT; 49655682Smarkm 49755682Smarkm case '"': 49855682Smarkm sb++; goto S1; 49955682Smarkm 50055682Smarkm default: 50155682Smarkm *ap++ = *sb++; 50255682Smarkm got_one = 1; 50355682Smarkm goto S3; 50455682Smarkm } 50555682Smarkm 50655682SmarkmOUT: 50755682Smarkm if (got_one) 50855682Smarkm *ap++ = '\0'; 50955682Smarkm argbase = ap; /* update storage pointer */ 51055682Smarkm stringbase = sb; /* update scan pointer */ 51155682Smarkm if (got_one) { 51255682Smarkm return (tmp); 51355682Smarkm } 51455682Smarkm switch (slrflag) { 51555682Smarkm case 0: 51655682Smarkm slrflag++; 51755682Smarkm break; 51855682Smarkm case 1: 51955682Smarkm slrflag++; 52055682Smarkm altarg = (char *) 0; 52155682Smarkm break; 52255682Smarkm default: 52355682Smarkm break; 52455682Smarkm } 52555682Smarkm return NULL; 52655682Smarkm} 52755682Smarkm 52855682Smarkm#define HELPINDENT ((int) sizeof ("directory")) 52955682Smarkm 53055682Smarkm/* 53155682Smarkm * Help command. 53255682Smarkm * Call each command handler with argc == 0 and argv[0] == name. 53355682Smarkm */ 53455682Smarkmvoid 53555682Smarkmhelp(int argc, char **argv) 53655682Smarkm{ 53755682Smarkm struct cmd *c; 53855682Smarkm 53955682Smarkm if (argc == 1) { 54055682Smarkm int i, j, w, k; 54155682Smarkm int columns, width = 0, lines; 54255682Smarkm 54355682Smarkm printf("Commands may be abbreviated. Commands are:\n\n"); 54455682Smarkm for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 54555682Smarkm int len = strlen(c->c_name); 54655682Smarkm 54755682Smarkm if (len > width) 54855682Smarkm width = len; 54955682Smarkm } 55055682Smarkm width = (width + 8) &~ 7; 55155682Smarkm columns = 80 / width; 55255682Smarkm if (columns == 0) 55355682Smarkm columns = 1; 55455682Smarkm lines = (NCMDS + columns - 1) / columns; 55555682Smarkm for (i = 0; i < lines; i++) { 55655682Smarkm for (j = 0; j < columns; j++) { 55755682Smarkm c = cmdtab + j * lines + i; 558233294Sstas if ((!proxy || c->c_proxy)) { 55955682Smarkm printf("%s", c->c_name); 560233294Sstas } else { 56155682Smarkm for (k=0; k < strlen(c->c_name); k++) { 56255682Smarkm putchar(' '); 56355682Smarkm } 56455682Smarkm } 56555682Smarkm if (c + lines >= &cmdtab[NCMDS]) { 56655682Smarkm printf("\n"); 56755682Smarkm break; 56855682Smarkm } 56955682Smarkm w = strlen(c->c_name); 57055682Smarkm while (w < width) { 57155682Smarkm w = (w + 8) &~ 7; 57255682Smarkm putchar('\t'); 57355682Smarkm } 57455682Smarkm } 57555682Smarkm } 57655682Smarkm return; 57755682Smarkm } 57855682Smarkm while (--argc > 0) { 57955682Smarkm char *arg; 58055682Smarkm arg = *++argv; 58155682Smarkm c = getcmd(arg); 58255682Smarkm if (c == (struct cmd *)-1) 58355682Smarkm printf("?Ambiguous help command %s\n", arg); 58455682Smarkm else if (c == (struct cmd *)0) 58555682Smarkm printf("?Invalid help command %s\n", arg); 58655682Smarkm else 58755682Smarkm printf("%-*s\t%s\n", HELPINDENT, 58855682Smarkm c->c_name, c->c_help); 58955682Smarkm } 59055682Smarkm} 591