Use getopt_long instead of custom arg parsing

One side affect of this change is we no longer copy the filename args into
malloc'ed buffers, we just use const char pointers.

This also fixes how we recognize filename extensions, to ensure that the
filename really ends with the extension (i.e. .bin, not .binary).

BUG=chromium-os:21568
TEST=builds clean;
 Test following cases:
  cyapa_fw_update
  cyapa_fw_update -h
  cyapa_fw_update -v
  cyapa_fw_update -f
  cyapa_fw_update -f -b
  cyapa_fw_update -b -o bkup.bin
  cyapa_fw_update -b -o bkup
  cyapa_fw_update -f fw.iic
  cyapa_fw_update -f -b fw.binary

Change-Id: I48c2f686e2118e8b1ca91a54e87ee29c80f37b2e
diff --git a/cyapa_fw_update.c b/cyapa_fw_update.c
index b83c664..28cd3f0 100644
--- a/cyapa_fw_update.c
+++ b/cyapa_fw_update.c
@@ -108,8 +108,8 @@
 	bool backup_fw;
 	bool force;
 
-	char *new_fw_image;
-	char *bak_fw_image;
+	const char *new_fw_image;
+	const char *bak_fw_image;
 
 	int fd_dev;
 	int fd_new_fw;
@@ -179,14 +179,14 @@
 	printf("Usage: %s <new-firmware-image> [options]\n",
 		name ? name : DEFAULT_PROGRAM_NAME);
 	printf("Options:\n");
-	printf("\t -b\n");
+	printf("\t -b, --backup\n");
 	printf("\t     Backup current firmware before updating"
 			" to new firmware.\n");
 	printf("\t     If -o option is not set, the default back up"
 			" path is \"/tmp/cypress\".\n");
 	printf("\t     If -o option is set, use the path specified in"
 			"-o <path>.\n");
-	printf("\t -o path\n");
+	printf("\t -o, --output <path>\n");
 	printf("\t     Specifies full path of the output file for where to\n");
 	printf("\t     back up a copy of the current trackpad firmware.\n");
 	printf("\t     Only valid when option -b is set and\n");
@@ -195,15 +195,15 @@
 	printf("\t             %s\n", DEFAULT_FW_BAK_IMAGE_NAME);
 	printf("\t     If the requested update fails, this backed up copy\n");
 	printf("\t     will be rewritten to the trackpad.\n");
-	printf("\t -f\n");
+	printf("\t -f, --force\n");
 	printf("\t     Force new firmware to be updated to trackpad device\n");
 	printf("\t     and suppress any prompt information.\n");
 	printf("\t     After update, the system will be rebooted"
 			" immediately\n");
 	printf("\t     if required, so be careful when using this option.\n");
-	printf("\t -v, -V\n");
+	printf("\t -v, --version\n");
 	printf("\t     Print version information and exit.\n");
-	printf("\t -h, -H, --help\n");
+	printf("\t -h, --help\n");
 	printf("\t     Show this help information.\n");
 	printf("NOTE:\n");
 	printf("       This program must be executed as root\n");
@@ -225,6 +225,25 @@
 	printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
 }
 
+static struct option options[] = {
+		{"backup", no_argument, NULL, 'b'},
+		{"force", no_argument, NULL, 'f'},
+		{"help", no_argument, NULL, 'h'},
+		{"output", required_argument, NULL, 'o'},
+		{"version", no_argument, NULL, 'v'},
+		{0, 0, 0, 0}
+};
+
+
+bool ends_with(const char* name, const char* ext)
+{
+	size_t nlen = strlen(name);
+	size_t elen = strlen(ext);
+
+	return (nlen >= elen && !strcmp(&name[nlen-elen], ext));
+}
+
+
 /**
  * return value:
  *     0 - parse and get valid input parameters.
@@ -234,103 +253,56 @@
  */
 int check_input_args(int argc, char **argv, struct args *args)
 {
-	int i;
-	int ret = 0;
-	int fd = 0;
-	char *filename = NULL;
-	char *extstr = NULL;
-	char *temp_path = NULL;
+	int c;
 
-	memset(args, 0, sizeof(struct args));
+	while (1) {
+		int index = 0;
 
-	/* parse options for this program. */
-	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "-b")) {
+		c = getopt_long(argc, argv, "bfho:v", options, &index);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'b':
 			args->backup_fw = true;
-		} else if (!strcmp(argv[i], "-o")) {
-			i++;
-			if (argv[i] == NULL) {
-				ret = -1;
-				goto error;
-			}
-
-			extstr = strrchr(argv[i], '.');
-			if ((extstr == NULL) ||
-				(strcmp(extstr, ".bin") != 0)) {
-				ret = -2;
-				goto error;
-			}
-
-			if (access(argv[i], F_OK))
-				remove(argv[i]);
-			args->bak_fw_image = calloc(1, strlen(argv[i]) + 1);
-			if (args->bak_fw_image == NULL) {
-				ret = -3;
-				goto error;
-			}
-			strcpy(args->bak_fw_image, argv[i]);
-		} else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-H")
-			|| !strcmp(argv[i], "--help")) {
-			ret = 1;
-			goto error;
-		} else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "-V")) {
-			ret = 2;
-			goto error;
-		} else if (!strcmp(argv[i], "-f")) {
+			break;
+		case 'f':
 			args->force = true;
-		} else {
-			/* parse new firmware file. */
-			if (argv[i] == NULL || access(argv[i], R_OK)) {
-				ret = -4;
-				goto error;
-			}
-
-			args->new_fw_image = calloc(1, strlen(argv[i]) + 1);
-			if (args->new_fw_image == NULL) {
-				ret = -5;
-				goto error;
-			}
-			strcpy(args->new_fw_image, argv[i]);
+			break;
+		case 'o':
+			args->bak_fw_image = optarg;
+			break;
+		case 'v':
+			return 2;
+		default:
+			return 1;
 		}
 	}
 
