OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
argpar-json.c
Go to the documentation of this file.
1 #include "argpar.h"
2 
3 #include <ctype.h>
4 #include <errno.h>
5 #include <limits.h>
6 #include <stdbool.h>
7 #include <stdlib.h>
8 #include <string.h>
9 
10 #include <jansson.h>
11 
12 
13 static int print_wrapped(json_t *json, argpar* p, const char *key, const char *doc, int filter_key) {
14 // printf("%s\n", __PRETTY_FUNCTION__);
15  char *text;
16  if (filter_key && p->help_filter) {
17  text = p->help_filter(filter_key, doc, NULL);
18  } else if (doc) {
19  text = (char*)doc;
20  } else {
21  return 0;
22  }
23  if (text) {
24  json_object_set_new(json, key, json_string(doc));
25 
26  if (text != doc) {
27  free(text);
28  }
29  }
30  return 0;
31 }
32 
33 static const char *get_arg_text(const argpar_option *o) {
34 // printf("%s\n", __PRETTY_FUNCTION__);
35  const char *arg;
36  if (o->arg) {
37  arg = o->arg;
38  } else if (o->flags & OPTION_DBL) {
39  arg = "DBL";
40  } else if (o->flags & OPTION_INT) {
41  arg = "INT";
42  } else {
43  arg = "ARG";
44  }
45  return arg;
46 }
47 
48 typedef enum {
49  HEADER = 1, OPTION = 2, DOC = 3
50 } option_type;
51 typedef enum {
52  MERGED = 0, NOT_MERGED = 1
53 } child_type;
54 typedef struct item_counts {
55  unsigned header;
56  unsigned option;
57  unsigned doc;
58 } item_counts;
59 
60 // all the group/option stuff should probably be pre-processed, like in argp (see clusters), but it works fine
61 static int print_all_options(json_t *json, const argpar_child *c, bool is_parent); // should prototype all group ones so this looks less weird
62 
63 static int _print_group(json_t *json, const argpar_child *c, bool is_parent, option_type type, int group, child_type merged, item_counts *counts) {
64 // printf("%s\n", __PRETTY_FUNCTION__);
65 // printf("%s %p, group %d, type %d, not merged %d\n", c->header ? "child" : "parent", c->argpar, group, type, merged);
66  argpar *p = c->argpar;
67  bool printed = false;
68  if ((merged == NOT_MERGED && c->header && group == c->group) || (merged == MERGED && !c->header)) {
69  printed = true;
70  if (c->header) {
71  if (strlen(c->header)) {
72  if (print_wrapped(json, p, "header", c->header, ARGPAR_KEY_HELP_HEADER)) {
73  return 1;
74  }
75  counts->header += 1;
76  }
77  argpar_child merged_child = { c->argpar, c->flags, NULL, 0 };
78  print_all_options(json, &merged_child, false);
79  } else {
80  const argpar_option *o = p->options;
81  int i = -1, cur_group = 0;
82  json_t *valid_values = NULL;
83  json_t *aliases = NULL;
84  json_t *current_option = NULL;
85  while (o[++i].name || o[i].doc) {
86  int is_doc = o[i].flags & OPTION_DOC;
87  if (!group && (o[i].group || (!o[i].name && !is_doc))) {
88  break;
89  } else if (o[i].group) {
90  cur_group = o[i].group;
91  } else if (!o[i].name && !is_doc) {
92  if (cur_group < 0) {
93  cur_group--;
94  } else {
95  cur_group++;
96  }
97  }
98 
99  if (cur_group == group && !((o[i].flags & OPTION_PARENT) && !is_parent) && !((o[i].flags & OPTION_CHILD) && is_parent)) {
100 // if (cur_group == group && !(o[i].flags & OPTION_HIDDEN) && !((o[i].flags & OPTION_PARENT) && !is_parent) && !((o[i].flags & OPTION_CHILD) && is_parent)) {
101  switch (type) {
102  case HEADER:
103  if (!o[i].name && !(o[i].flags & OPTION_DOC)) {
104 // if (print_wrapped(json, p, "header", o[i].doc, ARGPAR_KEY_HELP_HEADER)) {
105 // return 1;
106 // }
107  counts->header += 1;
108  }
109  break;
110  case OPTION:
111  if (o[i].name) {
112  if ((o[i].flags & OPTION_ATTR) == OPTION_ATTR) {
113  json_object_set_new(current_option, o[i].name, json_string(o[i].doc));
114  } else if ((o[i].flags & OPTION_ENUM) == OPTION_ENUM) {
115  if (valid_values == NULL){
116  valid_values = json_array();
117  json_object_set_new(current_option, "validValues", valid_values);
118  }
119 
120  json_t *valid_value = json_object();
121  json_array_append_new(valid_values, valid_value);
122  json_object_set_new(valid_value, "value", json_string(o[i].name));
123  if (o[i].doc){
124  json_object_set_new(valid_value, "description", json_string(o[i].doc));
125  }
126  } else if ((o[i].flags & OPTION_ALIAS) == OPTION_ALIAS) {
127  if (aliases == NULL){
128  aliases = json_array();
129  json_object_set_new(current_option, "aliases", aliases);
130  }
131  json_array_append_new(aliases, json_string(o[i].name));
132  } else if (o[i].flags & OPTION_DOC) {
133  json_object_set_new(current_option, "doc", json_string(o[i].name));
134  } else {
135  current_option = json_object();
136  json_array_append_new(json, current_option);
137  const char *arg = get_arg_text(&o[i]);
138  json_object_set_new(current_option, "name", json_string(o[i].name));
139  json_object_set_new(current_option, "arg", json_string(arg));
140 
141  if (o[i].doc) {
142  if (print_wrapped(current_option, p, "description", o[i].doc, o[i].key)) {
143  return 1;
144  }
145  }
146 
147  valid_values = NULL;
148  }
149 
150  counts->option += 1;
151  }
152  break;
153  case DOC:
154  if (!o[i].name && (o[i].flags & OPTION_DOC)) {
155 // if (print_wrapped(json, p, "doc", o[i].doc, ARGPAR_KEY_HELP_OPTION_DOC)) {
156 // return 1;
157 // }
158  counts->doc += 1;
159  }
160  break;
161  }
162  }
163  }
164  }
165  }
166 
167  if (p->children && (printed || merged == NOT_MERGED)) {
168  const argpar_child *c;
169  for (c = p->children; c->argpar; c++) {
170  if (_print_group(json, c, false, type, group, merged, counts)) {
171  return 1;
172  }
173  }
174  }
175 
176  return 0;
177 }
178 
179 static int print_group(json_t *json, const argpar_child *c, bool is_parent, int group, child_type merged, item_counts *counts) {
180 // printf("%s\n", __PRETTY_FUNCTION__);
182  if (merged == MERGED) {
183  for (type = HEADER; type <= DOC; type++) {
184  if (_print_group(json, c, is_parent, type, group, merged, counts)) {
185  return 1;
186  }
187  }
188  } else {
189  if (_print_group(json, c, is_parent, HEADER, group, merged, counts)) {
190  return 1;
191  }
192  }
193  return 0;
194 }
195 
196 static int print_args(json_t *json, const argpar_child *c) {
197 // printf("%s\n", __PRETTY_FUNCTION__);
198  argpar *p = c->argpar;
199  if (p) {
200  if (p->args_doc) {
201  print_wrapped(json, p, "args", p->args_doc, ARGPAR_KEY_HELP_ARGS_DOC);
202  }
203 // if (p->options){
204 // const argpar_option *o = p->options;
205 // int i = -1;
206 // while (o[++i].name || o[i].doc){
207 // if (!o[i].name || (o[i].flags & OPTION_HIDDEN) || (o[i].flags & OPTION_NO_USAGE) || (o[i].flags & OPTION_DOC)){
208 // continue;
209 // } else if ((o[i].flags & OPTION_ARG_OPTIONAL) == 0){ // required option
210 // const char *arg = get_arg_text(o);
211 // int adding_cols = strlen(o[i].name) + strlen(arg) + 1;
212 // if ((*cur_column + adding_cols) > RMARGIN){
213 // fprintf(argpar_ostream, "\n%*s", WRAP_INDENT, "");
214 // *cur_column = WRAP_INDENT;
215 // }
216 // fprintf(argpar_ostream, "%s=%s ", o[i].name, arg);
217 // *cur_column += adding_cols;
218 // }
219 // }
220 // }
221  if (p->children) {
222  const argpar_child *c;
223  for (c = p->children; c->argpar; c++) {
224  if (print_args(json, c)) {
225  return 1;
226  }
227  }
228  }
229  }
230  return 0;
231 }
232 
233 typedef struct group_limits {
234  int max, min;
235 } group_limits;
236 
237 static int find_group_limits(const argpar_child *c, group_limits *l) {
238 // printf("%s\n", __PRETTY_FUNCTION__);
239  argpar *p = c->argpar;
240  if (p && p->options) {
241  if (c->group > l->max) {
242  l->max = c->group;
243  }
244  if (c->group < l->min) {
245  l->min = c->group;
246  }
247  if (!c->header) {
248  const argpar_option *o = p->options;
249  int i = -1, cur_group = 0;
250  while (o[++i].name || o[i].doc) {
251  if (o[i].group) {
252  cur_group = o[i].group;
253  } else if (!o[i].name && !(o[i].flags & OPTION_DOC)) {
254  if (cur_group < 0) {
255  cur_group--;
256  } else {
257  cur_group++;
258  }
259  }
260  if (cur_group > l->max) {
261  l->max = cur_group;
262  }
263  if (cur_group < l->min) {
264  l->min = cur_group;
265  }
266  }
267  if (p->children) {
268  const argpar_child *c;
269  for (c = p->children; c->argpar; c++) {
270  if (find_group_limits(c, l)) {
271  return 1;
272  }
273  }
274  }
275  }
276  }
277  return 0;
278 }
279 
280 static int print_all_options(json_t *json, const argpar_child *c, bool is_parent) {
281 // printf("%s\n", __PRETTY_FUNCTION__);
282  group_limits g_lims = { .min = INT_MAX, .max = INT_MIN };
283  find_group_limits(c, &g_lims);
284 
285  int group_i; // really need to get rid of the following copy pasta
286  for (group_i = 0; group_i <= g_lims.max; group_i++) {
287  item_counts counts = { 0, 0, 0 };
288  if (print_group(json, c, is_parent, group_i, MERGED, &counts)) {
289  return 1;
290  }
291  if (print_group(json, c, is_parent, group_i, NOT_MERGED, &counts)) {
292  return 1;
293  }
294  }
295  for (group_i = g_lims.min; group_i < 0; group_i++) {
296  item_counts counts = { 0, 0, 0 };
297  if (print_group(json, c, is_parent, group_i, MERGED, &counts)) {
298  return 1;
299  }
300  if (print_group(json, c, is_parent, group_i, NOT_MERGED, &counts)) {
301  return 1;
302  }
303  }
304  return 0;
305 }
306 
307 static int print_ending_doc(json_t *json, const argpar_child *c) {
308 // printf("%s\n", __PRETTY_FUNCTION__);
309  argpar *p = c->argpar;
310  if (p->doc) {
311  char *ending_doc = strchr(p->doc, '\v');
312  if (ending_doc) {
313  int printed = print_wrapped(json, p, "post_doc", NULL, ARGPAR_KEY_HELP_POST_DOC);
314  if (printed > 0) {
315  return 1;
316  }
317  }
318  }
319 
320  if (p->children) {
321  const argpar_child *c;
322  for (c = p->children; c->argpar; c++) {
323  if (print_ending_doc(json, c)) {
324  return 1;
325  }
326  }
327  }
328  return 0;
329 }
330 
331 #if HAVE_DECL_PROGRAM_INVOCATION_NAME
332 // stolen directly from argp
333 static char *nondestructive_basename(char *name) {
334  char *short_name = strrchr (name, '/');
335  return short_name ? short_name + 1 : name;
336 }
337 #endif
338 
339 // mostly stolen from argp
340 static char *program_name() {
341 #if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME
342  return program_invocation_short_name;
343 #elif HAVE_DECL_PROGRAM_INVOCATION_NAME
344  return nondestructive_basename(program_invocation_name);
345 #else
346  return (argpar_program_name ? (char*) argpar_program_name : "<cmd>");
347 #endif
348 }
349 
350 static int _argpar_print_usage(argpar *p, unsigned flags) {
351  const argpar_child base_argpar = { p, flags, NULL, 0 };
352  if (!argpar_ostream) {
354  }
355  char *progname = program_name();
356 
357  json_t *json = json_object();
358  json_t *json_program = json_object();
359  json_object_set_new(json, "program", json_program);
360  json_object_set_new(json_program, "name", json_string(progname));
361 
362  print_args(json_program, &base_argpar);
363 
364  if (p->doc) {
365  char *ending_doc = strchr(p->doc, '\v');
366  if (ending_doc) {
367  size_t new_doc_length = strlen(p->doc) - strlen(ending_doc);
368  char new_doc[new_doc_length + 1];
369  strncpy(new_doc, p->doc, new_doc_length);
370  new_doc[new_doc_length] = '\0';
371  if (print_wrapped(json_program, p, "pre_doc", new_doc, ARGPAR_KEY_HELP_PRE_DOC)) {
372  return 1;
373  }
374  } else {
375  if (print_wrapped(json_program, p, "pre_doc", p->doc, ARGPAR_KEY_HELP_PRE_DOC)) {
376  return 1;
377  }
378  }
379  }
380 
381  json_t *json_options = json_array();
382  json_object_set_new(json, "options", json_options);
383  if (print_all_options(json_options, &base_argpar, true)) {
384  return 1;
385  }
386 
387  if (p->children) {
388  print_ending_doc(json, &base_argpar);
389  }
390 
391  print_wrapped(json_program, p, "help_extra", NULL, ARGPAR_KEY_HELP_EXTRA);
392  if (p->children) {
393  const argpar_child *c;
394  for (c = p->children; c->argpar; c++) {
395  if (print_wrapped(json_program, c->argpar, "help_extra", NULL, ARGPAR_KEY_HELP_EXTRA)) {
396  return 1;
397  }
398  }
399  }
400 
401 #if JANSSON_VERSION_HEX >= 0x020800
402  int ret = json_dumpf(json, argpar_ostream, JSON_INDENT(4));
403 #else
404  int ret = json_dumpf(json, argpar_ostream, JSON_INDENT(4) | JSON_PRESERVE_ORDER);
405 #endif
406 
407  ret |= json_object_clear(json);
408  json_decref(json);
409 
410  return ret;
411 }
412 
413 
415  return _argpar_print_usage(state->argpar, state->flags);
416 }
418  return _argpar_print_usage(argpar, 0);
419 }
420 int argpar_help_json(argpar *argpar, FILE *stream, unsigned flags, char *name) {
421  argpar_ostream = stream;
422  if (name != NULL) {
424  }
425  return _argpar_print_usage(argpar, flags);
426 }
Master structure containing options, document strings, child parsers, and text filters....
Definition: argpar.h:398
unsigned doc
Definition: argpar-help.c:142
option_type
Definition: argpar-help.c:133
#define ARGPAR_KEY_HELP_PRE_DOC
Passed as the key to each help filter when printing the doc string before options are listed....
Definition: argpar.h:315
@ DOC
Definition: argpar-json.c:49
These are used to scale the SD before writing it to the HDF4 file The default is and which means the product is not scaled at all Since the product is usually stored as a float inside of this is a way to write the float out as a integer l2prod min
int argpar_help_json(argpar *argpar, FILE *stream, unsigned flags, char *name)
Print the default usage summary with all available sections, in a format more suitable for automated ...
Definition: argpar-json.c:420
#define NULL
Definition: decode_rs.h:63
unsigned header
Definition: argpar-help.c:140
#define OPTION_INT
Cast this option as a long. The value and any error will be reflected in the argpar_state struct duri...
Definition: argpar.h:160
FILE * argpar_ostream
Used as the stream for printing help and usage messages. Defaults to STDERR.
Definition: argpar.h:190
const char * argpar_program_name
How to display the program name. If not given, it will be derived during a call to argpar_parse_args.
@ MERGED
Definition: argpar-json.c:52
uint8 * counts
Definition: l1_czcs_hdf.c:30
child_type
Definition: argpar-help.c:136
Child parser for nesting argpars.
Definition: argpar.h:377
child_type
Definition: argpar-json.c:51
#define OPTION_ATTR
This option serves to document additional attributes for an option. These are hidden from the normal ...
Definition: argpar.h:183
unsigned option
Definition: argpar-help.c:141
@ HEADER
Definition: argpar-json.c:49
@ OPTION
Definition: argpar-json.c:49
#define ARGPAR_KEY_HELP_POST_DOC
Passed as the key to each help filter when printing the doc string after all options are listed,...
Definition: argpar.h:320
int state(double tjdTDB, JPLIntUtilType *util, double posvel[13][6], double *pnut)
#define OPTION_ENUM
This option serves to document a valid value for an option. This is not enforced by argpar.
Definition: argpar.h:179
int flags
Modify the behavior of the argument by OR'ing one or more OPTION_ macros.
Definition: argpar.h:100
#define ARGPAR_KEY_HELP_HEADER
Passed as the key to each help filter when printing a group header.
Definition: argpar.h:323
option_type
Definition: argpar-json.c:48
#define ARGPAR_KEY_HELP_EXTRA
Passed as the key to each help filter after all other documentation is printed, to return extra infor...
Definition: argpar.h:330
#define OPTION_ALIAS
Do not add an extra newline after this documentation string. Useful for lists and manual formatting.
Definition: argpar.h:175
flags
Definition: DDAlgorithm.h:22
#define OPTION_DOC
This option isn't actually an option, merely text for the usage summary.
Definition: argpar.h:152
int argpar_usage_json(argpar_state *state)
Print usage summary, called within a argpar_parser, in a format more suitable for automated parsing.
Definition: argpar-json.c:414
State variable to be filled before each call to the parser callback.
Definition: argpar.h:196
PARAM_TYPE_NONE Default value No parameter is buried in the product name name_prefix is case insensitive string compared to the product name PARAM_TYPE_VIS_WAVE The visible wavelength bands from the sensor are buried in the product name The product name is compared by appending and name_suffix ie aph_412_giop where prod_ix will be set to PARAM_TYPE_IR_WAVE same search method as PARAM_TYPE_VIS_WAVE except only wavelength above are looped through but prod_ix is still based ie aph_2_giop for the second and prod_ix set to PARAM_TYPE_INT name_prefix is compared with the beginning of the product name If name_suffix is not empty the it must match the end of the product name The characters right after the prefix are read as an integer and prod_ix is set to that number strncpy(l2prod->name_prefix, "myprod", UNITLEN)
#define ARGPAR_KEY_HELP_ARGS_DOC
Passed as the key to each help filter when printing the args_doc.
Definition: argpar.h:310
Library for reading command-line arguments in the form of key=value.
#define OPTION_CHILD
This option only applies if this parser is not the top-most parser.
Definition: argpar.h:167
int argpar_usage_default_json(argpar *argpar)
Print the default usage summary with all available sections, in a format more suitable for automated ...
Definition: argpar-json.c:417
@ NOT_MERGED
Definition: argpar-json.c:52
no change in intended resolving MODur00064 Corrected handling of bad ephemeris attitude resolving resolving GSFcd00179 Corrected handling of fill values for[Sensor|Solar][Zenith|Azimuth] resolving MODxl01751 Changed to validate LUT version against a value retrieved from the resolving MODxl02056 Changed to calculate Solar Diffuser angles without adjustment for estimated post launch changes in the MODIS orientation relative to incidentally resolving defects MODxl01766 Also resolves MODxl01947 Changed to ignore fill values in SCI_ABNORM and SCI_STATE rather than treating them as resolving MODxl01780 Changed to use spacecraft ancillary data to recognise when the mirror encoder data is being set by side A or side B and to change calculations accordingly This removes the need for seperate LUTs for Side A and Side B data it makes the new LUTs incompatible with older versions of the and vice versa Also resolves MODxl01685 A more robust GRing algorithm is being which will create a non default GRing anytime there s even a single geolocated pixel in a granule Removed obsolete messages from seed as required for compatibility with version of the SDP toolkit Corrected test output file names to end in per delivery and then split off a new MYD_PR03 pcf file for Aqua Added AssociatedPlatformInstrumentSensor to the inventory metadata in MOD01 mcf and MOD03 mcf Created new versions named MYD01 mcf and MYD03 where AssociatedPlatformShortName is rather than Terra The program itself has been changed to read the Satellite Instrument validate it against the input L1A and LUT and to use it determine the correct files to retrieve the ephemeris and attitude data from Changed to produce a LocalGranuleID starting with MYD03 if run on Aqua data Added the Scan Type file attribute to the Geolocation copied from the L1A and attitude_angels to radians rather than degrees The accumulation of Cumulated gflags was moved from GEO_validate_earth_location c to GEO_locate_one_scan c
Definition: HISTORY.txt:464
int i
Definition: decode_rs.h:71
#define OPTION_PARENT
This option only applies if this parser is the top-most parser. Useful for return value documentation...
Definition: argpar.h:164
const char * arg
What to display as the right side of the argument in the usage statement.
Definition: argpar.h:98
Stores the configuration for a par argument.
Definition: argpar.h:87
#define OPTION_DBL
Cast this option as a double. The value and any error will be reflected in the argpar_state struct du...
Definition: argpar.h:156
float p[MODELMAX]
Definition: atrem_corl1.h:131
int group
The group associated with this option. If 0 and a documentation option, it is auto-incremented.
Definition: argpar.h:111