btrfs-progs: Remove unused parameter
[platform/upstream/btrfs-progs.git] / btrfs.c
diff --git a/btrfs.c b/btrfs.c
index 742dd27..2d39f2c 100644 (file)
--- a/btrfs.c
+++ b/btrfs.c
  * Boston, MA 021110-1307, USA.
  */
 
-#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <getopt.h>
 
-#include "kerncompat.h"
-#include "btrfs_cmds.h"
-#include "version.h"
-
-#define BASIC_HELP 0
-#define ADVANCED_HELP 1
-
-typedef int (*CommandFunction)(int argc, char **argv);
-
-struct Command {
-       CommandFunction func;   /* function which implements the command */
-       int     nargs;          /* if == 999, any number of arguments
-                                  if >= 0, number of arguments,
-                                  if < 0, _minimum_ number of arguments */
-       char    *verb;          /* verb */
-       char    *help;          /* help lines; from the 2nd line onward they 
-                                   are automatically indented */
-        char    *adv_help;      /* advanced help message; from the 2nd line 
-                                   onward they are automatically indented */
-
-       /* the following fields are run-time filled by the program */
-       char    **cmds;         /* array of subcommands */
-       int     ncmds;          /* number of subcommand */
-};
-
-static struct Command commands[] = {
+#include "volumes.h"
+#include "crc32c.h"
+#include "commands.h"
+#include "utils.h"
+#include "help.h"
 
-       /*
-               avoid short commands different for the case only
-       */
-       { do_clone, 2,
-         "subvolume snapshot", "<source> [<dest>/]<name>\n"
-               "Create a writable snapshot of the subvolume <source> with\n"
-               "the name <name> in the <dest> directory.",
-         NULL
-       },
-       { do_delete_subvolume, 1,
-         "subvolume delete", "<subvolume>\n"
-               "Delete the subvolume <subvolume>.",
-         NULL
-       },
-       { do_create_subvol, 1,
-         "subvolume create", "[<dest>/]<name>\n"
-               "Create a subvolume in <dest> (or the current directory if\n"
-               "not passed).",
-         NULL
-       },
-       { do_subvol_list, 1, "subvolume list", "<path>\n"
-               "List the snapshot/subvolume of a filesystem.",
-         NULL
-       },
-       { do_set_default_subvol, 2,
-         "subvolume set-default", "<id> <path>\n"
-               "Set the subvolume of the filesystem <path> which will be mounted\n"
-               "as default.",
-         NULL
-       },
-       { do_find_newer, 2, "subvolume find-new", "<path> <last_gen>\n"
-               "List the recently modified files in a filesystem.",
-         NULL
-       },
-       { do_defrag, -1,
-         "filesystem defragment", "[-vf] [-c[zlib,lzo]] [-s start] [-l len] [-t size] <file>|<dir> [<file>|<dir>...]\n"
-               "Defragment a file or a directory.",
-               "[-vcf] [-s start] [-l len] [-t size] <file>|<dir> [<file>|<dir>...]\n"
-               "Defragment file data or directory metadata.\n"
-               "-v         be verbose\n"
-               "-c         compress the file while defragmenting\n"
-               "-f         flush data to disk immediately after defragmenting\n"
-               "-s start   defragment only from byte onward\n"
-               "-l len     defragment only up to len bytes\n"
-               "-t size    minimal size of file to be considered for defragmenting\n"
-       },
-       { do_fssync, 1,
-         "filesystem sync", "<path>\n"
-               "Force a sync on the filesystem <path>.",
-         NULL
-       },
-       { do_resize, 2,
-         "filesystem resize", "[+/-]<newsize>[gkm]|max <filesystem>\n"
-               "Resize the file system. If 'max' is passed, the filesystem\n"
-               "will occupe all available space on the device.",
-         NULL
-       },
-       { do_show_filesystem, 999,
-         "filesystem show", "[<device>|<uuid>|<label>]\n"
-               "Show the info of a btrfs filesystem. If no argument\n"
-               "is passed, info of all the btrfs filesystem are shown.",
-         NULL
-       },
-       { do_df_filesystem, 1,
-         "filesystem df", "<path>\n"
-               "Show space usage information for a mount point.",
-         NULL
-       },
-       { do_balance, 1,
-         "filesystem balance", "<path>\n"
-               "Balance the chunks across the device.",
-         NULL
-       },
-       { do_scan, 999, 
-         "device scan", "[<device>...]\n"
-               "Scan all device for or the passed device for a btrfs\n"
-               "filesystem.",
-         NULL
-       },
-       { do_add_volume, -2,
-         "device add", "<device> [<device>...] <path>\n"
-               "Add a device to a filesystem.",
-         NULL
-       },
-       { do_remove_volume, -2,
-         "device delete", "<device> [<device>...] <path>\n"
-               "Remove a device from a filesystem.",
-         NULL
-       },
-       { 0, 0, 0, 0 }
+static const char * const btrfs_cmd_group_usage[] = {
+       "btrfs [--help] [--version] <group> [<group>...] <command> [<args>]",
+       NULL
 };
 
-static char *get_prgname(char *programname)
-{
-       char    *np;
-       np = strrchr(programname,'/');
-       if(!np)
-               np = programname;
-       else
-               np++;
+static const char btrfs_cmd_group_info[] =
+       "Use --help as an argument for information on a specific group or command.";
 
-       return np;
+static inline const char *skip_prefix(const char *str, const char *prefix)
+{
+       size_t len = strlen(prefix);
+       return strncmp(str, prefix, len) ? NULL : str + len;
 }
 
-static void print_help(char *programname, struct Command *cmd, int helptype)
+static int parse_one_token(const char *arg, const struct cmd_group *grp,
+                          const struct cmd_struct **cmd_ret)
 {
-       char    *pc;
-
-       printf("\t%s %s ", programname, cmd->verb );
-
-       if (helptype == ADVANCED_HELP && cmd->adv_help)
-               for(pc = cmd->adv_help; *pc; pc++){
-                       putchar(*pc);
-                       if(*pc == '\n')
-                               printf("\t\t");
-               }
-       else
-               for(pc = cmd->help; *pc; pc++){
-                       putchar(*pc);
-                       if(*pc == '\n')
-                               printf("\t\t");
+       const struct cmd_struct *cmd = grp->commands;
+       const struct cmd_struct *abbrev_cmd = NULL, *ambiguous_cmd = NULL;
+
+       for (; cmd->token; cmd++) {
+               const char *rest;
+
+               rest = skip_prefix(arg, cmd->token);
+               if (!rest) {
+                       if (!prefixcmp(cmd->token, arg)) {
+                               if (abbrev_cmd) {
+                                       /*
+                                        * If this is abbreviated, it is
+                                        * ambiguous. So when there is no
+                                        * exact match later, we need to
+                                        * error out.
+                                        */
+                                       ambiguous_cmd = abbrev_cmd;
+                               }
+                               abbrev_cmd = cmd;
+                       }
+                       continue;
                }
+               if (*rest)
+                       continue;
 
-       putchar('\n');
-}
+               *cmd_ret = cmd;
+               return 0;
+       }
 
-static void help(char *np)
-{
-       struct Command *cp;
+       if (ambiguous_cmd)
+               return -2;
 
-       printf("Usage:\n");
-       for( cp = commands; cp->verb; cp++ )
-               print_help(np, cp, BASIC_HELP);
+       if (abbrev_cmd) {
+               *cmd_ret = abbrev_cmd;
+               return 0;
+       }
 
-       printf("\n\t%s help|--help|-h\n\t\tShow the help.\n",np);
-       printf("\n\t%s <cmd> --help\n\t\tShow detailed help for a command or\n\t\t"
-              "subset of commands.\n",np);
-       printf("\n%s\n", BTRFS_BUILD_VERSION);
+       return -1;
 }
 
-static int split_command(char *cmd, char ***commands)
+static const struct cmd_struct *
+parse_command_token(const char *arg, const struct cmd_group *grp)
 {
-       int     c, l;
-       char    *p, *s;
+       const struct cmd_struct *cmd = NULL;
 
-       for( *commands = 0, l = c = 0, p = s = cmd ; ; p++, l++ ){
-               if ( *p && *p != ' ' )
-                       continue;
-
-               /* c + 2 so that we have room for the null */
-               (*commands) = realloc( (*commands), sizeof(char *)*(c + 2));
-               (*commands)[c] = strndup(s, l);
-               c++;
-               l = 0;
-               s = p+1;
-               if( !*p ) break;
+       switch(parse_one_token(arg, grp, &cmd)) {
+       case -1:
+               help_unknown_token(arg, grp);
+       case -2:
+               help_ambiguous_token(arg, grp);
        }
 
-       (*commands)[c] = 0;
-       return c;
+       return cmd;
 }
 
-/*
-       This function checks if the passed command is ambiguous
-*/
-static int check_ambiguity(struct Command *cmd, char **argv){
-       int             i;
-       struct Command  *cp;
-       /* check for ambiguity */
-       for( i = 0 ; i < cmd->ncmds ; i++ ){
-               int match;
-               for( match = 0, cp = commands; cp->verb; cp++ ){
-                       int     j, skip;
-                       char    *s1, *s2;
-
-                       if( cp->ncmds < i )
-                               continue;
-
-                       for( skip = 0, j = 0 ; j < i ; j++ )
-                               if( strcmp(cmd->cmds[j], cp->cmds[j])){
-                                       skip=1;
-                                       break;
-                               }
-                       if(skip)
-                               continue;
-
-                       if( !strcmp(cmd->cmds[i], cp->cmds[i]))
-                               continue;
-                       for(s2 = cp->cmds[i], s1 = argv[i+1];
-                               *s1 == *s2 && *s1; s1++, s2++ ) ;
-                       if( !*s1 )
-                               match++;
-               }
-               if(match){
-                       int j;
-                       fprintf(stderr, "ERROR: in command '");
-                       for( j = 0 ; j <= i ; j++ )
-                               fprintf(stderr, "%s%s",j?" ":"", argv[j+1]);
-                       fprintf(stderr, "', '%s' is ambiguous\n",argv[j]);
-                       return -2;
+static void handle_help_options_next_level(const struct cmd_struct *cmd,
+               int argc, char **argv)
+{
+       if (argc < 2)
+               return;
+
+       if (!strcmp(argv[1], "--help")) {
+               if (cmd->next) {
+                       argc--;
+                       argv++;
+                       help_command_group(cmd->next, argc, argv);
+               } else {
+                       usage_command(cmd, 1, 0);
                }
+
+               exit(0);
        }
-       return 0;
 }
 
-/*
- * This function, compacts the program name and the command in the first
- * element of the '*av' array
- */
-static int prepare_args(int *ac, char ***av, char *prgname, struct Command *cmd ){
-
-       char    **ret;
-       int     i;
-       char    *newname;
-
-       ret = (char **)malloc(sizeof(char*)*(*ac+1));
-       newname = (char*)malloc(strlen(prgname)+strlen(cmd->verb)+2);
-       if( !ret || !newname ){
-               free(ret);
-               free(newname);
-               return -1;
-       }
+int handle_command_group(const struct cmd_group *grp, int argc,
+                        char **argv)
 
-       ret[0] = newname;
-       for(i=0; i < *ac ; i++ )
-               ret[i+1] = (*av)[i];
+{
+       const struct cmd_struct *cmd;
 
-       strcpy(newname, prgname);
-       strcat(newname, " ");
-       strcat(newname, cmd->verb);
+       argc--;
+       argv++;
+       if (argc < 1) {
+               usage_command_group(grp, 0, 0);
+               exit(1);
+       }
 
-       (*ac)++;
-       *av = ret;
+       cmd = parse_command_token(argv[0], grp);
 
-       return 0;
+       handle_help_options_next_level(cmd, argc, argv);
 
+       fixup_argv0(argv, cmd->token);
+       return cmd->fn(argc, argv);
 }
 
+static const struct cmd_group btrfs_cmd_group;
 
+static const char * const cmd_help_usage[] = {
+       "btrfs help [--full]",
+       "Display help information",
+       "",
+       "--full     display detailed help on every command",
+       NULL
+};
 
-/*
-       This function performs the following jobs:
-       - show the help if '--help' or 'help' or '-h' are passed
-       - verify that a command is not ambiguous, otherwise show which
-         part of the command is ambiguous
-       - if after a (even partial) command there is '--help' show detailed help
-         for all the matching commands
-       - if the command doesn't match show an error
-       - finally, if a command matches, they return which command matched and
-         the arguments
-
-       The function return 0 in case of help is requested; <0 in case
-       of uncorrect command; >0 in case of matching commands
-       argc, argv are the arg-counter and arg-vector (input)
-       *nargs_ is the number of the arguments after the command (output)
-       **cmd_  is the invoked command (output)
-       ***args_ are the arguments after the command
-
-*/
-static int parse_args(int argc, char **argv,
-                     CommandFunction *func_,
-                     int *nargs_, char **cmd_, char ***args_ )
+static int cmd_help(int argc, char **argv)
 {
-       struct Command  *cp;
-       struct Command  *matchcmd=0;
-       char            *prgname = get_prgname(argv[0]);
-       int             i=0, helprequested=0;
-
-       if( argc < 2 || !strcmp(argv[1], "help") ||
-               !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){
-               help(prgname);
-               return 0;
-       }
+       help_command_group(&btrfs_cmd_group, argc, argv);
+       return 0;
+}
 
-       for( cp = commands; cp->verb; cp++ )
-               if( !cp->ncmds)
-                       cp->ncmds = split_command(cp->verb, &(cp->cmds));
+static const char * const cmd_version_usage[] = {
+       "btrfs version",
+       "Display btrfs-progs version",
+       NULL
+};
 
-       for( cp = commands; cp->verb; cp++ ){
-               int     match;
+static int cmd_version(int argc, char **argv)
+{
+       printf("%s\n", PACKAGE_STRING);
+       return 0;
+}
 
-               if( argc-1 < cp->ncmds )
-                       continue;
-               for( match = 1, i = 0 ; i < cp->ncmds ; i++ ){
-                       char    *s1, *s2;
-                       s1 = cp->cmds[i];
-                       s2 = argv[i+1];
-
-                       for(s2 = cp->cmds[i], s1 = argv[i+1];
-                               *s1 == *s2 && *s1;
-                               s1++, s2++ ) ;
-                       if( *s1 ){
-                               match=0;
-                               break;
-                       }
-               }
+/*
+ * Parse global options, between binary name and first non-option argument
+ * after processing all valid options (including those with arguments).
+ *
+ * Returns index to argv where parsting stopped, optind is reset to 1
+ */
+static int handle_global_options(int argc, char **argv)
+{
+       enum { OPT_HELP = 256, OPT_VERSION, OPT_FULL };
+       static const struct option long_options[] = {
+               { "help", no_argument, NULL, OPT_HELP },
+               { "version", no_argument, NULL, OPT_VERSION },
+               { "full", no_argument, NULL, OPT_FULL },
+               { NULL, 0, NULL, 0}
+       };
+       int shift;
+
+       if (argc == 0)
+               return 0;
 
-               /* If you understand why this code works ...
-                       you are a genious !! */
-               if(argc>i+1 && !strcmp(argv[i+1],"--help")){
-                       if(!helprequested)
-                               printf("Usage:\n");
-                       print_help(prgname, cp, ADVANCED_HELP);
-                       helprequested=1;
-                       continue;
+       opterr = 0;
+       while (1) {
+               int c;
+
+               c = getopt_long(argc, argv, "+", long_options, NULL);
+               if (c < 0)
+                       break;
+
+               switch (c) {
+               case OPT_HELP: break;
+               case OPT_VERSION: break;
+               case OPT_FULL: break;
+               default:
+                       fprintf(stderr, "Unknown global option: %s\n",
+                                       argv[optind - 1]);
+                       exit(129);
                }
+       }
 
-               if(!match)
-                       continue;
+       shift = optind;
+       optind = 1;
 
-               matchcmd = cp;
-               *nargs_  = argc-matchcmd->ncmds-1;
-               *cmd_ = matchcmd->verb;
-               *args_ = argv+matchcmd->ncmds+1;
-               *func_ = cp->func;
+       return shift;
+}
 
-               break;
+void handle_special_globals(int shift, int argc, char **argv)
+{
+       int has_help = 0;
+       int has_full = 0;
+       int i;
+
+       for (i = 0; i < shift; i++) {
+               if (strcmp(argv[i], "--help") == 0)
+                       has_help = 1;
+               else if (strcmp(argv[i], "--full") == 0)
+                       has_full = 1;
        }
 
-       if(helprequested){
-               printf("\n%s\n", BTRFS_BUILD_VERSION);
-               return 0;
+       if (has_help) {
+               if (has_full)
+                       usage_command_group(&btrfs_cmd_group, 1, 0);
+               else
+                       cmd_help(argc, argv);
+               exit(0);
        }
 
-       if(!matchcmd){
-               fprintf( stderr, "ERROR: unknown command '%s'\n",argv[1]);
-               help(prgname);
-               return -1;
-       }
+       for (i = 0; i < shift; i++)
+               if (strcmp(argv[i], "--version") == 0) {
+                       cmd_version(argc, argv);
+                       exit(0);
+               }
+}
 
-       if(check_ambiguity(matchcmd, argv))
-               return -2;
+static const struct cmd_group btrfs_cmd_group = {
+       btrfs_cmd_group_usage, btrfs_cmd_group_info, {
+               { "subvolume", cmd_subvolume, NULL, &subvolume_cmd_group, 0 },
+               { "filesystem", cmd_filesystem, NULL, &filesystem_cmd_group, 0 },
+               { "balance", cmd_balance, NULL, &balance_cmd_group, 0 },
+               { "device", cmd_device, NULL, &device_cmd_group, 0 },
+               { "scrub", cmd_scrub, NULL, &scrub_cmd_group, 0 },
+               { "check", cmd_check, cmd_check_usage, NULL, 0 },
+               { "rescue", cmd_rescue, NULL, &rescue_cmd_group, 0 },
+               { "restore", cmd_restore, cmd_restore_usage, NULL, 0 },
+               { "inspect-internal", cmd_inspect, NULL, &inspect_cmd_group, 0 },
+               { "property", cmd_property, NULL, &property_cmd_group, 0 },
+               { "send", cmd_send, cmd_send_usage, NULL, 0 },
+               { "receive", cmd_receive, cmd_receive_usage, NULL, 0 },
+               { "quota", cmd_quota, NULL, &quota_cmd_group, 0 },
+               { "qgroup", cmd_qgroup, NULL, &qgroup_cmd_group, 0 },
+               { "replace", cmd_replace, NULL, &replace_cmd_group, 0 },
+               { "help", cmd_help, cmd_help_usage, NULL, 0 },
+               { "version", cmd_version, cmd_version_usage, NULL, 0 },
+               NULL_CMD_STRUCT
+       },
+};
 
-       /* check the number of argument */
-       if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_ ){
-               fprintf(stderr, "ERROR: '%s' requires minimum %d arg(s)\n",
-                       matchcmd->verb, -matchcmd->nargs);
-                       return -2;
-       }
-       if(matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999){
-               fprintf(stderr, "ERROR: '%s' requires %d arg(s)\n",
-                       matchcmd->verb, matchcmd->nargs);
-                       return -2;
+int main(int argc, char **argv)
+{
+       const struct cmd_struct *cmd;
+       const char *bname;
+       int ret;
+
+       btrfs_config_init();
+
+       if ((bname = strrchr(argv[0], '/')) != NULL)
+               bname++;
+       else
+               bname = argv[0];
+
+       if (!strcmp(bname, "btrfsck")) {
+               argv[0] = "check";
+       } else {
+               int shift;
+
+               shift = handle_global_options(argc, argv);
+               handle_special_globals(shift, argc, argv);
+               while (shift-- > 0) {
+                       argc--;
+                       argv++;
+               }
+               if (argc == 0) {
+                       usage_command_group_short(&btrfs_cmd_group);
+                       exit(1);
+               }
        }
-       
-        if (prepare_args( nargs_, args_, prgname, matchcmd )){
-                fprintf(stderr, "ERROR: not enough memory\\n");
-               return -20;
-        }
 
+       cmd = parse_command_token(argv[0], &btrfs_cmd_group);
 
-       return 1;
-}
-int main(int ac, char **av )
-{
+       handle_help_options_next_level(cmd, argc, argv);
 
-       char            *cmd=0, **args=0;
-       int             nargs=0, r;
-       CommandFunction func=0;
+       crc32c_optimization_init();
 
-       r = parse_args(ac, av, &func, &nargs, &cmd, &args);
-       if( r <= 0 ){
-               /* error or no command to parse*/
-               exit(-r);
-       }
+       fixup_argv0(argv, cmd->token);
 
-       exit(func(nargs, args));
+       ret = cmd->fn(argc, argv);
 
-}
+       btrfs_close_all_devices();
 
+       exit(ret);
+}