Hi Richard,

This patch looks great. My comments below are just my process for understanding 
what the code does. I really like this approach, it's a pretty small amount of 
code and lets us easily override any option from the command line, without 
having to continuously add more short options for esoteric configurations.

Regards,
Jake

> -----Original Message-----
> From: Richard Cochran [mailto:richardcoch...@gmail.com]
> Sent: Tuesday, December 06, 2016 10:50 AM
> To: linuxptp-devel@lists.sourceforge.net
> Subject: [Linuxptp-devel] [PATCH] ptp4l: Accept any configuration option as a
> command line argument.
> 
> This patch provides a way to use the entire table of configuration options
> as "long" command line switches.
> 

Neat, I didn't think it would be so little code! Thanks!

> Signed-off-by: Richard Cochran <richardcoch...@gmail.com>
> ---
>  config.c | 64
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> --
>  config.h | 11 +++++++++++
>  ptp4l.c  | 11 +++++++++--
>  3 files changed, 82 insertions(+), 4 deletions(-)
> 
> diff --git a/config.c b/config.c
> index 5da5ecc..384b437 100644
> --- a/config.c
> +++ b/config.c
> @@ -329,6 +329,7 @@ static enum parser_result parse_section_line(char *s,
> enum config_section *secti
>  }
> 
>  static enum parser_result parse_item(struct config *cfg,
> +                                  int commandline,

Ok so we add a boolean to set whether we're calling parse_item from command 
line code.

>                                    const char *section,
>                                    const char *option,
>                                    const char *value)
> @@ -387,7 +388,7 @@ static enum parser_result parse_item(struct config *cfg,
>                               return NOT_PARSED;
>                       }
>               }
> -     } else if (cgi->flags & CFG_ITEM_LOCKED) {
> +     } else if (!commandline && cgi->flags & CFG_ITEM_LOCKED) {

And here, we do allow over-writing a previous assignment with a later 
assignment from the command line. Good.

>               /* This global option was set on the command line. */
>               return PARSED_OK;
>       } else {
> @@ -415,6 +416,10 @@ static enum parser_result parse_item(struct config *cfg,
>               dst->flags |= CFG_ITEM_DYNSTR;
>               break;
>       }
> +
> +     if (commandline) {
> +             dst->flags &= CFG_ITEM_LOCKED;
> +     }

Finally, if we run from the command line, we make sure we lock the item now so 
that it doesn't change again. This makes sense.

>       return PARSED_OK;
>  }
> 
> @@ -490,6 +495,25 @@ static void check_deprecated_options(const char
> **option)
>       }
>  }
> 
> +static struct option *config_alloc_longopts(struct config *cfg)
> +{
> +     struct config_item *ci;
> +     struct option *opts;
> +     int i;
> +
> +     opts = calloc(1, (1 + N_CONFIG_ITEMS) * sizeof(*opts));
> +     if (!opts) {
> +             return NULL;
> +     }
> +     for (i = 0; i < N_CONFIG_ITEMS; i++) {
> +             ci = &config_tab[i];
> +             opts[i].name = ci->label;
> +             opts[i].has_arg = required_argument;
> +     }
> +
> +     return opts;
> +}
> +

A new function here to generate long option structures from the configuration 
data. Ok.

