blob: 85e0dbd87c5447fc37d12aacd6a2c96814dd3d9a [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,
Julius Werner7ddcb232020-03-13 14:21:48 -070072 "writeprotect boot set", "[-p] " "<device> [<number>]\n"
73 "Set the boot partition write protect status for <device>.\n"
74 "If <number> is passed (0 or 1), only protect that particular\n"
75 "eMMC boot partition, otherwise protect both. It will be\n"
76 "write-protected until the next boot.\n"
77 " -p Protect partition permanently instead. WARNING: THIS IS IRREVERSIBLE!\n",
Al Cooper1b7f5d72016-06-07 16:35:46 -040078 NULL
79 },
80 { do_writeprotect_user_set, -4,
81 "writeprotect user set", "<type>" "<start block>" "<blocks>" "<device>\n"
82#ifdef DANGEROUS_COMMANDS_ENABLED
83 "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.",
84#else
85 "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.",
86#endif /* DANGEROUS_COMMANDS_ENABLED */
87 NULL
88 },
89 { do_writeprotect_user_get, -1,
90 "writeprotect user get", "<device>\n"
91 "Print the user areas write protect configuration for <device>.",
Chris Ball45541d52012-02-12 11:49:53 -050092 NULL
93 },
Saugata Dasb7e25992012-05-17 09:26:34 -040094 { do_disable_512B_emulation, -1,
95 "disable 512B emulation", "<device>\n"
Chris Ball65997802012-09-21 18:19:25 +080096 "Set the eMMC data sector size to 4KB by disabling emulation on\n<device>.",
Saugata Dasb7e25992012-05-17 09:26:34 -040097 NULL
98 },
Balaji T Kd78ce082015-04-29 18:12:33 -040099 { do_create_gp_partition, -6,
Tomas Melin766a3cd2016-08-29 11:59:42 -0400100 "gp create", "<-y|-n|-c> " "<length KiB> " "<partition> " "<enh_attr> " "<ext_attr> " "<device>\n"
101 "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 -0400102 NULL
103 },
Ben Gardinerd91d3692013-05-30 17:12:51 -0400104 { do_enh_area_set, -4,
Tomas Melin766a3cd2016-08-29 11:59:42 -0400105 "enh_area set", "<-y|-n|-c> " "<start KiB> " "<length KiB> " "<device>\n"
106 "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 -0400107 NULL
108 },
Ben Gardiner196d0d22013-09-19 11:14:29 -0400109 { do_write_reliability_set, -2,
Tomas Melin766a3cd2016-08-29 11:59:42 -0400110 "write_reliability set", "<-y|-n|-c> " "<partition> " "<device>\n"
111 "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 -0400112 NULL
113 },
Ben Gardiner27c357d2013-05-30 17:12:47 -0400114 { do_status_get, -1,
115 "status get", "<device>\n"
116 "Print the response to STATUS_SEND (CMD13).",
117 NULL
118 },
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200119 { do_write_boot_en, -3,
120 "bootpart enable", "<boot_partition> " "<send_ack> " "<device>\n"
Markus Schuetterlefbc0e6c2016-03-19 08:42:41 +0100121 "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 +0200122 NULL
123 },
Al Cooper794314c2015-05-01 08:24:37 -0400124 { do_boot_bus_conditions_set, -4,
125 "bootbus set", "<boot_mode> " "<reset_boot_bus_conditions> " "<boot_bus_width> " "<device>\n"
126 "Set Boot Bus Conditions.\n"
127 "<boot_mode> must be \"single_backward|single_hs|dual\"\n"
128 "<reset_boot_bus_conditions> must be \"x1|retain\"\n"
129 "<boot_bus_width> must be \"x1|x4|x8\"",
130 NULL
131 },
Jaehoon Chung86496512012-09-21 10:08:05 +0000132 { do_write_bkops_en, -1,
133 "bkops enable", "<device>\n"
134 "Enable the eMMC BKOPS feature on <device>.\nNOTE! This is a one-time programmable (unreversible) change.",
135 NULL
136 },
Chris Ballf74dfe22012-10-19 16:49:55 -0400137 { do_hwreset_en, -1,
138 "hwreset enable", "<device>\n"
139 "Permanently enable the eMMC H/W Reset feature on <device>.\nNOTE! This is a one-time programmable (unreversible) change.",
140 NULL
141 },
142 { do_hwreset_dis, -1,
143 "hwreset disable", "<device>\n"
144 "Permanently disable the eMMC H/W Reset feature on <device>.\nNOTE! This is a one-time programmable (unreversible) change.",
145 NULL
146 },
Yaniv Gardi21bb4732013-05-26 13:25:33 -0400147 { do_sanitize, -1,
148 "sanitize", "<device>\n"
149 "Send Sanitize command to the <device>.\nThis will delete the unmapped memory region of the device.",
150 NULL
151 },
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900152 { do_rpmb_write_key, -1,
153 "rpmb write-key", "<rpmb device> <key file>\n"
Chris Balld186ab52014-08-12 10:44:52 -0400154 "Program authentication key which is 32 bytes length and stored\n"
155 "in the specified file. Also you can specify '-' instead of\n"
156 "key file path to read the key from stdin.\n"
157 "NOTE! This is a one-time programmable (unreversible) change.\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900158 "Example:\n"
Chris Balld186ab52014-08-12 10:44:52 -0400159 " $ echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH | \\\n"
160 " mmc rpmb write-key /dev/mmcblk0rpmb -",
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900161 NULL
162 },
163 { do_rpmb_read_counter, -1,
164 "rpmb read-counter", "<rpmb device>\n"
165 "Counter value for the <rpmb device> will be read to stdout.",
166 NULL
167 },
168 { do_rpmb_read_block, -1,
169 "rpmb read-block", "<rpmb device> <address> <blocks count> <output file> [key file]\n"
Chris Balld186ab52014-08-12 10:44:52 -0400170 "Blocks of 256 bytes will be read from <rpmb device> to output\n"
171 "file or stdout if '-' is specified. If key is specified - read\n"
172 "data will be verified. Instead of regular path you can specify\n"
173 "'-' to read key from stdin.\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900174 "Example:\n"
Chris Balld186ab52014-08-12 10:44:52 -0400175 " $ echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH | \\\n"
176 " mmc rpmb read-block /dev/mmcblk0rpmb 0x02 2 /tmp/block -\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900177 "or read two blocks without verification\n"
178 " $ mmc rpmb read-block /dev/mmcblk0rpmb 0x02 2 /tmp/block",
179 NULL
180 },
181 { do_rpmb_write_block, -1,
182 "rpmb write-block", "<rpmb device> <address> <256 byte data file> <key file>\n"
Chris Balld186ab52014-08-12 10:44:52 -0400183 "Block of 256 bytes will be written from data file to\n"
184 "<rpmb device>. Also you can specify '-' instead of key\n"
185 "file path or data file to read the data from stdin.\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900186 "Example:\n"
Chris Balld186ab52014-08-12 10:44:52 -0400187 " $ (awk 'BEGIN {while (c++<256) printf \"a\"}' | \\\n"
188 " echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH) | \\\n"
Roman Peniaev023cc7c2014-08-12 23:25:45 +0900189 " mmc rpmb write-block /dev/mmcblk0rpmb 0x02 - -",
190 NULL
191 },
Al Cooper786418c2015-04-29 18:12:35 -0400192 { do_cache_en, -1,
193 "cache enable", "<device>\n"
194 "Enable the eMMC cache feature on <device>.\n"
195 "NOTE! The cache is an optional feature on devices >= eMMC4.5.",
196 NULL
197 },
198 { do_cache_dis, -1,
199 "cache disable", "<device>\n"
200 "Disable the eMMC cache feature on <device>.\n"
201 "NOTE! The cache is an optional feature on devices >= eMMC4.5.",
202 NULL
203 },
Sebastian Rasmussen7e00a5a2016-02-23 13:37:28 +0800204 { do_read_csd, -1,
205 "csd read", "<device path>\n"
206 "Print CSD data from <device path>.\n"
207 "The device path should specify the csd file directory.",
208 NULL
209 },
210 { do_read_cid, -1,
211 "cid read", "<device path>\n"
212 "Print CID data from <device path>.\n"
213 "The device path should specify the cid file directory.",
214 NULL
215 },
216 { do_read_scr, -1,
217 "scr read", "<device path>\n"
218 "Print SCR data from <device path>.\n"
219 "The device path should specify the scr file directory.",
220 NULL
221 },
Julius Wernerbcc3e2e2016-04-21 16:53:02 -0700222 { do_blockprotect_enable, -2, "blockprotect enable",
223 "[-p|-r] <device> <sector>\n"
224 "Enable block protection for a given sector. Will write protect all\n"
225 "sectors within the target sector's write protect block. Write protect\n"
226 "block size is a multiple of erase block size and device dependent.\n"
227 "Run mmc blockprotect info to query write protect block size.\n"
228 " -p Protect block permanently. WARNING: THIS IS IRREVERSIBLE!\n"
229 " -r Protect block until next power-on.",
230 NULL
231 },
232 { do_blockprotect_disable, -2, "blockprotect disable",
233 "<device> <sector>\n"
234 "Disable block protection for a given sector. Will disable protection\n"
235 "for all sectors within the target sector's write protect block.\n"
236 "Cannot disable permanent or power-on write protection.",
237 NULL
238 },
239 { do_blockprotect_read, -2, "blockprotect read", "<device> <sector>\n"
240 "Query the current block protection status of a given sector.",
241 NULL
242 },
243 { do_blockprotect_info, -1, "blockprotect info", "<device>\n"
244 "Query information about device's block protect capabilities.",
245 NULL
246 },
Avi Shchislowskidc7ab962016-03-08 14:22:41 -0500247 { do_emmc50_ffu, -2,
248 "old_ffu", "[-k hack_type[:hack_value]] <image name> <device>\n"
249 "run eMMC 5.0 Field firmware update.\n"
250 "Device specific hacks can be specificied.",
251 NULL
252 },
253 { do_ffu, -2,
254 "ffu", "<image name> <device>\n"
255 "Run Field Firmware Update with <image name> on <device>.\n",
256 NULL
257 },
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500258 { 0, 0, 0, 0 }
259};
260
261static char *get_prgname(char *programname)
262{
263 char *np;
264 np = strrchr(programname,'/');
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800265 if (!np)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500266 np = programname;
267 else
268 np++;
269
270 return np;
271}
272
273static void print_help(char *programname, struct Command *cmd, int helptype)
274{
275 char *pc;
276
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800277 printf("\t%s %s ", programname, cmd->verb);
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500278
279 if (helptype == ADVANCED_HELP && cmd->adv_help)
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800280 for (pc = cmd->adv_help; *pc; pc++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500281 putchar(*pc);
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800282 if (*pc == '\n')
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500283 printf("\t\t");
284 }
285 else
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800286 for (pc = cmd->help; *pc; pc++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500287 putchar(*pc);
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800288 if (*pc == '\n')
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500289 printf("\t\t");
290 }
291
292 putchar('\n');
293}
294
295static void help(char *np)
296{
297 struct Command *cp;
298
299 printf("Usage:\n");
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800300 for (cp = commands; cp->verb; cp++)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500301 print_help(np, cp, BASIC_HELP);
302
303 printf("\n\t%s help|--help|-h\n\t\tShow the help.\n",np);
304 printf("\n\t%s <cmd> --help\n\t\tShow detailed help for a command or subset of commands.\n",np);
305 printf("\n%s\n", MMC_VERSION);
306}
307
308static int split_command(char *cmd, char ***commands)
309{
310 int c, l;
311 char *p, *s;
312
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800313 for (*commands = 0, l = c = 0, p = s = cmd ; ; p++, l++) {
314 if (*p && *p != ' ')
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500315 continue;
316
317 /* c + 2 so that we have room for the null */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800318 (*commands) = realloc((*commands), sizeof(char *)*(c + 2));
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500319 (*commands)[c] = strndup(s, l);
320 c++;
321 l = 0;
322 s = p+1;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800323 if (!*p) break;
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500324 }
325
326 (*commands)[c] = 0;
327 return c;
328}
329
330/*
331 This function checks if the passed command is ambiguous
332*/
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800333static int check_ambiguity(struct Command *cmd, char **argv) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500334 int i;
335 struct Command *cp;
336 /* check for ambiguity */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800337 for (i = 0 ; i < cmd->ncmds ; i++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500338 int match;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800339 for (match = 0, cp = commands; cp->verb; cp++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500340 int j, skip;
341 char *s1, *s2;
342
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800343 if (cp->ncmds < i)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500344 continue;
345
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800346 for (skip = 0, j = 0 ; j < i ; j++)
347 if (strcmp(cmd->cmds[j], cp->cmds[j])) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500348 skip=1;
349 break;
350 }
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800351 if (skip)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500352 continue;
353
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800354 if (!strcmp(cmd->cmds[i], cp->cmds[i]))
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500355 continue;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800356 for (s2 = cp->cmds[i], s1 = argv[i+1];
357 *s1 == *s2 && *s1; s1++, s2++) ;
358 if (!*s1)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500359 match++;
360 }
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800361 if (match) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500362 int j;
363 fprintf(stderr, "ERROR: in command '");
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800364 for (j = 0 ; j <= i ; j++)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500365 fprintf(stderr, "%s%s",j?" ":"", argv[j+1]);
366 fprintf(stderr, "', '%s' is ambiguous\n",argv[j]);
367 return -2;
368 }
369 }
370 return 0;
371}
372
373/*
374 * This function, compacts the program name and the command in the first
375 * element of the '*av' array
376 */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800377static int prepare_args(int *ac, char ***av, char *prgname,
378 struct Command *cmd) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500379
380 char **ret;
381 int i;
382 char *newname;
383
384 ret = (char **)malloc(sizeof(char*)*(*ac+1));
385 newname = (char*)malloc(strlen(prgname)+strlen(cmd->verb)+2);
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800386 if (!ret || !newname) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500387 free(ret);
388 free(newname);
389 return -1;
390 }
391
392 ret[0] = newname;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800393 for (i=0; i < *ac ; i++)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500394 ret[i+1] = (*av)[i];
395
396 strcpy(newname, prgname);
397 strcat(newname, " ");
398 strcat(newname, cmd->verb);
399
400 (*ac)++;
401 *av = ret;
402
403 return 0;
404
405}
406
407/*
408 This function performs the following jobs:
409 - show the help if '--help' or 'help' or '-h' are passed
410 - verify that a command is not ambiguous, otherwise show which
411 part of the command is ambiguous
412 - if after a (even partial) command there is '--help' show detailed help
413 for all the matching commands
414 - if the command doesn't match show an error
415 - finally, if a command matches, they return which command matched and
416 the arguments
417
418 The function return 0 in case of help is requested; <0 in case
419 of uncorrect command; >0 in case of matching commands
420 argc, argv are the arg-counter and arg-vector (input)
421 *nargs_ is the number of the arguments after the command (output)
422 **cmd_ is the invoked command (output)
423 ***args_ are the arguments after the command
424
425*/
426static int parse_args(int argc, char **argv,
427 CommandFunction *func_,
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800428 int *nargs_, char **cmd_, char ***args_)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500429{
430 struct Command *cp;
431 struct Command *matchcmd=0;
432 char *prgname = get_prgname(argv[0]);
433 int i=0, helprequested=0;
434
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800435 if (argc < 2 || !strcmp(argv[1], "help") ||
436 !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500437 help(prgname);
438 return 0;
439 }
440
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800441 for (cp = commands; cp->verb; cp++)
442 if (!cp->ncmds)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500443 cp->ncmds = split_command(cp->verb, &(cp->cmds));
444
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800445 for (cp = commands; cp->verb; cp++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500446 int match;
447
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800448 if (argc-1 < cp->ncmds)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500449 continue;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800450 for (match = 1, i = 0 ; i < cp->ncmds ; i++) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500451 char *s1, *s2;
452 s1 = cp->cmds[i];
453 s2 = argv[i+1];
454
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800455 for (s2 = cp->cmds[i], s1 = argv[i+1];
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500456 *s1 == *s2 && *s1;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800457 s1++, s2++) ;
458 if (*s1) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500459 match=0;
460 break;
461 }
462 }
463
464 /* If you understand why this code works ...
465 you are a genious !! */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800466 if (argc>i+1 && !strcmp(argv[i+1],"--help")) {
467 if (!helprequested)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500468 printf("Usage:\n");
469 print_help(prgname, cp, ADVANCED_HELP);
470 helprequested=1;
471 continue;
472 }
473
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800474 if (!match)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500475 continue;
476
477 matchcmd = cp;
478 *nargs_ = argc-matchcmd->ncmds-1;
479 *cmd_ = matchcmd->verb;
480 *args_ = argv+matchcmd->ncmds+1;
481 *func_ = cp->func;
482
483 break;
484 }
485
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800486 if (helprequested) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500487 printf("\n%s\n", MMC_VERSION);
488 return 0;
489 }
490
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800491 if (!matchcmd) {
492 fprintf(stderr, "ERROR: unknown command '%s'\n",argv[1]);
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500493 help(prgname);
494 return -1;
495 }
496
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800497 if (check_ambiguity(matchcmd, argv))
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500498 return -2;
499
500 /* check the number of argument */
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800501 if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500502 fprintf(stderr, "ERROR: '%s' requires minimum %d arg(s)\n",
503 matchcmd->verb, -matchcmd->nargs);
504 return -2;
505 }
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800506 if (matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500507 fprintf(stderr, "ERROR: '%s' requires %d arg(s)\n",
508 matchcmd->verb, matchcmd->nargs);
509 return -2;
510 }
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800511 if (prepare_args(nargs_, args_, prgname, matchcmd)) {
512 fprintf(stderr, "ERROR: not enough memory\\n");
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500513 return -20;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800514 }
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500515
516
517 return 1;
518}
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800519int main(int ac, char **av)
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500520{
521 char *cmd=0, **args=0;
522 int nargs=0, r;
523 CommandFunction func=0;
524
525 r = parse_args(ac, av, &func, &nargs, &cmd, &args);
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800526 if (r <= 0) {
Goffredo Baroncelli80d26602012-02-12 11:43:14 -0500527 /* error or no command to parse*/
528 exit(-r);
529 }
530
531 exit(func(nargs, args));
532}
533