-	if (args->new_fw_image != NULL) {
-		temp_path = calloc(1, PATH_MAX);
-		if (temp_path == NULL) {
-			ret = -6;
-			goto error;
-		}
-		strcpy(temp_path, args->new_fw_image);
+	/* The last option should be the path of a new firmware image */
+	if (optind < argc)
+		args->new_fw_image = argv[optind++];
 
-		filename = basename(temp_path);
-		extstr = strrchr(filename, '.');
-		if (extstr && !strcmp(extstr, ".iic"))
-			args->file_type = FILE_TYPE_IIC;
-		else if (extstr && !strcmp(extstr, ".bin"))
-			args->file_type = FILE_TYPE_BIN;
-		else {
-			ret = -7;
-			goto error;
-		}
-	}
+	if (args->new_fw_image == NULL)
+		return 1;
 
-	if (args->backup_fw && args->bak_fw_image == NULL) {
-		args->bak_fw_image =
-			calloc(1, strlen(DEFAULT_FW_BAK_IMAGE_NAME) + 1);
-		if (args->bak_fw_image == NULL) {
-			ret = -8;
-			goto error;
-		}
+	if (ends_with(args->new_fw_image, ".iic"))
+		args->file_type = FILE_TYPE_IIC;
+	else if (ends_with(args->new_fw_image, ".bin"))
+		args->file_type = FILE_TYPE_BIN;
+	else
+		return 1;
 
-		strcpy(args->bak_fw_image, DEFAULT_FW_BAK_IMAGE_NAME);
+	if (args->backup_fw && !args->bak_fw_image) {
+		int fd;
+
+		args->bak_fw_image = DEFAULT_FW_BAK_IMAGE_NAME;
 		mkdir(DEFAULT_FW_BAK_FOLDER, 0777);
 		remove(args->bak_fw_image);
 		fd = creat(args->bak_fw_image, O_CREAT |
 			O_TRUNC | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
-		if (fd < 0) {
-			ret = -9;
-			goto error;
-		}
+		if (fd < 0)
+			return 1;
 		close(fd);
 	}
 
@@ -341,28 +313,7 @@
 		prt_info("Backup firmware image from trackpad device to: %s\n",
 			args->bak_fw_image);
 
-	free(temp_path);
 	return 0;
-
-error:
-	if (temp_path == NULL)
-		temp_path = malloc(PATH_MAX);
-	if (temp_path == NULL || args->new_fw_image == NULL) {
-		filename = NULL;
-	} else {
-		memset(temp_path, 0, PATH_MAX);
-		strcpy(temp_path, args->new_fw_image);
-		filename = basename(temp_path);
-	}
-	if (ret == 2)
-		show_version_info(filename);
-	else
-		show_usage(filename);
-
-	free(temp_path);
-	free(args->new_fw_image);
-	free(args->bak_fw_image);
-	return ret;
 }
 
 #define MAX_PROGRESS_LENGTH 110
@@ -1729,20 +1680,24 @@
 	int err_code = 0;
 	int input;
 	int fd_temp = 0;
-	struct args args;
+	struct args args = { 0 };
 	struct cyapa_firmware_ver fw_version;
 	int old_protocol = -1;
 	int new_protocol = -1;
 
-	prt_info("\n");
 	/* only root priority user can execute this program. */
 	if (check_run_as_root(argv[0]) < 0)
 		return -1;
 
 	/* parse input parameters. */
-	memset(&args, 0, sizeof(struct args));
-	if (check_input_args(argc, argv, &args) != 0)
-		return -2;
+	err_code = check_input_args(argc, argv, &args);
+	if (err_code == 2) {
+		show_version_info(basename(argv[0]));
+		exit(0);
+	} else if (err_code == 1) {
+		show_usage(basename(argv[0]));
+		exit(0);
+	}
 
 	/* show warning info about reboot system after firmware updated. */
 	if (!args.force) {
@@ -1914,8 +1869,6 @@
 		close(args.fd_new_fw);
 	if (args.fd_bak_fw > 0)
 		close(args.fd_bak_fw);
-	free(args.new_fw_image);
-	free(args.bak_fw_image);
 
 	prt_info("\n");