>  int config_read(char *name, struct config *cfg)
>  {
>       enum config_section current_section = UNKNOWN_SECTION;
> @@ -554,7 +578,7 @@ int config_read(char *name, struct config *cfg)
> 
>               check_deprecated_options(&option);
> 
> -             parser_res = parse_item(cfg, current_section ==
> GLOBAL_SECTION ?
> +             parser_res = parse_item(cfg, 0, current_section ==
> GLOBAL_SECTION ?
>                                       NULL : current_port->name, option,
> value);
> 
>               switch (parser_res) {
> @@ -627,8 +651,15 @@ struct config *config_create(void)
>       }
>       STAILQ_INIT(&cfg->interfaces);
> 
> +     cfg->opts = config_alloc_longopts(cfg);
> +     if (!cfg->opts) {
> +             free(cfg);
> +             return NULL;
> +     }
> +
>       cfg->htab = hash_create();
>       if (!cfg->htab) {
> +             free(cfg->opts);
>               free(cfg);
>               return NULL;
>       }
> @@ -657,6 +688,7 @@ struct config *config_create(void)
>       return cfg;
>  fail:
>       hash_destroy(cfg->htab, NULL);
> +     free(cfg->opts);
>       free(cfg);
>       return NULL;
>  }
> @@ -670,6 +702,7 @@ void config_destroy(struct config *cfg)
>               free(iface);
>       }
>       hash_destroy(cfg->htab, config_item_free);
> +     free(cfg->opts);
>       free(cfg);
>  }
> 
> @@ -720,6 +753,33 @@ char *config_get_string(struct config *cfg, const char
> *section,
>       return ci->val.s;
>  }
> 
> +int config_parse_option(struct config *cfg, const char *opt, const char *val)
> +{
> +     enum parser_result result;
> +
> +     result = parse_item(cfg, 1, NULL, opt, val);
> +

Right so we can re-use the parse item code but setting command line to true 
here.

> +     switch (result) {
> +     case PARSED_OK:
> +             return 0;
> +     case NOT_PARSED:
> +             fprintf(stderr, "unknown option %s\n", opt);
> +             break;
> +     case BAD_VALUE:
> +             fprintf(stderr, "%s is a bad value for option %s\n", val, opt);
> +             break;
> +     case MALFORMED:
> +             fprintf(stderr, "%s is a malformed value for option %s\n",
> +                     val, opt);
> +             break;
> +     case OUT_OF_RANGE:
> +             fprintf(stderr, "%s is an out of range value for option %s\n",
> +                     val, opt);
> +             break;
> +     }
> +     return -1;
> +}
> +
>  int config_set_double(struct config *cfg, const char *option, double val)
>  {
>       struct config_item *ci = config_find_item(cfg, NULL, option);
> diff --git a/config.h b/config.h
> index b02bde6..1cc7051 100644
> --- a/config.h
> +++ b/config.h
> @@ -20,6 +20,7 @@
>  #ifndef HAVE_CONFIG_H
>  #define HAVE_CONFIG_H
> 
> +#include <getopt.h>
>  #include <sys/queue.h>
> 
>  #include "ds.h"
> @@ -43,6 +44,9 @@ struct config {
>       STAILQ_HEAD(interfaces_head, interface) interfaces;
>       int n_interfaces;
> 
> +     /* for parsing command line options */
> +     struct option *opts;
> +
>       /* hash of all non-legacy items */
>       struct hash *htab;
>  };
> @@ -64,6 +68,13 @@ int config_get_int(struct config *cfg, const char *section,
>  char *config_get_string(struct config *cfg, const char *section,
>                       const char *option);
> 
> +static inline struct option *config_long_options(struct config *cfg)
> +{
> +     return cfg->opts;
> +}
> +
> +int config_parse_option(struct config *cfg, const char *opt, const char 
> *val);
> +
>  int config_set_double(struct config *cfg, const char *option, double val);
> 
>  int config_set_section_int(struct config *cfg, const char *section,
> diff --git a/ptp4l.c b/ptp4l.c
> index a87e7e6..e90fcb2 100644
> --- a/ptp4l.c
> +++ b/ptp4l.c
> @@ -73,8 +73,9 @@ static void usage(char *progname)
>  int main(int argc, char *argv[])
>  {
>       char *config = NULL, *req_phc = NULL, *progname;
> -     int c, err = -1, print_level;
> +     int c, err = -1, index, print_level;
>       struct clock *clock = NULL;
> +     struct option *opts;
>       struct config *cfg;
> 
>       if (handle_term_signals())
> @@ -84,12 +85,18 @@ int main(int argc, char *argv[])
>       if (!cfg) {
>               return -1;
>       }
> +     opts = config_long_options(cfg);
> 

So in the config code we have method for generating the options structure, and 
we get that here. Nice.

>       /* Process the command line arguments. */
>       progname = strrchr(argv[0], '/');
>       progname = progname ? 1+progname : argv[0];
> -     while (EOF != (c = getopt(argc, argv, "AEP246HSLf:i:p:sl:mqvh"))) {
> +     while (EOF != (c = getopt_long(argc, argv, "AEP246HSLf:i:p:sl:mqvh",
> +                                    opts, &index))) {

Finally, we switch to using getopt_long, which will grab the index of the 
matching long option.. So we can then parse its values next.

>               switch (c) {
> +             case 0:
> +                     if (config_parse_option(cfg, opts[index].name, optarg))
> +                             goto out;
> +                     break;
>               case 'A':
>                       if (config_set_int(cfg, "delay_mechanism", DM_AUTO))
>                               goto out;
> --
> 2.1.4
> 
> 
> ------------------------------------------------------------------------------
> Developer Access Program for Intel Xeon Phi Processors
> Access to Intel Xeon Phi processor-based developer platforms.
> With one year of Intel Parallel Studio XE.
> Training and support from Colfax.
> Order your platform today.http://sdm.link/xeonphi
> _______________________________________________
> Linuxptp-devel mailing list
> Linuxptp-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/linuxptp-devel
------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to