blob: fe3f479902ef8b4918e99d203244041867da05ad [file] [log] [blame]
Goffredo Baroncelli80d26602012-02-12 11:43:14 -05001/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License v2 as published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 * General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public
12 * License along with this program; if not, write to the
13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14 * Boston, MA 021110-1307, USA.
15 *
Avi Shchislowskidc7ab962016-03-08 14:22:41 -050016 * Modified to add field firmware update support,
17 * those modifications are Copyright (c) 2016 SanDisk Corp.
18 *
Goffredo Baroncelli80d26602012-02-12 11:43:14 -050019 * (This code is based on btrfs-progs/btrfs.c.)
20 */
21
22#define _GNU_SOURCE
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
Chris Ball45541d52012-02-12 11:49:53 -050027#include "mmc_cmds.h"
28
Goffredo Baroncelli80d26602012-02-12 11:43:14 -050029#define MMC_VERSION "0.1"
30
31#define BASIC_HELP 0
32#define ADVANCED_HELP 1
33
34typedef int (*CommandFunction)(int argc, char **argv);
35
36struct Command {
37 CommandFunction func; /* function which implements the command */
38 int nargs; /* if == 999, any number of arguments
39 if >= 0, number of arguments,
40 if < 0, _minimum_ number of arguments */
41 char *verb; /* verb */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -080042 char *help; /* help lines; from the 2nd line onward they
43 are automatically indented */
44 char *adv_help; /* advanced help message; from the 2nd line
45 onward they are automatically indented */
Goffredo Baroncelli80d26602012-02-12 11:43:14 -050046
47 /* the following fields are run-time filled by the program */
48 char **cmds; /* array of subcommands */
49 int ncmds; /* number of subcommand */
50};
51
52static struct Command commands[] = {
53 /*
54 * avoid short commands different for the case only
55 */
Chris Ball45541d52012-02-12 11:49:53 -050056 { do_read_extcsd, -1,
57 "extcsd read", "<device>\n"
58 "Print extcsd data from <device>.",
59 NULL
60 },
Nick Sanders9d57aa72014-03-05 21:38:54 -080061 { do_dump_extcsd, -1,
62 "extcsd dump", "<device>\n"
63 "Print raw extcsd data from <device>.",
64 NULL
65 },
Al Cooper1b7f5d72016-06-07 16:35:46 -040066 { do_writeprotect_boot_get, -1,
67 "writeprotect boot get", "<device>\n"
68 "Print the boot partitions write protect status for <device>.",
Chris Ballb9c7a172012-02-20 12:34:25 -050069 NULL
70 },
Al Cooper1b7f5d72016-06-07 16:35:46 -040071 { do_writeprotect_boot_set, -1,
72 "writeprotect boot set", "<device>\n"
73 "Set the boot partitions write protect status for <device>.\nThis sets the eMMC boot partitions to be write-protected until\nthe next boot.",
74 NULL
75 },
76 { do_writeprotect_user_set, -4,
77 "writeprotect user set", "<type>" "<start block>" "<blocks>" "<device>\n"
78#ifdef DANGEROUS_COMMANDS_ENABLED
79 "Set the write protect configuration for the specified region\nof the user area for <device>.\n<type> must be \"none|temp|pwron|perm\".\n \"none\" - Clear temporary write protection.\n \"temp\" - Set temporary write protection.\n \"pwron\" - Set write protection until the next poweron.\n \"perm\" - Set permanent write protection.\n<start block> specifies the first block of the protected area.\n<blocks> specifies the size of the protected area in blocks.\nNOTE! The area must start and end on Write Protect Group\nboundries, Use the \"writeprotect user get\" command to get the\nWrite Protect Group size.\nNOTE! \"perm\" is a one-time programmable (unreversible) change.",
80#else
81 "Set the write protect configuration for the specified region\nof the user area for <device>.\n<type> must be \"none|temp|pwron\".\n \"none\" - Clear temporary write protection.\n \"temp\" - Set temporary write protection.\n \"pwron\" - Set write protection until the next poweron.\n<start block> specifies the first block of the protected area.\n<blocks> specifies the size of the protected area in blocks.\nNOTE! The area must start and end on Write Protect Group\nboundries, Use the \"writeprotect user get\" command to get the\nWrite Protect Group size.",
82#endif /* DANGEROUS_COMMANDS_ENABLED */
83 NULL
84 },
85 { do_writeprotect_user_get, -1,
86 "writeprotect user get", "<device>\n"
87 "Print the user areas write protect configuration for <device>.",
Chris Ball45541d52012-02-12 11:49:53 -050088 NULL
89 },
Saugata Dasb7e25992012-05-17 09:26:34 -040090 { do_disable_512B_emulation, -1,
91 "disable 512B emulation", "<device>\n"
Chris Ball65997802012-09-21 18:19:25 +080092 "Set the eMMC data sector size to 4KB by disabling emulation on\n<device>.",
Saugata Dasb7e25992012-05-17 09:26:34 -040093 NULL
94 },
Balaji T Kd78ce082015-04-29 18:12:33 -040095 { do_create_gp_partition, -6,
Tomas Melin766a3cd2016-08-29 11:59:42 -040096 "gp create", "<-y|-n|-c> " "<length KiB> " "<partition> " "<enh_attr> " "<ext_attr> " "<device>\n"
97 "Create general purpose partition for the <device>.\nDry-run only unless -y or -c is passed.\nUse -c if more partitioning settings are still to come.\nNOTE! This is a one-time programmable (unreversible) change.\nTo set enhanced attribute to general partition being created set\n <enh_attr> to 1 else set it to 0.\nTo set extended attribute to general partition\n set <ext_attr> to 1,2 else set it to 0",
Balaji T Kd78ce082015-04-29 18:12:33 -040098 NULL
99 },
Ben Gardinerd91d3692013-05-30 17:12:51 -0400100 { do_enh_area_set, -4,
Tomas Melin766a3cd2016-08-29 11:59:42 -0400101 "enh_area set", "<-y|-n|-c> " "<start KiB> " "<length KiB> " "<device>\n"
102 "Enable the enhanced user area for the <device>.\nDry-run only unless -y or -c is passed.\nUse -c if more partitioning settings are still to come.\nNOTE! This is a one-time programmable (unreversible) change.",
Ben Gardinerd91d3692013-05-30 17:12:51 -0400103 NULL
104 },
Ben Gardiner196d0d22013-09-19 11:14:29 -0400105 { do_write_reliability_set, -2,
Tomas Melin766a3cd2016-08-29 11:59:42 -0400106 "write_reliability set", "<-y|-n|-c> " "<partition> " "<device>\n"
107 "Enable write reliability per partition for the <device>.\nDry-run only unless -y or -c is passed.\nUse -c if more partitioning settings are still to come.\nNOTE! This is a one-time programmable (unreversible) change.",
Ben Gardiner196d0d22013-09-19 11:14:29 -0400108 NULL
109 },
Ben Gardiner27c357d2013-05-30 17:12:47 -0400110 { do_status_get, -1,
111 "status get", "<device>\n"
112 "Print the response to STATUS_SEND (CMD13).",
113 NULL
114 },
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200115 { do_write_boot_en, -3,
116 "bootpart enable", "<boot_partition> " "<send_ack> " "<device>\n"
Markus Schuetterlefbc0e6c2016-03-19 08:42:41 +0100117 "Enable the boot partition for the <device>.\nDisable the boot partition for the <device> if <boot_partition> is set to 0.\nTo receive acknowledgment of boot from the card set <send_ack>\nto 1, else set it to 0.",
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200118 NULL
119 },
Al Cooper794314c2015-05-01 08:24:37 -0400120 { do_boot_bus_conditions_set, -4,
121 "bootbus set", "<boot_mode> " "<reset_boot_bus_conditions> " "<boot_bus_width> " "<device>\n"
122 "Set Boot Bus Conditions.\n"
123 "<boot_mode> must be \"single_backward|single_hs|dual\"\n"
124 "<reset_boot_bus_conditions> must be \"x1|retain\"\n"
125 "<boot_bus_width> must be \"x1|x4|x8\"",
126 NULL
127 },
Jaehoon Chung86496512012-09-21 10:08:05 +0000128 { do_write_bkops_en, -1,
129 "bkops enable", "<device>\n"
130 "Enable the eMMC BKOPS feature on <device>.\nNOTE! This is a one-time programmable (unreversible) change.",
131 NULL
132 },
Chris Ballf74dfe22012-10-19 16:49:55 -0400133 { do_hwreset_en, -1,
134 "hwreset enable", "<device>\n"
135 "Permanently enable the eMMC H/W Reset feature on <device>.\nNOTE! This is a one-time programmable (unreversible) change.",
136 NULL
137 },
138 { do_hwreset_dis, -1,
139 "hwreset disable", "<device>\n"
140 "Permanently disable the eMMC H/W Reset feature on <device>.\nNOTE! This is a one-time programmable (unreversible) change.",
141 NULL
142 },
Yaniv Gardi21bb4732013-05-26 13:25:33 -0400143 { do_sanitize, -1,
144 "sanitize", "<device>\n"
145 "Send Sanitize command to the <device>.\nThis will delete the unmapped memory region of the device.",
146 NULL
147 },
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900148 { do_rpmb_write_key, -1,
149 "rpmb write-key", "<rpmb device> <key file>\n"
Chris Balld186ab52014-08-12 10:44:52 -0400150 "Program authentication key which is 32 bytes length and stored\n"
151 "in the specified file. Also you can specify '-' instead of\n"
152 "key file path to read the key from stdin.\n"
153 "NOTE! This is a one-time programmable (unreversible) change.\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900154 "Example:\n"
Chris Balld186ab52014-08-12 10:44:52 -0400155 " $ echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH | \\\n"
156 " mmc rpmb write-key /dev/mmcblk0rpmb -",
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900157 NULL
158 },
159 { do_rpmb_read_counter, -1,
160 "rpmb read-counter", "<rpmb device>\n"
161 "Counter value for the <rpmb device> will be read to stdout.",
162 NULL
163 },
164 { do_rpmb_read_block, -1,
165 "rpmb read-block", "<rpmb device> <address> <blocks count> <output file> [key file]\n"
Chris Balld186ab52014-08-12 10:44:52 -0400166 "Blocks of 256 bytes will be read from <rpmb device> to output\n"
167 "file or stdout if '-' is specified. If key is specified - read\n"
168 "data will be verified. Instead of regular path you can specify\n"
169 "'-' to read key from stdin.\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900170 "Example:\n"
Chris Balld186ab52014-08-12 10:44:52 -0400171 " $ echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH | \\\n"
172 " mmc rpmb read-block /dev/mmcblk0rpmb 0x02 2 /tmp/block -\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900173 "or read two blocks without verification\n"
174 " $ mmc rpmb read-block /dev/mmcblk0rpmb 0x02 2 /tmp/block",
175 NULL
176 },
177 { do_rpmb_write_block, -1,
178 "rpmb write-block", "<rpmb device> <address> <256 byte data file> <key file>\n"
Chris Balld186ab52014-08-12 10:44:52 -0400179 "Block of 256 bytes will be written from data file to\n"
180 "<rpmb device>. Also you can specify '-' instead of key\n"
181 "file path or data file to read the data from stdin.\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900182 "Example:\n"
Chris Balld186ab52014-08-12 10:44:52 -0400183 " $ (awk 'BEGIN {while (c++<256) printf \"a\"}' | \\\n"
184 " echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH) | \\\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900185 " mmc rpmb write-block /dev/mmcblk0rpmb 0x02 - -",
186 NULL
187 },
Al Cooper786418c2015-04-29 18:12:35 -0400188 { do_cache_en, -1,
189 "cache enable", "<device>\n"
190 "Enable the eMMC cache feature on <device>.\n"
191 "NOTE! The cache is an optional feature on devices >= eMMC4.5.",
192 NULL
193 },
194 { do_cache_dis, -1,
195 "cache disable", "<device>\n"
196 "Disable the eMMC cache feature on <device>.\n"
197 "NOTE! The cache is an optional feature on devices >= eMMC4.5.",
198 NULL
199 },
Sebastian Rasmussen7e00a5a2016-02-23 13:37:28 +0800200 { do_read_csd, -1,
201 "csd read", "<device path>\n"
202 "Print CSD data from <device path>.\n"
203 "The device path should specify the csd file directory.",
204 NULL
205 },
206 { do_read_cid, -1,
207 "cid read", "<device path>\n"
208 "Print CID data from <device path>.\n"
209 "The device path should specify the cid file directory.",
210 NULL
211 },
212 { do_read_scr, -1,
213 "scr read", "<device path>\n"
214 "Print SCR data from <device path>.\n"
215 "The device path should specify the scr file directory.",
216 NULL
217 },
Julius Wernerbcc3e2e2016-04-21 16:53:02 -0700218 { do_blockprotect_enable, -2, "blockprotect enable",
219 "[-p|-r] <device> <sector>\n"
220 "Enable block protection for a given sector. Will write protect all\n"
221 "sectors within the target sector's write protect block. Write protect\n"
222 "block size is a multiple of erase block size and device dependent.\n"
223 "Run mmc blockprotect info to query write protect block size.\n"
224 " -p Protect block permanently. WARNING: THIS IS IRREVERSIBLE!\n"
225 " -r Protect block until next power-on.",
226 NULL
227 },
228 { do_blockprotect_disable, -2, "blockprotect disable",
229 "<device> <sector>\n"
230 "Disable block protection for a given sector. Will disable protection\n"
231 "for all sectors within the target sector's write protect block.\n"
232 "Cannot disable permanent or power-on write protection.",
233 NULL
234 },
235 { do_blockprotect_read, -2, "blockprotect read", "<device> <sector>\n"
236 "Query the current block protection status of a given sector.",
237 NULL
238 },
239 { do_blockprotect_info, -1, "blockprotect info", "<device>\n"
240 "Query information about device's block protect capabilities.",
241 NULL
242 },
Avi Shchislowskidc7ab962016-03-08 14:22:41 -0500243 { do_emmc50_ffu, -2,
244 "old_ffu", "[-k hack_type[:hack_value]] <image name> <device>\n"
245 "run eMMC 5.0 Field firmware update.\n"
246 "Device specific hacks can be specificied.",
247 NULL
248 },
249 { do_ffu, -2,
250 "ffu", "<image name> <device>\n"
251 "Run Field Firmware Update with <image name> on <device>.\n",
252 NULL
253 },
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500254 { 0, 0, 0, 0 }
255};
256
257static char *get_prgname(char *programname)
258{
259 char *np;
260 np = strrchr(programname,'/');
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800261 if (!np)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500262 np = programname;
263 else
264 np++;
265
266 return np;
267}
268
269static void print_help(char *programname, struct Command *cmd, int helptype)
270{
271 char *pc;
272
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800273 printf("\t%s %s ", programname, cmd->verb);
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500274
275 if (helptype == ADVANCED_HELP && cmd->adv_help)
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800276 for (pc = cmd->adv_help; *pc; pc++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500277 putchar(*pc);
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800278 if (*pc == '\n')
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500279 printf("\t\t");
280 }
281 else
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800282 for (pc = cmd->help; *pc; pc++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500283 putchar(*pc);
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800284 if (*pc == '\n')
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500285 printf("\t\t");
286 }
287
288 putchar('\n');
289}
290
291static void help(char *np)
292{
293 struct Command *cp;
294
295 printf("Usage:\n");
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800296 for (cp = commands; cp->verb; cp++)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500297 print_help(np, cp, BASIC_HELP);
298
299 printf("\n\t%s help|--help|-h\n\t\tShow the help.\n",np);
300 printf("\n\t%s <cmd> --help\n\t\tShow detailed help for a command or subset of commands.\n",np);
301 printf("\n%s\n", MMC_VERSION);
302}
303
304static int split_command(char *cmd, char ***commands)
305{
306 int c, l;
307 char *p, *s;
308
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800309 for (*commands = 0, l = c = 0, p = s = cmd ; ; p++, l++) {
310 if (*p && *p != ' ')
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500311 continue;
312
313 /* c + 2 so that we have room for the null */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800314 (*commands) = realloc((*commands), sizeof(char *)*(c + 2));
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500315 (*commands)[c] = strndup(s, l);
316 c++;
317 l = 0;
318 s = p+1;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800319 if (!*p) break;
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500320 }
321
322 (*commands)[c] = 0;
323 return c;
324}
325
326/*
327 This function checks if the passed command is ambiguous
328*/
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800329static int check_ambiguity(struct Command *cmd, char **argv) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500330 int i;
331 struct Command *cp;
332 /* check for ambiguity */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800333 for (i = 0 ; i < cmd->ncmds ; i++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500334 int match;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800335 for (match = 0, cp = commands; cp->verb; cp++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500336 int j, skip;
337 char *s1, *s2;
338
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800339 if (cp->ncmds < i)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500340 continue;
341
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800342 for (skip = 0, j = 0 ; j < i ; j++)
343 if (strcmp(cmd->cmds[j], cp->cmds[j])) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500344 skip=1;
345 break;
346 }
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800347 if (skip)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500348 continue;
349
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800350 if (!strcmp(cmd->cmds[i], cp->cmds[i]))
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500351 continue;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800352 for (s2 = cp->cmds[i], s1 = argv[i+1];
353 *s1 == *s2 && *s1; s1++, s2++) ;
354 if (!*s1)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500355 match++;
356 }
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800357 if (match) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500358 int j;
359 fprintf(stderr, "ERROR: in command '");
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800360 for (j = 0 ; j <= i ; j++)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500361 fprintf(stderr, "%s%s",j?" ":"", argv[j+1]);
362 fprintf(stderr, "', '%s' is ambiguous\n",argv[j]);
363 return -2;
364 }
365 }
366 return 0;
367}
368
369/*
370 * This function, compacts the program name and the command in the first
371 * element of the '*av' array
372 */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800373static int prepare_args(int *ac, char ***av, char *prgname,
374 struct Command *cmd) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500375
376 char **ret;
377 int i;
378 char *newname;
379
380 ret = (char **)malloc(sizeof(char*)*(*ac+1));
381 newname = (char*)malloc(strlen(prgname)+strlen(cmd->verb)+2);
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800382 if (!ret || !newname) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500383 free(ret);
384 free(newname);
385 return -1;
386 }
387
388 ret[0] = newname;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800389 for (i=0; i < *ac ; i++)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500390 ret[i+1] = (*av)[i];
391
392 strcpy(newname, prgname);
393 strcat(newname, " ");
394 strcat(newname, cmd->verb);
395
396 (*ac)++;
397 *av = ret;
398
399 return 0;
400
401}
402
403/*
404 This function performs the following jobs:
405 - show the help if '--help' or 'help' or '-h' are passed
406 - verify that a command is not ambiguous, otherwise show which
407 part of the command is ambiguous
408 - if after a (even partial) command there is '--help' show detailed help
409 for all the matching commands
410 - if the command doesn't match show an error
411 - finally, if a command matches, they return which command matched and
412 the arguments
413
414 The function return 0 in case of help is requested; <0 in case
415 of uncorrect command; >0 in case of matching commands
416 argc, argv are the arg-counter and arg-vector (input)
417 *nargs_ is the number of the arguments after the command (output)
418 **cmd_ is the invoked command (output)
419 ***args_ are the arguments after the command
420
421*/
422static int parse_args(int argc, char **argv,
423 CommandFunction *func_,
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800424 int *nargs_, char **cmd_, char ***args_)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500425{
426 struct Command *cp;
427 struct Command *matchcmd=0;
428 char *prgname = get_prgname(argv[0]);
429 int i=0, helprequested=0;
430
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800431 if (argc < 2 || !strcmp(argv[1], "help") ||
432 !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500433 help(prgname);
434 return 0;
435 }
436
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800437 for (cp = commands; cp->verb; cp++)
438 if (!cp->ncmds)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500439 cp->ncmds = split_command(cp->verb, &(cp->cmds));
440
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800441 for (cp = commands; cp->verb; cp++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500442 int match;
443
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800444 if (argc-1 < cp->ncmds)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500445 continue;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800446 for (match = 1, i = 0 ; i < cp->ncmds ; i++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500447 char *s1, *s2;
448 s1 = cp->cmds[i];
449 s2 = argv[i+1];
450
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800451 for (s2 = cp->cmds[i], s1 = argv[i+1];
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500452 *s1 == *s2 && *s1;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800453 s1++, s2++) ;
454 if (*s1) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500455 match=0;
456 break;
457 }
458 }
459
460 /* If you understand why this code works ...
461 you are a genious !! */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800462 if (argc>i+1 && !strcmp(argv[i+1],"--help")) {
463 if (!helprequested)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500464 printf("Usage:\n");
465 print_help(prgname, cp, ADVANCED_HELP);
466 helprequested=1;
467 continue;
468 }
469
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800470 if (!match)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500471 continue;
472
473 matchcmd = cp;
474 *nargs_ = argc-matchcmd->ncmds-1;
475 *cmd_ = matchcmd->verb;
476 *args_ = argv+matchcmd->ncmds+1;
477 *func_ = cp->func;
478
479 break;
480 }
481
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800482 if (helprequested) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500483 printf("\n%s\n", MMC_VERSION);
484 return 0;
485 }
486
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800487 if (!matchcmd) {
488 fprintf(stderr, "ERROR: unknown command '%s'\n",argv[1]);
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500489 help(prgname);
490 return -1;
491 }
492
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800493 if (check_ambiguity(matchcmd, argv))
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500494 return -2;
495
496 /* check the number of argument */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800497 if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500498 fprintf(stderr, "ERROR: '%s' requires minimum %d arg(s)\n",
499 matchcmd->verb, -matchcmd->nargs);
500 return -2;
501 }
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800502 if (matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500503 fprintf(stderr, "ERROR: '%s' requires %d arg(s)\n",
504 matchcmd->verb, matchcmd->nargs);
505 return -2;
506 }
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800507 if (prepare_args(nargs_, args_, prgname, matchcmd)) {
508 fprintf(stderr, "ERROR: not enough memory\\n");
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500509 return -20;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800510 }
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500511
512
513 return 1;
514}
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800515int main(int ac, char **av)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500516{
517 char *cmd=0, **args=0;
518 int nargs=0, r;
519 CommandFunction func=0;
520
521 r = parse_args(ac, av, &func, &nargs, &cmd, &args);
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800522 if (r <= 0) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500523 /* error or no command to parse*/
524 exit(-r);
525 }
526
527 exit(func(nargs, args));
528}
529