+/*
+ * Separate option name to 3 fields
+ * Ex:
+ * Input: name = channel.cpu.bufsize
+ * Output: name1 = channel
+ * name2 = cpu
+ * name3 = bufsize
+ * Ret: 0 on success
+ * 1 on fail
+ *
+ * Note:
+ * Make sure that name1~3 longer than OPT_NAMELEN.
+ * name1~3 can be NULL to discard value
+ *
+ */
+static int separate_opt(const char *name, char *name1, char *name2, char *name3)
+{
+ char *p;
+
+ if (!name)
+ return 1;
+
+ /* segment1 */
+ p = strchr(name, '.');
+ if (!p)
+ return 1;
+ if (p - name >= OPT_NAMELEN)
+ return 1;
+ if (name1) {
+ memcpy(name1, name, p - name);
+ name1[p - name] = 0;
+ }
+ name = p + 1;
+
+ /* segment2 */
+ p = strchr(name, '.');
+ if (!p)
+ return 1;
+ if (p - name >= OPT_NAMELEN)
+ return 1;
+ if (name2) {
+ memcpy(name2, name, p - name);
+ name2[p - name] = 0;
+ }
+ name = p + 1;
+
+ /* segment3 */
+ if (strlen(name) >= OPT_NAMELEN)
+ return 1;
+ if (name3)
+ strcpy(name3, name);
+
+ return 0;
+}
+
+/*
+ * get option's type by its name,
+ * can also be used to check is option exists
+ * (return option_type_unknown when not exist)
+ */
+static enum lttcrl_option_type opt_type(const char *name1, const char *name2,
+ const char *name3)
+{
+ if (!name1 || !name2 || !name3)
+ return option_type_unknown;
+
+ if (strlen(name1) >= OPT_NAMELEN
+ || strlen(name2) >= OPT_NAMELEN
+ || strlen(name3) >= OPT_NAMELEN)
+ return option_type_unknown;
+
+ if (strcmp(name1, "channel") == 0) {
+ /* Option is channel class */
+ if (strcmp(name3, "enable") == 0)
+ return option_type_bool;
+ if (strcmp(name3, "overwrite") == 0)
+ return option_type_bool;
+ if (strcmp(name3, "bufnum") == 0)
+ return option_type_uint;
+ if (strcmp(name3, "bufsize") == 0)
+ return option_type_uint;
+ return option_type_unknown;
+ }
+
+ /*
+ * Now we only support channel options
+ * other option class' will used in future
+ */
+
+ return option_type_unknown;
+}
+
+static struct lttctl_option *find_opt(const char *name1, const char *name2,
+ const char *name3)
+{
+ int i;
+
+ if (!name1 || !name2 || !name3)
+ return NULL;
+
+ for (i = 0; i < opt_option_n; i++) {
+ if (strcmp(opt_options[i].name1, name1) == 0
+ && strcmp(opt_options[i].name2, name2) == 0
+ && strcmp(opt_options[i].name3, name3) == 0)
+ return opt_options + i;
+ }
+
+ return NULL;
+}
+
+static struct lttctl_option *get_opt(const char *name1, const char *name2,
+ const char *name3)
+{
+ struct lttctl_option *opt;
+
+ if (!name1 || !name2 || !name3)
+ return NULL;
+
+ opt = find_opt(name1, name2, name3);
+ if (opt)
+ return opt;
+
+ if (opt_option_n >= OPT_MAX) {
+ fprintf(stderr, "Option number out of range\n");
+ return NULL;
+ }
+
+ if (strlen(name1) >= OPT_NAMELEN
+ || strlen(name2) >= OPT_NAMELEN
+ || strlen(name3) >= OPT_NAMELEN) {
+ fprintf(stderr, "Option name too long: %s.%s.%s\n",
+ name1, name2, name3);
+ return NULL;
+ }
+
+ opt = &opt_options[opt_option_n];
+ strcpy(opt->name1, name1);
+ strcpy(opt->name2, name2);
+ strcpy(opt->name3, name3);
+ opt_option_n++;
+
+ return opt;
+}
+
+static int parst_opt(const char *optarg)
+{
+ int ret;
+ char opt_name[OPT_NAMELEN * 3];
+ char opt_valstr[OPT_VALSTRINGLEN];
+ char *p;
+
+ char name1[OPT_NAMELEN];
+ char name2[OPT_NAMELEN];
+ char name3[OPT_NAMELEN];
+
+ enum lttcrl_option_type opttype;
+ int opt_intval;
+ unsigned int opt_uintval;
+ struct lttctl_option *newopt;
+
+ if (!optarg) {
+ fprintf(stderr, "Option empty\n");
+ return -EINVAL;
+ }
+
+ /* Get option name and val_str */
+ p = strchr(optarg, '=');
+ if (!p) {
+ fprintf(stderr, "Option format error: %s\n", optarg);
+ return -EINVAL;
+ }
+
+ if (p - optarg >= sizeof(opt_name)/sizeof(opt_name[0])) {
+ fprintf(stderr, "Option name too long: %s\n", optarg);
+ return -EINVAL;
+ }
+
+ if (strlen(p+1) >= OPT_VALSTRINGLEN) {
+ fprintf(stderr, "Option value too long: %s\n", optarg);
+ return -EINVAL;
+ }
+
+ memcpy(opt_name, optarg, p - optarg);
+ opt_name[p - optarg] = 0;
+ strcpy(opt_valstr, p+1);
+
+ /* separate option name into 3 fields */
+ ret = separate_opt(opt_name, name1, name2, name3);
+ if (ret != 0) {
+ fprintf(stderr, "Option name error: %s\n", optarg);
+ return -EINVAL;
+ }
+
+ /*
+ * check and add option
+ */
+ opttype = opt_type(name1, name2, name3);
+ switch (opttype) {
+ case option_type_unknown:
+ fprintf(stderr, "Option not supported: %s\n", optarg);
+ return -EINVAL;
+ case option_type_string:
+ newopt = get_opt(name1, name2, name3);
+ if (!newopt)
+ return -EINVAL;
+ strcpy(newopt->value.v_string, opt_valstr);
+ return 0;
+ case option_type_int:
+ ret = sscanf(opt_valstr, "%d", &opt_intval);
+ if (ret != 1) {
+ fprintf(stderr, "Option format error: %s\n", optarg);
+ return -EINVAL;
+ }
+ newopt = get_opt(name1, name2, name3);
+ if (!newopt)
+ return -EINVAL;
+ newopt->value.v_int = opt_intval;
+ return 0;
+ case option_type_uint:
+ ret = sscanf(opt_valstr, "%u", &opt_uintval);
+ if (ret != 1) {
+ fprintf(stderr, "Option format error: %s\n", optarg);
+ return -EINVAL;
+ }
+ newopt = get_opt(name1, name2, name3);
+ if (!newopt)
+ return -EINVAL;
+ newopt->value.v_uint = opt_uintval;
+ return 0;
+ case option_type_positive:
+ ret = sscanf(opt_valstr, "%u", &opt_uintval);
+ if (ret != 1 || opt_uintval == 0) {
+ fprintf(stderr, "Option format error: %s\n", optarg);
+ return -EINVAL;
+ }
+ newopt = get_opt(name1, name2, name3);
+ if (!newopt)
+ return -EINVAL;
+ newopt->value.v_uint = opt_uintval;
+ return 0;
+ case option_type_bool:
+ if (opt_valstr[1] != 0) {
+ fprintf(stderr, "Option format error: %s\n", optarg);
+ return -EINVAL;
+ }
+ if (opt_valstr[0] == 'Y' || opt_valstr[0] == 'y'
+ || opt_valstr[0] == '1')
+ opt_intval = 1;
+ else if (opt_valstr[0] == 'N' || opt_valstr[0] == 'n'
+ || opt_valstr[0] == '0')
+ opt_intval = 0;
+ else {
+ fprintf(stderr, "Option format error: %s\n", optarg);
+ return -EINVAL;
+ }
+
+ newopt = get_opt(name1, name2, name3);
+ if (!newopt)
+ return -EINVAL;
+ newopt->value.v_bool = opt_intval;
+ return 0;
+ default:
+ fprintf(stderr, "Internal error on opt %s\n", optarg);
+ return -EINVAL;
+ }
+
+ return 0; /* should not run to here */
+}
+