/* ************************************************************************** */ /* _____ _ */ /* p7os/args.c |_ _|__ _ _| |__ ___ _ _ */ /* | Project: p7utils | |/ _ \| | | | '_ \ / _ \ | | | */ /* | | (_) | |_| | | | | __/ |_| | */ /* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ /* Last updated: 2017/01/16 23:55:54 |___/ */ /* */ /* ************************************************************************** */ #include "main.h" #include #include #include #include #include /* ************************************************************************** */ /* Help and version messages */ /* ************************************************************************** */ /* The version message - that's when the President comes in */ static const char version_message[] = QUOTE(BIN) " - from " QUOTE(NAME) " v" QUOTE(VERSION) " (licensed under GPLv2)\n" "Maintained by " QUOTE(MAINTAINER) ".\n" "\n" "This is free software; see the source for copying conditions.\n" "There is NO warranty; not even for MERCHANTABILITY or\n" "FITNESS FOR A PARTICULAR PURPOSE."; /* Main help message */ static const char help_main[] = "Usage: " QUOTE(BIN) " [--version|-v] [--help|-h] [--com ]\n" " [--no-prepare] [--uexe ]\n" " [options...]\n" "\n" "Subcommands you can use are :\n" " prepare-only Set-up the update program, but leave it for other programs\n" " to interact with it.\n" " get Get the OS image.\n" "\n" "General options:\n" " -h, --help Display the help page of the (sub)command and quit.\n" " -v, --version Display the version message and quit.\n" " --com The USB-serial port, if you want to communicate with a\n" " calculator connected using a USB-to-serial cable. (1 to 20)\n" " If this option isn't used, the program will look for a\n" " directly connected USB calculator.\n" " --no-prepare Use the current environment, instead of uploading one.\n" " --uexe Use a custom update program.\n" "\n" "Type \"" QUOTE(BIN) " --help\" for some help about a subcommand.\n" "Report bugs to " QUOTE(MAINTAINER) "."; /* Subcommands help messages footer */ #define FOOT \ "\nType \"" QUOTE(BIN) " --help\" for other subcommands and general options." /* Help message for prepare subcommand */ static const char help_prepare_only[] = "Usage: " QUOTE(BIN) " prepare-only\n" "Send the P7 server on the calculator for further operations.\n" "This must be used before any other p7os operation.\n" FOOT; /* Help message for get subcommand */ static const char help_get[] = "Usage: " QUOTE(BIN) " get [-o ]\n" "Get the calculator OS image.\n" "You must have \"p7os prepare\"-ed before.\n" "\n" "Options are :\n" " -o Where to store the image (default is \"os.bin\")\n" FOOT; /* ************************************************************************** */ /* Main function */ /* ************************************************************************** */ /* Help macro */ #define sub_init(CMD, NARGS) { \ args->menu = mn_##CMD; \ if (help || pc != (NARGS)) { \ puts(help_##CMD); \ return (1); \ }} /** * parse_args: * Args parsing main function. * * Based on my very first experiment with getopt. * * @arg ac the arguments count * @arg av the arguments values * @arg args the parsed args pointer * @return if has been parsed successfully */ int parse_args(int ac, char **av, args_t *args) { /* initialize args */ *args = (args_t){ .menu = 0, .com = 0, .noprepare = 0, .uexe = NULL, .local = NULL, .localpath = NULL }; /* define options */ const char shopts[] = "hvo:#"; const struct option longopts[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"com", required_argument, NULL, 'c'}, {"no-prepare", no_argument, NULL, 'n'}, {"uexe", required_argument, NULL, 'u'}, {"output", required_argument, NULL, 'o'}, {NULL, 0, NULL, 0} }; /* get all options */ int c; opterr = 0; int help = 0, version = 0; const char *s_out = "os.bin", *s_com = 0, *s_uexe = NULL; while ((c = getopt_long(ac, av, shopts, longopts, NULL)) != -1) switch (c) { /* help */ case 'h': help = 1; break; /* version */ case 'v': version = 1; break; /* com port */ case 'c': s_com = optarg; break; /* no prepare */ case 'n': args->noprepare = 1; break; /* uexe */ case 'u': s_uexe = optarg; break; /* output */ case 'o': s_out = optarg; break; /* error */ case '?': if (optopt == 'o') log("-o, --output: expected an argument\n"); else if (optopt == 'c') log("--com: expected an argument\n"); else if (optopt == 'u') log("--uexe: expected an argument\n"); else break; return (1); } /* check for version */ if (version) { puts(version_message); return (1); } /* get non-option arguments (subcommand and parameters) */ int pc = ac - optind; char **pv = &av[optind]; char *sub = pc ? pv[0] : NULL; pc--; pv++; /* subcommand. */ char fpmode[2] = "r"; if (!sub || !strcmp(sub, "help")) { puts(help_main); return (1); } else if (!strcmp(sub, "version")) { puts(version_message); return (1); } else if (!strcmp(sub, "prepare-only")) { sub_init(prepare_only, 0) if (args->noprepare) { log("So we should prepare but we should not prepare? Duh!\n"); return (1); } } else if (!strcmp(sub, "get")) { sub_init(get, 0) args->localpath = s_out; fpmode[0] = 'w'; } else { log("Unknown subcommand '%s'.\n", sub); return (1); } /* check com port */ if (s_com) { if (!isdigit(s_com[0])) { log("-c, --com: expected a number\n"); return (0); } args->com = atoi(s_com); if (args->com < 1 || args->com > 20) { log("-c, --com: COM port number should be between 1 and 20\n"); return (0); } } /* open destination file */ if (args->localpath) { args->local = fopen(args->localpath, fpmode); if (!args->local) { log("Could not open local file: %s\n", strerror(errno)); return (1); } } /* open update.exe file */ if (s_uexe) { args->uexe = fopen(s_uexe, "r"); if (!args->uexe) { log("Could not open update program: %s\n", strerror(errno)); if (args->local) fclose(args->local); return (1); } } /* everything went well :) */ return (0); }