It appears that there are a handful of places in u-boot that we want to munge the linux command line. This adds some helper functions that make that easier.
Signed-off-by: Doug Anderson <diand...@chromium.org> --- common/Makefile | 1 + common/cmdline.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/cmdline.h | 30 +++++ 3 files changed, 349 insertions(+), 0 deletions(-) create mode 100644 common/cmdline.c create mode 100644 include/cmdline.h diff --git a/common/Makefile b/common/Makefile index ae795e0..90d2ff0 100644 --- a/common/Makefile +++ b/common/Makefile @@ -186,6 +186,7 @@ COBJS-$(CONFIG_UPDATE_TFTP) += update.o COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o endif +COBJS-y += cmdline.o COBJS-y += console.o COBJS-y += memsize.o COBJS-y += stdio.o diff --git a/common/cmdline.c b/common/cmdline.c new file mode 100644 index 0000000..0862838 --- /dev/null +++ b/common/cmdline.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* Functions for munging the Linux command line */ + +/* + * To run unit tests in this file: + * gcc -DRUN_UNITTESTS -Wall -Werror common/cmdline.c -o cmdline && ./cmdline + */ +#ifdef RUN_UNITTESTS + +#define CONFIG_SILENT_CONSOLE + +#include <assert.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#else + +#include <common.h> +#include <malloc.h> + +#include <cmdline.h> + +#include <linux/ctype.h> + +#endif + + +/** + * Modify the command line to remove a parameter. + * + * This can either remove standalone parameters or ones with arguments. For + * instance you can remove the param "console=/dev/ttyS0" by passing in + * "console" and you can remove the param "earlyprintk" by passing in + * "earlyprintk". + * + * WARNING: + * - The current code doesn't handle removing parameters with spaces in + * them. Specifically, you can't use it to remove xyz if you have + * something like xyz="abc def". + * + * Notes: + * - It is _not_ considered an error to remove a parameter that doesn't exist. + * - If the parameter exists more than once, this just removes the first. + * You can loop to get them all. + * - The parameter must match exactly. AKA "onsole" doesn't match "console". + * + * @param cmdline The command line to modify. + * @param param_name The name of the parameter to remove. + * @return 1 if the param was removed; 0 otherwise. + */ +int remove_cmdline_param(char *cmdline, const char *param_name) +{ + char *start; + char *end; + int param_name_len = strlen(param_name); + + assert(param_name_len != 0); + + /* + * Try to find the param; if we find it, start will point to the + * beginning of the param and end to the character after the param + * (could be '\0', '=', or ' '). If we fail, we return 0 in the loop. + */ + start = cmdline; + while (1) { + start = strstr(start, param_name); + if (!start) + return 0; + end = start + param_name_len; + + /* + * Loop break condition is space (or nothing) before param and + * space or equals (or nothing) after param. + */ + if (((start == cmdline) || isspace(start[-1])) && + ((*end == '\0') || (*end == '=') || isspace(*end))) + break; + + start = end; + } + + /* + * Skip so end points to the start of the next param; note that we don't + * handle quoting here (!), so we'll get confused with abc="def ghi" + */ + while ((*end != '\0') && !isspace(*end)) + end++; + while (isspace(*end)) + end++; + + /* + * Move start backwards to clean up any extra spaces. After this runs, + * start will point to the place to move end onto. + */ + if (start != cmdline) { + start--; + while ((start != cmdline) && isspace(*start)) + start--; + + /* + * Two cases: + * - nothing at end: move fwd one char so we don't clobber the + * last char of the previous cmd. + * - more stuff at end: add exactly one ' ' to separate the + * chunks. + */ + start++; + if (*end != '\0') { + *start = ' '; + start++; + } + } + + /* Kill the parameter */ + memmove(start, end, strlen(end)+1); + + return 1; +} + +/** + * Add a parameter to the command line. + * + * This is much like a glorified strncat(), but handles adding a space between + * the old cmdline and the new one if needed and takes the whole bufsize + * instead of the number of characters to copy. + * + * @param cmdline The command line to modify. + * @param toappend The parameter to append, like "console=/dev/ttyS0". + * @param bufsize The number of bytes that were allocated to cmdline, so + * we know not to overrun. + */ +void add_cmdline_param(char *cmdline, const char *toappend, int bufsize) +{ + int cmdline_len = strlen(cmdline); + + if (cmdline_len == 0) + strncat(cmdline, toappend, bufsize-1); + else { + int bytes_avail = bufsize - cmdline_len; + + if (bytes_avail <= 0) { + assert(bytes_avail == 0); + return; + } + cmdline[bufsize-1] = '\0'; + cmdline[cmdline_len] = ' '; + strncpy(&cmdline[cmdline_len+1], toappend, + bytes_avail-2); + } +} + + +#ifdef RUN_UNITTESTS + +/** + * Unit tests for remove_cmdline_param(). + */ +void remove_cmdline_param_unittest(void) +{ + char *original_str; + char *expected_str; + char *result; + int retval; + + /* Try removing each bit of a reasonable sample */ + original_str = "console=ttyS0,115200n8 root=/dev/mmcblk0p3 rootwait ro"; + expected_str = "root=/dev/mmcblk0p3 rootwait ro"; + result = strdup(original_str); + retval = remove_cmdline_param(result, "console"); + assert(strcmp(result, expected_str) == 0); + assert(retval == 1); + free(result); + + original_str = "console=ttyS0,115200n8 root=/dev/mmcblk0p3 rootwait ro"; + expected_str = "console=ttyS0,115200n8 rootwait ro"; + result = strdup(original_str); + retval = remove_cmdline_param(result, "root"); + assert(strcmp(result, expected_str) == 0); + assert(retval == 1); + free(result); + + original_str = "console=ttyS0,115200n8 root=/dev/mmcblk0p3 rootwait ro"; + expected_str = "console=ttyS0,115200n8 root=/dev/mmcblk0p3 ro"; + result = strdup(original_str); + retval = remove_cmdline_param(result, "rootwait"); + assert(strcmp(result, expected_str) == 0); + assert(retval == 1); + free(result); + + original_str = "console=ttyS0,115200n8 root=/dev/mmcblk0p3 rootwait ro"; + expected_str = "console=ttyS0,115200n8 root=/dev/mmcblk0p3 rootwait"; + result = strdup(original_str); + retval = remove_cmdline_param(result, "ro"); + assert(strcmp(result, expected_str) == 0); + assert(retval == 1); + free(result); + + /* Remove something that's not there */ + original_str = "console=ttyS0,115200n8 root=/dev/mmcblk0p3 rootwait ro"; + expected_str = "console=ttyS0,115200n8 root=/dev/mmcblk0p3 rootwait ro"; + result = strdup(original_str); + retval = remove_cmdline_param(result, "oogabooga"); + assert(strcmp(result, expected_str) == 0); + assert(retval == 0); + free(result); + + /* Remove from a NULL string */ + original_str = ""; + expected_str = ""; + result = strdup(original_str); + retval = remove_cmdline_param(result, "oogabooga"); + assert(strcmp(result, expected_str) == 0); + assert(retval == 0); + free(result); + + /* Remove with an '=' based param at the end */ + original_str = "root=/dev/mmcblk0p3 rootwait ro console=ttyS0,115200n8"; + expected_str = "root=/dev/mmcblk0p3 rootwait ro"; + result = strdup(original_str); + retval = remove_cmdline_param(result, "console"); + assert(strcmp(result, expected_str) == 0); + assert(retval == 1); + free(result); + + /* Remove with a non-'=' based param at the beginning */ + original_str = "ro root=/dev/mmcblk0p3 rootwait console=ttyS0,115200n8"; + expected_str = "root=/dev/mmcblk0p3 rootwait console=ttyS0,115200n8"; + result = strdup(original_str); + retval = remove_cmdline_param(result, "ro"); + assert(strcmp(result, expected_str) == 0); + assert(retval == 1); + free(result); + + /* Add a few extra spaces and see how it deals with it */ + original_str = "console=ttyS0,115200n8\t root=/dev/mmcblk0p3 \tro"; + expected_str = "console=ttyS0,115200n8 ro"; + result = strdup(original_str); + retval = remove_cmdline_param(result, "root"); + assert(strcmp(result, expected_str) == 0); + assert(retval == 1); + free(result); + + printf("remove_cmdline_param_unittest: pass\n"); +} + +/** + * Unit tests for add_cmdline_param(). + */ +void add_cmdline_param_unittest(void) +{ + char *original_str; + char *expected_str; + char *result; + int extra_chars = strlen(" console="); + int bufsize; + + /* Simple case first; try adding "console=" */ + original_str = "root=/dev/mmcblk0p3 rootwait ro"; + expected_str = "root=/dev/mmcblk0p3 rootwait ro console="; + bufsize = strlen(original_str) + 1 + extra_chars; + result = malloc(bufsize); + strcpy(result, original_str); + add_cmdline_param(result, "console=", bufsize); + assert(strcmp(result, expected_str) == 0); + free(result); + + /* Add to an empty string; should see no ' ' before... */ + original_str = ""; + expected_str = "console="; + bufsize = strlen(original_str) + 1 + extra_chars - 1; + result = malloc(bufsize); + strcpy(result, original_str); + add_cmdline_param(result, "console=", bufsize); + assert(strcmp(result, expected_str) == 0); + free(result); + + /* Shrink down bufsize and see loss of = */ + original_str = "root=/dev/mmcblk0p3 rootwait ro"; + expected_str = "root=/dev/mmcblk0p3 rootwait ro console"; + bufsize = strlen(original_str) + 1 + extra_chars - 1; + result = malloc(bufsize); + strcpy(result, original_str); + add_cmdline_param(result, "console=", bufsize); + assert(strcmp(result, expected_str) == 0); + free(result); + + printf("add_cmdline_param_unittest: pass\n"); +} + +int main(int argc, char **argv) +{ + remove_cmdline_param_unittest(); + add_cmdline_param_unittest(); + return 0; +} +#endif diff --git a/include/cmdline.h b/include/cmdline.h new file mode 100644 index 0000000..65b415c --- /dev/null +++ b/include/cmdline.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* Functions for munging the Linux command line */ + +#ifndef _CMDLINE_H_ +#define _CMDLINE_H_ + +int remove_cmdline_param(char *cmdline, const char *param_name); +void add_cmdline_param(char *cmdline, const char *toappend, int bufsize); + +#endif /*_CMDLINE_H_ */ -- 1.7.3.1 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot