Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_command.c
Go to the documentation of this file.
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * AFW - Adaptive Framework afw Command
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
9 #include "afw.h"
10 #include "afw_command_internal.h"
12 #include <apr_file_io.h>
13 #include <apr_getopt.h>
14 #include <stdio.h>
15 
30 static void
31 impl_print_result(afw_command_self_t *self, const char *format, ...);
32 
33 static void
34 impl_print_result_value(afw_command_self_t *self, const afw_value_t *value);
35 
36 static void
37 impl_print_error(afw_command_self_t *self,
38  const afw_error_t *error, afw_xctx_t *xctx);
39 
40 static void
41 impl_print_end(afw_command_self_t *self);
42 
43 static int
44 impl_octet_get_cb(afw_utf8_octet_t *octet, void *data, afw_xctx_t *xctx);
45 
46 
47 /* Command line options. */
48 static const apr_getopt_option_t opts[] = {
49  /* long-option, short-option, has-arg flag, description. */
50  { "allow", 'a', TRUE, "Content type used to output adaptive values." },
51  { "check", 'k', FALSE, "Parse but don't evaluate." },
52  { "conf", 'f', TRUE, "Configuration file." },
53  { "expression", 'x', TRUE, "The first string to evaluate." },
54  { "extension", 'e', TRUE, "Load extension." },
55  { "help", 'h', FALSE, "Print this help and exit successfully." },
56  { "local", 'l', TRUE, "Run in \"local\" mode with output to path or fd number." },
57  { "syntax", 's', TRUE, "expression_tuple, expression, hybrid, parenthesized, script, template, test_script" },
58  { "type", 't', TRUE, "Content type of configuration file." },
59  { "version", 'v', FALSE, "Print version and exit successfully." },
60  { NULL, 0, 0, NULL }
61 };
62 
64 static const char * impl_additional_help_text =
65  "\n"
66  "The syntax option (-s) determines how input will be parsed:\n"
67  "\n"
68  " -s expression_tuple - The input is an adaptive expression tuple.\n"
69  " -s expression - The input is an adaptive expression.\n"
70  " -s hybrid - The input is an adaptive hybrid.\n"
71  " -s parentheses - The input is an adaptive expression enclose in\n"
72  " parentheses.\n"
73  " -s script - The input is an adaptive script.\n"
74  " -s template - The input is an adaptive template.\n"
75  " -s test_script - The input is an adaptive test script.\n"
76  "\n"
77  "A line ending with \"\\\" will be combined with the line that follows\n"
78  "to form one logical line.\n"
79  "\n"
80  "When option -s expression or -s template is specified, the input is\n"
81  "evaluated one logical line at a time.\n"
82  "\n"
83  "When option -s parentheses is specified, logical lines are evaluated up\n"
84  "up to the expression's closing parenthesis (')'). The parenthesized\n"
85  "expression can be preceded by any amount of whitespace, including\n"
86  "comments, and followed by whitespace up to the end of the line.\n"
87  "\n"
88  "When option -s hybrid is specified and the input does not begin with a\n"
89  "square bracket ('['), the input must be on one logical line; otherwise,\n"
90  "input will be evaluated through the logical line with the hybrid's\n"
91  "closing square bracket (']').\n"
92  "\n"
93  "The conf option (-f) is used to specify the path to a configuration\n"
94  "file. By default, this file should be encoded with a relaxed json\n"
95  "syntax, which accepts standard json, plus allows block comments\n"
96  "(/* ... */), line comments (//), unquoted property names, and several\n"
97  "other conveniences.\n"
99  "\n"
100  "The type option (-t) can be used to specify a content type other than the\n"
101  "default. If the content type is not part of AFW core, use the extension\n"
102  "option (-e) to load the appropriate extension.\n"
103  "\n"
104  "If the -f option is not specified, only core AFW functionality is\n"
105  "available.\n"
106  "\n"
107  "The optional adaptive expression specified by expression option (-x)\n"
108  "is evaluated first. If -x is specified and [IN] is not specified,\n"
109  "afw will end after evaluating the expression.\n"
110  "\n"
111  "If neither -x or [IN] is specified, input is read from stdin.\n"
112  "Each line read from stdin is evaluated as an adaptive expressions\n"
113  "until either a line containing only \"exit\" or end of file is\n"
114  "encountered.\n"
115  "\n"
116  "If [IN] is specified but -s is not, the file is evaluated as an\n"
117  "adaptive script.\n"
118  "\n"
119  "If the -l option is specified, output is written to the path or fd\n"
120  "number specified. Input is always read from stdin so [IN] cannot be\n"
121  "specified.\n"
122  "\n"
123  "When the -l option is specified, input must be, and output will be made\n"
124  "up of one or more segments that are in turn made up of one or more\n"
125  "chunks. Each chunk is preceded with a decimal length of the chunk\n"
126  "followed by a '\\n'. A line containing only \"0\\n\" is used to\n"
127  "indicate the end of a segment. The last input segment must be:\n"
128  "4\\nexit0\\n.\n"
129  "\n"
130  "See the afw command documentation for information about what can be\n"
131  "contained in each segment.\n"
132  "\n"
133  "A simple example using default directives to add 2 plus 2:\n"
134  "\n"
135  " 8\\nadd(2,2)0\\n4\\nexit0\\n\n"
136  "\n"
137  "The output will be:\n"
138  "\n"
139  " 1\\n40\\n\n"
140  "\n"
141 ;
142 
143 
144 int
145 impl_octet_get_cb(afw_utf8_octet_t *octet, void *data, afw_xctx_t *xctx)
146 {
147  afw_command_self_t *self = data;
148  int c;
149 
150  /* Get an octet. */
151  c = fgetc(self->fd_input);
152 
153  /* If not an error, return octet with rv = 0. */
154  if (c >= 0) {
155  *octet = (afw_utf8_octet_t)c;
156  return 0;
157  }
158 
159  /* If there is an error, return 0 for octet and rv = -1. */
160  *octet = 0;
161  return -1;
162 }
163 
164 
165 
166 /* Get input. */
167 static afw_utf8_t *
168 impl_get_input(afw_command_self_t *self, afw_xctx_t *xctx)
169 {
170  int c;
171  char prev_c;
172 
173  /* If eof, just return NULL. */
174  if (self->eof) return NULL;
175 
176  /* Allocate array for input. */
177  if (!self->input_buffer) {
178  self->input_buffer = apr_array_make(
179  afw_pool_get_apr_pool(self->xctx->p),
180  2000, 1);
181  }
182 
183  /* Read input up to a \n that is not preceded by a \ */
184  apr_array_clear(self->input_buffer);
185  for (prev_c = 0; ; prev_c = c) {
186  c = fgetc(self->fd_input);
187  if (c == EOF) {
188  self->eof = true;
189  break;
190  }
191  if (c == '\n' && prev_c == '\\') {
192  apr_array_pop(self->input_buffer);
193  continue;
194  }
195  APR_ARRAY_PUSH(self->input_buffer, unsigned char) = c;
196  if (c == '\n') break;
197  }
198 
199  /* Return resulting string making sure it is NFC utf8. */
200  return (self->input_buffer->nelts == 0)
201  ? NULL
202  : (afw_utf8_t *)afw_utf8_create(self->input_buffer->elts,
203  self->input_buffer->nelts, xctx->p, xctx);
204 }
205 
206 
207 /*
208  * If exit_code is not NULL, the exit_code is returned instead of printing
209  * output.
210  */
211 static afw_boolean_t
212 impl_evaluate(
213  afw_command_self_t *self,
214  const afw_utf8_t *input,
215  int *exit_code)
216 {
217  const afw_value_t *value;
218  const afw_value_t *evaluated_value;
219  afw_xctx_t *xctx;
220  afw_boolean_t error_occurred;
221  afw_boolean_t keep_going;
222  afw_utf8_t *line;
223 
224 
225  xctx = afw_xctx_create(&afw_command_s_afw_command, 0, self->xctx);
226  error_occurred = false;
227  keep_going = true;
228  if (exit_code) {
229  *exit_code = 0;
230  }
231  AFW_TRY {
232 
233  /* Set environment object and qualifier in new xctx. */
234  afw_runtime_xctx_set_object(self->environment_variables_object,
235  true, xctx);
236  afw_xctx_push_qualifier_object(&afw_s_environment,
237  self->environment_variables_object, true, xctx->p, xctx);
238 
239  /* If input is not NULL, insure it is NFC normalized utf-8. */
240  if (input) {
241  input = afw_utf8_create(input->s, input->len, xctx->p, xctx);
242  }
243 
244  /* If input is NULL, get a non-empty input or break if eof. */
245  else if (!self->read_full) {
246  for (;;) {
247  line = impl_get_input(self, xctx);
248  if (!line) {
249  keep_going = false;
250  break;
251  }
252  if (line->len != 0 && !afw_utf8_equal_utf8_z(line, "\n")) {
253  /* If this is a template, remove trailing \n. */
254  if (self->compile_option == afw_compile_type_template ||
255  (self->compile_option == afw_compile_type_hybrid &&
256  *line->s != '['))
257  {
258  if (line->s[line->len - 1] == '\n') {
259  line->len--;
260  }
261  }
262  input = line;
263  break;
264  }
265  impl_print_end(self);
266  }
267  }
268  if (!keep_going) break;
269 
270  /* If input is "exit", just set keep_going to false and break. */
271  if (input) {
272  if (afw_utf8_equal_utf8_z(input, "exit\n") ||
273  afw_utf8_equal_utf8_z(input, "exit"))
274  {
275  keep_going = false;
276  break;
277  }
278  }
279 
280  /* Compile eval and evaluate if not check mode. */
282  self->callback, self->callback_data,
283  self->source_location, self->compile_option,
284  self->residual_check,
285  NULL, NULL, xctx->p, xctx);
286 
287  /*
288  * If option is parenthesized_expression, NULL can be returned if
289  * everything remaining is whitespace/comments. In this case, set
290  * keep_going to false and break.
291  */
292  if (!value) {
293  keep_going = false;
294  break;
295  }
296 
297  /* If not check mode, evaluate expression and print or return result. */
298  if (!self->check_mode) {
299  evaluated_value = afw_value_evaluate(value, xctx->p, xctx);
300 
301  /* If requested, determine exit code. */
302  if (exit_code) {
303  if (!afw_value_is_null(evaluated_value)) {
304  if (!afw_value_is_integer(evaluated_value) ||
305  ((const afw_value_integer_t *)evaluated_value)
306  ->internal < 0 ||
307  ((const afw_value_integer_t *)evaluated_value)
308  ->internal > 255)
309  {
310  *exit_code = EXIT_FAILURE;
311  fprintf(xctx->env->stderr_fd,
312  "Adaptive script run from shell must return "
313  "null or an integer between 0 and 255 but returned "
314  "this instead:\n");
315  impl_print_result_value(self, evaluated_value);
316  }
317  else {
318  *exit_code = (int)
319  ((const afw_value_integer_t *)evaluated_value)
320  ->internal;
321  }
322  }
323  }
324 
325  /* If not requesting exit code, print result. */
326  else {
327  impl_print_result_value(self, evaluated_value);
328  }
329  }
330  }
331 
333  impl_print_error(self, AFW_ERROR_THROWN, xctx);
334  error_occurred = true;
335  }
336 
337  AFW_FINALLY{
338  afw_adaptor_session_commit_and_release_cache(error_occurred, xctx);
339  afw_xctx_release(xctx, xctx);
340  /* Special case: xctx is gone, so return before AFW_ENDTRY. */
341  if (keep_going) impl_print_end(self);
342  return keep_going && !self->terminal_error;
343  }
344 
345  AFW_ENDTRY;
346 
347  /* Fix warning. Should not get to here. See Special case comment above. */
348  return false;
349 }
350 
351 
352 
353 /* Print usage. */
354 static void
355 print_usage(void)
356 {
357  const apr_getopt_option_t *opt;
358  int rv;
359 
360  rv = fprintf(stderr, "Usage: afw [OPTION]... [IN]\n\n");
361  if (rv < 0) exit(EXIT_FAILURE);
362 
363  rv = fprintf(stderr, "\n");
364  if (rv < 0) exit(EXIT_FAILURE);
365 
366  rv = fprintf(stderr, "OPTION:\n");
367  if (rv < 0) exit(EXIT_FAILURE);
368 
369  opt = &opts[0];
370  while (opt->name) {
371  rv = fprintf(stderr, " -%c, --%-10s %s %s\n",
372  opt->optch,
373  opt->name,
374  (opt->has_arg) ? " ARG " : " ",
375  opt->description);
376  if (rv < 0) exit(EXIT_FAILURE);
377  opt++;
378  }
379 
380  rv = fprintf(stderr, "%s", impl_additional_help_text);
381  if (rv < 0) exit(EXIT_FAILURE);
382 
383 }
384 
385 
386 static int
387 process_args_getopt(afw_command_self_t *self, int argc, const char * const *argv,
388  afw_xctx_t *xctx)
389 {
390  apr_getopt_t *os;
391  int option_ch;
392  const char * option_arg;
393  int rv;
394 
395  /* Parse parameters. */
396  if ((apr_getopt_init(&os, afw_pool_get_apr_pool(xctx->p), argc, argv))
397  != APR_SUCCESS)
398  {
399  fprintf(xctx->env->stderr_fd, "apr_getopt_init() error.\n");
400  return EXIT_FAILURE;
401  }
402 
403  self->compile_option = afw_compile_type_error;
404  self->residual_check = afw_compile_residual_check_to_full;
405  self->index_first_non_option = 1;
406  while ((rv = apr_getopt_long(os, opts, &option_ch, &option_arg))
407  == APR_SUCCESS)
408  {
409  self->index_first_non_option = os->ind;
410  switch (option_ch) {
411 
412  case 'a':
413  self->type_out_z = option_arg;
414  self->type_out.len = strlen(self->type_out_z);
415  break;
416 
417  case 'f':
418  self->conf_z = option_arg;
419  self->conf.len = strlen(self->conf_z);
420  break;
421 
422  case 'e':
423  self->extension_z = option_arg;
424  self->extension.len = strlen(self->extension_z);
425  break;
426 
427  case 'k':
428  self->check_mode = true;
429  break;
430 
431  case 'l':
432  self->local_mode_z = option_arg;
433  self->local_mode_out.len = strlen(self->local_mode_z);
434  break;
435 
436  case 's':
437 
438  if (afw_utf8_z_equal(option_arg, "expression_tuple"))
439  {
440  self->compile_option = afw_compile_type_expression_tuple;
441  self->can_span_lines = false;
442  }
443 
444  else if (afw_utf8_z_equal(option_arg, "expression"))
445  {
446  self->compile_option = afw_compile_type_expression;
447  self->can_span_lines = false;
448  }
449 
450  else if (afw_utf8_z_equal(option_arg, "hybrid"))
451  {
452  self->compile_option = afw_compile_type_hybrid;
453  self->can_span_lines = true;
454  }
455 
456  else if (afw_utf8_z_equal(option_arg, "parenthesized"))
457  {
458  self->compile_option = afw_compile_type_parenthesized_expression;
459  self->can_span_lines = true;
460  }
461 
462  else if (afw_utf8_z_equal(option_arg, "script"))
463  {
464  self->compile_option = afw_compile_type_script;
465  self->can_span_lines = false;
466  }
467 
468  else if (afw_utf8_z_equal(option_arg, "template"))
469  {
470  self->compile_option = afw_compile_type_template;
471  self->can_span_lines = false;
472  }
473 
474  else if (afw_utf8_z_equal(option_arg, "test_script"))
475  {
476  self->compile_option = afw_compile_type_test_script;
477  self->can_span_lines = false;
478  }
479  else {
480  rv = fprintf(stderr, "Invalid option --syntax %s\n", option_arg);
481  if (rv < 0) exit(EXIT_FAILURE);
482  print_usage();
483  return EXIT_FAILURE;
484  }
485 
486  break;
487 
488  case 't':
489  self->type_in_z = option_arg;
490  self->type_in.len = strlen(self->type_in_z);
491  break;
492 
493  case 'v':
494  rv = fprintf(stderr, "%s\n", AFW_COMMAND_VERSION_COMMAND_STRING);
495  if (rv < 0) exit(EXIT_FAILURE);
496  self->help_option = true;
497  return EXIT_SUCCESS;
498 
499  case 'x':
500  self->expression_z = option_arg;
501  self->expression.len = strlen(self->expression_z);
502  break;
503 
504  case 'h':
505  self->help_option = true;
506  print_usage();
507  return EXIT_SUCCESS;
508 
509  default:
510  rv = fprintf(stderr, "Error: Invalid command line parameter.\n");
511  if (rv < 0) exit(EXIT_FAILURE);
512  print_usage();
513  return EXIT_FAILURE;
514  }
515  }
516 
517  /* Error if apr_getopt_long() returns other than success. */
518  if (rv != APR_EOF) {
519  fprintf(xctx->env->stderr_fd, "Try --help.\n");
520  return EXIT_FAILURE;
521  }
522 
523  /* Normal return. */
524  return EXIT_SUCCESS;
525 }
526 
527 
528 static int
529 process_args(afw_command_self_t *self, int argc, const char * const *argv,
530  afw_xctx_t *xctx)
531 {
532  int rv;
533 
534  /* Process args with the ones from command line. */
535  rv = process_args_getopt(self, argc, argv, xctx);
536  if (rv != EXIT_SUCCESS) {
537  return rv;
538  }
539 
540  /* Default input type to a relaxed json syntax. */
541  if (!self->type_in_z) {
542  self->type_in.s = "json";
543  }
544  self->type_in.len = strlen(self->type_in.s);
545 
546  /* Default output type to json syntax. */
547  if (!self->type_out_z) {
548  self->type_out.s = "json";
549  }
550  self->type_out.len = strlen(self->type_out.s);
551 
552  /* Can have no more than 1 positional. */
553  if (argc > self->index_first_non_option + 1) {
554  rv = fprintf(stderr, "Error: Can not specify more than one positional parameter.\n");
555  if (rv < 0) exit(EXIT_FAILURE);
556  print_usage();
557  goto error;
558  }
559 
560  /* Set input if [IN] specified. */
561  if (argc == self->index_first_non_option + 1) {
562  self->in_z = argv[self->index_first_non_option];
563  self->in.len = strlen(self->in_z);
564  }
565 
566  /* Can not specify both -l and [IN] */
567  if (self->in_z && self->local_mode_z) {
568  rv = fprintf(stderr, "Error: Can not specify [IN] if -l specified.\n");
569  if (rv < 0) exit(EXIT_FAILURE);
570  print_usage();
571  goto error;
572  }
573 
574  /* Default -s is script if [IN] specified and e if not. */
575  if (self->compile_option == afw_compile_type_error)
576  {
577  self->compile_option = (self->in_z)
578  ? afw_compile_type_script
579  : afw_compile_type_expression;
580  }
581 
582  /* Interactive mode if not -x or [IN]. */
583  if (!self->local_mode_z && !self->expression_z && !self->in_z) {
584  self->interactive_mode = true;
585  }
586 
587  /* Normal return. */
588  return EXIT_SUCCESS;
589 
590  /* Error return. */
591 error:
592  return EXIT_FAILURE;
593 }
594 
595 
596 /* Print result followed by a \n */
597 void
598 impl_print_result(afw_command_self_t *self, const char *format, ...)
599 {
600  va_list ap;
601  int rv;
602 
603  va_start (ap, format);
604  rv = vfprintf(self->fd_output, format, ap);
605  va_end(ap);
606  if (rv < 0) exit(EXIT_FAILURE);
607 
608  rv = fprintf(self->fd_output, "\n");
609  if (rv < 0) exit(EXIT_FAILURE);
610  rv = fflush(self->fd_output);
611  if (rv < 0) exit(EXIT_FAILURE);
612 }
613 
614 
615 
616 /* Print result value. */
617 void impl_print_result_value(afw_command_self_t *self, const afw_value_t *value)
618 {
619  const afw_memory_t *raw;
620  const afw_utf8_t *string;
621 
622  /* JSON does not support undefined values. */
623  if (afw_value_is_undefined(value))
624  {
625  impl_print_result(self,
626  "%" AFW_UTF8_FMT,
627  AFW_UTF8_FMT_ARG(&afw_s_a_undefined_as_string));
628  }
629 
630  /* If value is defined and evaluated, use output content type.*/
631  else if (afw_value_is_defined_and_evaluated(value))
632  {
634  self->content_type_out,
635  value,
637  self->xctx->p, self->xctx);
638  if (raw)
639  {
640  impl_print_result(self, "%.*s",
641  (int)(raw)->size, (const char *)(raw)->ptr);
642  }
643  }
644 
645  /* If defined but not evaluated, return decompiled value. */
646  else
647  {
648  string = afw_value_decompile_to_string(value, NULL,
649  self->xctx->p, self->xctx);
650  impl_print_result(self,
651  "<unevaluated> %" AFW_UTF8_FMT,
652  AFW_UTF8_FMT_ARG(string));
653  }
654 }
655 
656 
657 void
658 impl_print_error(afw_command_self_t *self,
659  const afw_error_t *error, afw_xctx_t *xctx)
660 {
661  int rv;
662 
663  rv = fprintf(xctx->env->stderr_fd, "\n--- Error ---\n");
664  if (rv < 0) exit(EXIT_FAILURE);
665  rv = afw_error_print_with_xctx(xctx->env->stderr_fd, error,
666  xctx->p, xctx);
667  if (rv < 0) exit(EXIT_FAILURE);
668  rv = fprintf(xctx->env->stderr_fd, "\n");
669  if (rv < 0) exit(EXIT_FAILURE);
670  rv = fflush(xctx->env->stderr_fd);
671  if (rv < 0) exit(EXIT_FAILURE);
672 }
673 
674 
675 void
676 impl_print_end(afw_command_self_t *self)
677 {
678  /* Nothing to do. */
679 }
680 
681 
682 int
683 main(int argc, const char * const *argv) {
684  afw_command_self_t *self;
685  const afw_error_t *create_error ;
686  afw_xctx_t *xctx;
687  int rv;
688  const afw_memory_t *conf_file;
689  const afw_value_t *conf;
690  const char *s;
691 
692  /* Create Adaptive Framework environment for command. */
693  AFW_ENVIRONMENT_CREATE(xctx, argc, argv, &create_error);
694  if (!xctx) {
695  afw_error_print(xctx->env->stderr_fd, create_error);
696  return EXIT_FAILURE;
697  }
698 
699  /* stdout except for result goes to stderr.*/
700  afw_environment_set_stdout_fd(stderr, xctx);
701 
702  /* Generated register. */
704 
705  /* Make sure environment is cleaned up. */
706  self = NULL;
707  AFW_TRY{
708 
709  /* Allocate and initialize self. */
711  self->xctx = xctx;
712  self->fd_input = stdin;
713  self->fd_output = stdout;
714  self->source_location = &afw_command_s_afw_command;
715 
716  /* Process arguments. */
717  rv = process_args(self, argc, argv, xctx);
718  if (self->help_option || rv != EXIT_SUCCESS) {
719  break;
720  }
721 
722  /* Create and set environment object and qualifier. */
723  self->environment_variables_object =
725  afw_runtime_xctx_set_object(self->environment_variables_object,
726  true, xctx);
727  afw_xctx_push_qualifier_object(&afw_s_environment,
728  self->environment_variables_object, true, xctx->p, xctx);
729 
730  /* If extension specified, load it. */
731  if (self->extension.len > 0) {
732  afw_environment_load_extension(&self->extension,
733  NULL, NULL, xctx);
734  }
735 
736  /* If conf specified, process it. */
737  if (self->conf.len > 0) {
738 
739  /* Get content type for conf. */
740  self->content_type_in = afw_environment_get_content_type(
741  &self->type_in, xctx);
742  if (!self->content_type_in) {
743  AFW_THROW_ERROR_Z(general, "Invalid content-type.", xctx);
744  }
745 
746  /* Load conf as a list of conf objects. */
747  conf_file = afw_file_to_memory(&self->conf, 0, xctx->p, xctx);
748  conf = afw_content_type_raw_to_value(self->content_type_in,
749  conf_file, &self->conf, xctx->p, xctx);
750  if (!afw_value_is_list(conf)) {
751  AFW_THROW_ERROR_Z(general, "Invalid configuration.",
752  xctx);
753  }
755  ((const afw_value_list_t *)conf)->internal,
756  &self->conf, xctx);
757  }
758 
759  /* Get output content type. */
760  self->content_type_out = afw_environment_get_content_type(
761  &self->type_out, xctx);
762  if (!self->content_type_out) {
763  AFW_THROW_ERROR_Z(general, "Invalid --allow content-type.", xctx);
764  }
765 
766  /* If expression specified, evaluate it. */
767  if (self->expression_z) {
768  self->source_location = &afw_command_s_afw_command_dash_x;
769  impl_evaluate(self, &self->expression, NULL);
770  self->source_location = &afw_command_s_afw_command;
771  }
772 
773  /* If [IN] specified, read from file instead of stdin. */
774  if (self->in_z) {
775  self->source_location = &self->in;
776  self->fd_input = fopen(self->in_z, "r");
777  if (!self->fd_input) {
778  fprintf(xctx->env->stderr_fd,
779  "Error opening input %s errno %d - %s\n",
780  self->in_z, errno, strerror(errno));
781  return -1;
782  }
783  }
784 
785  /* If local mode specified, open output. */
786  if (self->local_mode_z) {
787  self->source_location = &afw_command_s_afw_command_local_mode;
788  for (s = self->local_mode_z; *s && *s >= '0' && *s <= '9'; s++);
789  if (*s == 0)
790  {
791  rv = atoi(self->local_mode_z);
792  if (rv > 0) {
793  self->fd_output = fdopen(rv, "w");
794  }
795  }
796  else {
797  self->fd_output = fopen(self->local_mode_z, "w");
798  }
799  if (!self->fd_output) {
800  fprintf(xctx->env->stderr_fd,
801  "Error opening -l path %s errno %d - %s\n",
802  self->local_mode_z, errno, strerror(errno));
803  return -1;
804  }
805  }
806 
807  if (self->interactive_mode) {
808  self->source_location = &afw_command_s_afw_command_interactive;
809  }
810 
811  /* If script, only evaluate once. */
812  if (self->compile_option == afw_compile_type_script ||
813  self->compile_option == afw_compile_type_test_script)
814  {
815  self->read_full = true;
816  self->callback = impl_octet_get_cb;
817  self->callback_data = self;
818  self->residual_check = afw_compile_residual_check_to_full;
819  if (self->compile_option == afw_compile_type_script) {
820  impl_evaluate(self, NULL, &rv);
821  }
822  else {
823  impl_evaluate(self, NULL, NULL);
824  rv = 0;
825  }
826  }
827 
828  /* If local mode, start local server and run. */
829  else if (self->local_mode_z) {
830  self->local_server = afw_command_local_server_create(self);
831  afw_server_run(self->local_server, NULL, xctx);
832  }
833 
834  /*
835  * If local or interactive mode, loop evaluating expressions until
836  * exit.
837  */
838  else if (self->in_z || self->interactive_mode) {
839  if (self->can_span_lines) {
840  self->callback = impl_octet_get_cb;
841  self->callback_data = self;
842  self->residual_check = afw_compile_residual_check_to_newline;
843  }
844  if (self->interactive_mode) {
845  impl_print_result(self,
846  "afw " AFW_VERSION_STRING "\n\n"
847  "Input will be evaluated as entered. "
848  "Type exit to end.\n");
849  }
850  impl_print_end(self);
851 
852  while (impl_evaluate(self, NULL, NULL));
853  }
854  }
855 
856  /* Print any unhandled errors. */
858  impl_print_error(self, AFW_ERROR_THROWN, xctx);
859  impl_print_end(self);
860  rv = EXIT_FAILURE;
861  }
862 
863  /* Make sure things are cleaned up. */
864  AFW_FINALLY{
865 
866  /* If [IN] specified and file open, close it. */
867  if (self && self->fd_input && self->in_z) {
868  fclose(self->fd_input);
869  }
870 
871  /* Release enviornment. */
873 
874  /* Special case: xctx is gone, so return before AFW_ENDTRY. */
875  return rv;
876  }
877 
878  AFW_ENDTRY;
879 }
880 
Adaptive Framework Core API.
void afw_command_generated_register(afw_xctx_t *xctx)
Generated register for afw_command.
Adaptive Framework afw command internal header.
Implementation for interface afw_command_local.
afw_adaptor_session_commit_and_release_cache(afw_boolean_t abort, afw_xctx_t *xctx)
Commit/Abort changes and release cached sessions and objects.
Definition: afw_adaptor.c:391
#define afw_value_is_integer(A_VALUE)
Macro to determine if value is evaluated integer.
#define afw_value_is_list(A_VALUE)
Macro to determine if value is evaluated list.
#define afw_value_is_null(A_VALUE)
Macro to determine if value is evaluated null.
afw_command_local_server_create(afw_command_self_t *command_self)
#define AFW_UTF8_FMT_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify arg.
Definition: afw_common.h:605
_Bool afw_boolean_t
Definition: afw_common.h:373
#define AFW_UTF8_FMT
Format string specifier used for afw_utf8_t.
Definition: afw_common.h:588
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
Definition: afw_common.h:236
afw_compile_to_value_with_callback(const afw_utf8_t *string, afw_utf8_octet_get_cb_t callback, void *callback_data, const afw_utf8_t *source_location, afw_compile_type_t compile_type, afw_compile_residual_check_t residual_check, const afw_value_compiled_value_t *parent, const afw_compile_shared_t *shared, const afw_pool_t *p, afw_xctx_t *xctx)
Compile string to adaptive value with callback.
Definition: afw_compile.c:140
#define afw_content_type_raw_to_value(instance, raw, source_location, p, xctx)
Call method raw_to_value of interface afw_content_type.
afw_content_type_value_to_raw(const afw_content_type_t *instance, const afw_value_t *value, const afw_object_options_t *options, const afw_pool_t *p, afw_xctx_t *xctx)
Convert value to the raw in specified pool.
afw_environment_set_stdout_fd(FILE *fd, afw_xctx_t *xctx)
Override fd used for stdout.
afw_environment_load_extension(const afw_utf8_t *extension_id, const afw_utf8_t *module_path, const afw_object_t *properties, afw_xctx_t *xctx)
Load and initialize environment extension.
#define AFW_ENVIRONMENT_CREATE(xctx, argc, argv, environment_create_error)
Call afw_environment_create() supplying version compiled with.
const afw_content_type_t * afw_environment_get_content_type(const afw_utf8_t *type, afw_xctx_t *xctx)
Get the afw_content_type struct associated with a content type.
afw_environment_release(afw_xctx_t *xctx)
Create the Adaptive Framework core environment and return base xctx.
void afw_environment_configure_with_object_list(const afw_list_t *entry_list, const afw_utf8_t *source_location, afw_xctx_t *xctx)
Configure environment with list of configuration entries.
const afw_object_t * afw_environment_create_environment_variables_object(afw_boolean_t preload_variables, afw_xctx_t *xctx)
Create a readonly object for accessing environment variables.
#define AFW_FINALLY
Always executed regardless of error.
Definition: afw_error.h:702
afw_error_print_with_xctx(FILE *fp, const afw_error_t *error, const afw_pool_t *p, afw_xctx_t *xctx)
Print error when xctx is available.
Definition: afw_error.c:783
#define AFW_CATCH_UNHANDLED
Catch an unhandled error that occurs in a AFW_TRY block.
Definition: afw_error.h:684
afw_error_print(FILE *fp, const afw_error_t *error)
Print error.
Definition: afw_error.c:679
#define AFW_ENDTRY
Ends an AFW try block.
Definition: afw_error.h:727
#define AFW_TRY
Begin an AFW TRY block.
Definition: afw_error.h:634
#define AFW_ERROR_THROWN
Access the thrown error. See AFW_TRY.
Definition: afw_error.h:554
#define AFW_THROW_ERROR_Z(code, message_z, xctx)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:283
afw_file_to_memory(const afw_utf8_t *file_path, apr_size_t file_size, const afw_pool_t *p, afw_xctx_t *xctx)
Read a file into a memory in a specifed pool.
Definition: afw_file.c:71
afw_object_options_whitespace
Whitespace only.
#define afw_pool_get_apr_pool(instance)
Call method get_apr_pool of interface afw_pool.
afw_runtime_xctx_set_object(const afw_object_t *object, afw_boolean_t overwrite, afw_xctx_t *xctx)
Set an object pointer in the xctx's runtime objects.
Definition: afw_runtime.c:292
#define afw_server_run(instance, handler, xctx)
Call method run of interface afw_server.
afw_boolean_t afw_utf8_equal_utf8_z(const afw_utf8_t *s1, const afw_utf8_z_t *s2_z)
Check to see if a string equals a utf8_z string.
afw_boolean_t afw_utf8_z_equal(const afw_utf8_z_t *s1, const afw_utf8_z_t *s2)
Definition: afw_utf8.h:799
#define afw_utf8_create(s, len, p, xctx)
Create utf-8 string without copy unless necessary in pool specified.
Definition: afw_utf8.h:239
#define afw_value_evaluate(value, p, xctx)
Evaluate value if needed using specific pool.
Definition: afw_value.h:841
const afw_utf8_t * afw_value_decompile_to_string(const afw_value_t *value, const afw_utf8_t *tab, const afw_pool_t *p, afw_xctx_t *xctx)
Decompile a value to a string.
#define afw_value_is_defined_and_evaluated(A_VALUE)
Macro to determine if value is defined and evaluated.
Definition: afw_value.h:481
#define afw_value_is_undefined(A_VALUE)
Determine if value is undefined.
Definition: afw_value.h:438
#define afw_xctx_release(instance, xctx)
Call method release of interface afw_xctx.
afw_xctx_push_qualifier_object(const afw_utf8_t *qualifier_name, const afw_object_t *qualifier_object, afw_boolean_t secure, const afw_pool_t *p, afw_xctx_t *xctx)
Push qualifier object on to stack.
Definition: afw_xctx.c:406
afw_xctx_create(const afw_utf8_t *name, afw_integer_t number, afw_xctx_t *xctx)
Create an Adaptive Framework xctx.
Definition: afw_xctx.c:145
#define afw_xctx_calloc_type(type, xctx)
Macro to allocate cleared memory to hold type in xctx's pool.
Definition: afw_xctx.h:199
Self typedef for afw_command self.
FILE * stderr_fd
Open file descriptor used for writing error output. Default stderr.
Definition: afw_common.h:1425
Adaptive Framework Error.
Definition: afw_error.h:65
Struct for memory pointer and size.
Definition: afw_common.h:505
NFC normalized UTF-8 string.
Definition: afw_common.h:545
struct for data type integer values.
struct for data type list values.
Interface afw_value public struct.
Interface afw_xctx public struct.