Skip to content
Snippets Groups Projects
main.c 33.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • {
    	char *p = buffer;
    
    #ifdef CONFIG_CMDLINE_EDITING
    
    	unsigned int len = CONFIG_SYS_CBSIZE;
    
    	static int initted = 0;
    
    
    	/*
    	 * History uses a global array which is not
    	 * writable until after relocation to RAM.
    	 * Revert to non-history version if still
    	 * running from flash.
    	 */
    	if (gd->flags & GD_FLG_RELOC) {
    		if (!initted) {
    			hist_init();
    			initted = 1;
    		}
    
    
    		if (prompt)
    			puts (prompt);
    
    		rc = cread_line(prompt, p, &len, timeout);
    
    		return rc < 0 ? rc : len;
    
    	} else {
    #endif	/* CONFIG_CMDLINE_EDITING */
    
    Kumar Gala's avatar
    Kumar Gala committed
    	char * p_buf = p;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int	n = 0;				/* buffer index		*/
    	int	plen = 0;			/* prompt length	*/
    	int	col;				/* output column cnt	*/
    	char	c;
    
    	/* print prompt */
    	if (prompt) {
    		plen = strlen (prompt);
    		puts (prompt);
    	}
    	col = plen;
    
    	for (;;) {
    #ifdef CONFIG_BOOT_RETRY_TIME
    		while (!tstc()) {	/* while no incoming data */
    			if (retry_time >= 0 && get_ticks() > endtime)
    				return (-2);	/* timed out */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    #endif
    		WATCHDOG_RESET();		/* Trigger watchdog, if needed */
    
    #ifdef CONFIG_SHOW_ACTIVITY
    		while (!tstc()) {
    			show_activity(0);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    #endif
    		c = getc();
    
    		/*
    		 * Special character handling
    		 */
    		switch (c) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		case '\n':
    			*p = '\0';
    			puts ("\r\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    			p_buf[0] = '\0';	/* discard input */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		case 0x15:			/* ^U - erase line	*/
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			while (col > plen) {
    				puts (erase_seq);
    				--col;
    			}
    
    			p = p_buf;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			n = 0;
    			continue;
    
    
    		case 0x17:			/* ^W - erase word	*/
    
    			p=delete_char(p_buf, p, &col, &n, plen);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			while ((n > 0) && (*p != ' ')) {
    
    				p=delete_char(p_buf, p, &col, &n, plen);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			}
    			continue;
    
    
    		case 0x08:			/* ^H  - backspace	*/
    		case 0x7F:			/* DEL - backspace	*/
    
    			p=delete_char(p_buf, p, &col, &n, plen);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			continue;
    
    		default:
    			/*
    			 * Must be a normal character then
    			 */
    
    			if (n < CONFIG_SYS_CBSIZE-2) {
    
    				if (c == '\t') {	/* expand TABs */
    
    #ifdef CONFIG_AUTO_COMPLETE
    					/* if auto completion triggered just continue */
    					*p = '\0';
    					if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
    
    						p = p_buf + n;	/* reset */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    					puts (tab_seq+(col&07));
    					col += 8 - (col&07);
    				} else {
    
    					 * Echo input using puts() to force an
    
    					 * LCD flush if we are using an LCD
    					 */
    					++col;
    					buf[0] = c;
    					buf[1] = '\0';
    					puts(buf);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				}
    				*p++ = c;
    				++n;
    			} else {			/* Buffer full		*/
    				putc ('\a');
    			}
    		}
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    /****************************************************************************/
    
    static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen)
    {
    	char *s;
    
    	if (*np == 0) {
    		return (p);
    	}
    
    	if (*(--p) == '\t') {			/* will retype the whole line	*/
    		while (*colp > plen) {
    			puts (erase_seq);
    			(*colp)--;
    		}
    		for (s=buffer; s<p; ++s) {
    			if (*s == '\t') {
    				puts (tab_seq+((*colp) & 07));
    				*colp += 8 - ((*colp) & 07);
    			} else {
    				++(*colp);
    				putc (*s);
    			}
    		}
    	} else {
    		puts (erase_seq);
    		(*colp)--;
    	}
    	(*np)--;
    	return (p);
    }
    
    /****************************************************************************/
    
    int parse_line (char *line, char *argv[])
    {
    	int nargs = 0;
    
    
    	debug_parser("parse_line: \"%s\"\n", line);
    
    	while (nargs < CONFIG_SYS_MAXARGS) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		/* skip any white space */
    
    		while (isblank(*line))
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			++line;
    
    		if (*line == '\0') {	/* end of line, no more args	*/
    			argv[nargs] = NULL;
    
    			debug_parser("parse_line: nargs=%d\n", nargs);
    			return nargs;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    
    		argv[nargs++] = line;	/* begin of argument string	*/
    
    		/* find end of string */
    
    		while (*line && !isblank(*line))
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			++line;
    
    		if (*line == '\0') {	/* end of line, no more args	*/
    			argv[nargs] = NULL;
    
    			debug_parser("parse_line: nargs=%d\n", nargs);
    			return nargs;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    
    		*line++ = '\0';		/* terminate current arg	 */
    	}
    
    
    	printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	debug_parser("parse_line: nargs=%d\n", nargs);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return (nargs);
    }
    
    /****************************************************************************/
    
    
    #ifndef CONFIG_SYS_HUSH_PARSER
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    static void process_macros (const char *input, char *output)
    {
    	char c, prev;
    	const char *varname_start = NULL;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int inputcnt = strlen (input);
    
    	int outputcnt = CONFIG_SYS_CBSIZE;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int state = 0;		/* 0 = waiting for '$'  */
    
    	/* 1 = waiting for '(' or '{' */
    	/* 2 = waiting for ')' or '}' */
    	/* 3 = waiting for '''  */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	char *output_start = output;
    
    
    	debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
    		     input);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	prev = '\0';		/* previous character   */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	while (inputcnt && outputcnt) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		c = *input++;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		inputcnt--;
    
    		if (state != 3) {
    			/* remove one level of escape characters */
    			if ((c == '\\') && (prev != '\\')) {
    				if (inputcnt-- == 0)
    					break;
    				prev = c;
    				c = *input++;
    			}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		switch (state) {
    		case 0:	/* Waiting for (unescaped) $    */
    			if ((c == '\'') && (prev != '\\')) {
    				state = 3;
    				break;
    			}
    			if ((c == '$') && (prev != '\\')) {
    				state++;
    			} else {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				*(output++) = c;
    				outputcnt--;
    			}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			break;
    		case 1:	/* Waiting for (        */
    			if (c == '(' || c == '{') {
    				state++;
    				varname_start = input;
    			} else {
    				state = 0;
    				*(output++) = '$';
    				outputcnt--;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				if (outputcnt) {
    					*(output++) = c;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    					outputcnt--;
    				}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			}
    			break;
    		case 2:	/* Waiting for )        */
    			if (c == ')' || c == '}') {
    				int i;
    
    				char envname[CONFIG_SYS_CBSIZE], *envval;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				int envcnt = input - varname_start - 1;	/* Varname # of chars */
    
    				/* Get the varname */
    				for (i = 0; i < envcnt; i++) {
    					envname[i] = varname_start[i];
    				}
    				envname[i] = 0;
    
    				/* Get its value */
    				envval = getenv (envname);
    
    				/* Copy into the line if it exists */
    				if (envval != NULL)
    					while ((*envval) && outputcnt) {
    						*(output++) = *(envval++);
    						outputcnt--;
    					}
    				/* Look for another '$' */
    				state = 0;
    			}
    			break;
    		case 3:	/* Waiting for '        */
    			if ((c == '\'') && (prev != '\\')) {
    				state = 0;
    			} else {
    				*(output++) = c;
    				outputcnt--;
    			}
    			break;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		prev = c;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	if (outputcnt)
    		*output = 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
    		     strlen(output_start), output_start);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    /****************************************************************************
     * returns:
     *	1  - command executed, repeatable
     *	0  - command executed but not repeatable, interrupted commands are
     *	     always considered not repeatable
     *	-1 - not executed (unrecognized, bootd recursion or too many args)
    
     *           (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *           considered unrecognized)
     *
     * WARNING:
     *
     * We must create a temporary copy of the command since the command we get
     * may be the result from getenv(), which returns a pointer directly to
     * the environment data, which may change magicly when the command we run
     * creates or modifies environment variables (like "bootp" does).
     */
    
    static int builtin_run_command(const char *cmd, int flag)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	char *token;			/* start of token in cmdbuf	*/
    	char *sep;			/* end of token (separator) in cmdbuf */
    
    	char finaltoken[CONFIG_SYS_CBSIZE];
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	char *str = cmdbuf;
    
    	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int repeatable = 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
    	if (DEBUG_PARSER) {
    		/* use puts - string may be loooong */
    		puts(cmd ? cmd : "NULL");
    		puts("\"\n");
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	clear_ctrlc();		/* forget any previous Control C */
    
    	if (!cmd || !*cmd) {
    		return -1;	/* empty command */
    	}
    
    
    	if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		puts ("## Command too long!\n");
    		return -1;
    	}
    
    	strcpy (cmdbuf, cmd);
    
    	/* Process separators and check for invalid
    	 * repeatable commands
    	 */
    
    
    	debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	while (*str) {
    
    		/*
    		 * Find separator, or string end
    		 * Allow simple escape of ';' by writing "\;"
    		 */
    
    		for (inquotes = 0, sep = str; *sep; sep++) {
    			if ((*sep=='\'') &&
    			    (*(sep-1) != '\\'))
    				inquotes=!inquotes;
    
    			if (!inquotes &&
    			    (*sep == ';') &&	/* separator		*/
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			    ( sep != str) &&	/* past string start	*/
    			    (*(sep-1) != '\\'))	/* and NOT escaped	*/
    				break;
    		}
    
    		/*
    		 * Limit the token to data between separators
    		 */
    		token = str;
    		if (*sep) {
    			str = sep + 1;	/* start of command for next pass */
    			*sep = '\0';
    		}
    		else
    			str = sep;	/* no more commands for next pass */
    
    		debug_parser("token: \"%s\"\n", token);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		/* find macros in this token and replace them */
    		process_macros (token, finaltoken);
    
    		/* Extract arguments */
    
    		if ((argc = parse_line (finaltoken, argv)) == 0) {
    			rc = -1;	/* no command at all */
    			continue;
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if (cmd_process(flag, argc, argv, &repeatable, NULL))
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		/* Did the user stop this? */
    		if (had_ctrlc ())
    
    			return -1;	/* if stopped then not repeatable */
    
    	return rc ? rc : repeatable;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    /*
     * Run a command using the selected parser.
     *
     * @param cmd	Command to run
     * @param flag	Execution flags (CMD_FLAG_...)
     * @return 0 on success, or != 0 on error.
     */
    int run_command(const char *cmd, int flag)
    {
    #ifndef CONFIG_SYS_HUSH_PARSER
    	/*
    	 * builtin_run_command can return 0 or 1 for success, so clean up
    	 * its result.
    	 */
    	if (builtin_run_command(cmd, flag) == -1)
    		return 1;
    
    	return 0;
    #else
    	return parse_string_outer(cmd,
    			FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);
    #endif
    }
    
    
    #ifndef CONFIG_SYS_HUSH_PARSER
    /**
     * Execute a list of command separated by ; or \n using the built-in parser.
     *
     * This function cannot take a const char * for the command, since if it
     * finds newlines in the string, it replaces them with \0.
     *
     * @param cmd	String containing list of commands
     * @param flag	Execution flags (CMD_FLAG_...)
     * @return 0 on success, or != 0 on error.
     */
    static int builtin_run_command_list(char *cmd, int flag)
    {
    	char *line, *next;
    	int rcode = 0;
    
    	/*
    	 * Break into individual lines, and execute each line; terminate on
    	 * error.
    	 */
    	line = next = cmd;
    	while (*next) {
    		if (*next == '\n') {
    			*next = '\0';
    			/* run only non-empty commands */
    			if (*line) {
    				debug("** exec: \"%s\"\n", line);
    				if (builtin_run_command(line, 0) < 0) {
    					rcode = 1;
    					break;
    				}
    			}
    			line = next + 1;
    		}
    		++next;
    	}
    	if (rcode == 0 && *line)
    		rcode = (builtin_run_command(line, 0) >= 0);
    
    	return rcode;
    }
    #endif
    
    int run_command_list(const char *cmd, int len, int flag)
    {
    	int need_buff = 1;
    	char *buff = (char *)cmd;	/* cast away const */
    	int rcode = 0;
    
    	if (len == -1) {
    		len = strlen(cmd);
    #ifdef CONFIG_SYS_HUSH_PARSER
    		/* hush will never change our string */
    		need_buff = 0;
    #else
    		/* the built-in parser will change our string if it sees \n */
    		need_buff = strchr(cmd, '\n') != NULL;
    #endif
    	}
    	if (need_buff) {
    		buff = malloc(len + 1);
    		if (!buff)
    			return 1;
    		memcpy(buff, cmd, len);
    		buff[len] = '\0';
    	}
    #ifdef CONFIG_SYS_HUSH_PARSER
    	rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
    #else
    	/*
    	 * This function will overwrite any \n it sees with a \0, which
    	 * is why it can't work with a const char *. Here we are making
    	 * using of internal knowledge of this function, to avoid always
    	 * doing a malloc() which is actually required only in a case that
    	 * is pretty rare.
    	 */
    	rcode = builtin_run_command_list(buff, flag);
    	if (need_buff)
    		free(buff);
    #endif
    
    	return rcode;
    }
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /****************************************************************************/
    
    
    int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	int i;
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	for (i=1; i<argc; ++i) {
    
    		char *arg;
    
    		if ((arg = getenv (argv[i])) == NULL) {
    			printf ("## Error: \"%s\" not defined\n", argv[i]);
    			return 1;
    		}
    
    		if (run_command(arg, flag) != 0)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }