pastebin - collaborative debugging tool
nrubsig.kpaste.net RSS


cpvparser1.c - test code for ksh93 compound variable parsing
Posted by Anonymous on Sat 18th Nov 2023 12:27
raw | new post
modification of post by Anonymous (view diff)

  1. /*
  2.  * cpvparser1.c - test code for ksh93 compound variable parsing
  3.  *
  4.  *
  5.  * It basically reads the output of $ print -v ... # like this:
  6.  * ---- snip ----
  7.  * $ ksh93 -c 'compound c=( va=1 vb=hello ) ; print -v c'
  8.  * (
  9.  *        va=1
  10.  *        vb=hello
  11.  * )
  12.  * ---- snip ----
  13.  *
  14.  * ToDo:
  15.  * - arrays (indexed, sparse indexed and associative)
  16.  * - multibyte characters
  17.  *
  18.  * Written by Roland Mainz <roland.mainz@nrubsig.org>
  19.  */
  20.  
  21. #include <stdlib.h>
  22. #include <stdbool.h>
  23. #include <string.h>
  24. #include <stdio.h>
  25. #include <ctype.h>
  26.  
  27. #define MAX_NAME_VAL_SIZE 1024
  28.  
  29. typedef struct cpv_name_val
  30. {
  31.         const char *cpv_name;
  32.         const char *cpv_value;
  33. } cpv_name_val;
  34.  
  35. /* prototypes */
  36. static int cpv_read_cpv_header(const char **instr_ptr, size_t maxsize);
  37. static void cpv_free_name_val_data(cpv_name_val *cnv);
  38. static int cpv_parse_name_val(const char **instr_ptr, size_t maxsize, cpv_name_val *cpv_nv);
  39.  
  40. static
  41. int cpv_read_cpv_header(const char **instr_ptr, size_t maxsize)
  42. {
  43.         const char *s = *instr_ptr;
  44.  
  45. skipspaces:
  46.         while((*s != '\0') && isspace(*s))
  47.                 s++;
  48.  
  49.         /*
  50.          * skip POSIX-style '#' comments
  51.          * (allowed since this is based on POSIX sh(1) syntax)
  52.          */
  53.         if (*s == '#') {
  54.                 s++;
  55.                 /* ignore everything until the end-of-line */
  56.                 while((*s != '\0') && (*s != '\n'))
  57.                         s++;
  58.                 goto skipspaces;
  59.         }
  60.  
  61.         if (*s == '(') {
  62.                 *instr_ptr=++s;
  63.                 (void)fprintf(stderr, "cpv_read_cpv_header: begin-of-cpv\n");
  64.                 return 0;
  65.         }
  66.  
  67.         (void)fprintf(stderr, "cpv_read_cpv_header: end-of-string, should not happen\n");
  68.         return 1;
  69. }
  70.  
  71. static
  72. int cpv_parse_name_val(const char **instr_ptr, size_t maxsize, cpv_name_val *cpv_nv)
  73. {
  74.         char namebuff[maxsize+1];
  75.         char valbuff[maxsize+1];
  76.  
  77.         const char *s = *instr_ptr;
  78.  
  79.         char *n; /* pointer in |namebuff| */
  80.         char *v; /* pointer in |valbuff| */
  81.  
  82. skipspaces:
  83.         while((*s != '\0') && isspace(*s))
  84.                 s++;
  85.  
  86.         /*
  87.          * skip POSIX-style '#' comments
  88.          * (allowed since this is based on POSIX sh(1) syntax)
  89.          */
  90.         if (*s == '#') {
  91.                 s++;
  92.                 /* ignore everything until the end-of-line */
  93.                 while((*s != '\0') && (*s != '\n'))
  94.                         s++;
  95.                 goto skipspaces;
  96.         }
  97.  
  98.         if (*s == '\0') {
  99.                 (void)fprintf(stderr, "cpv_parse_name_val: "
  100.                         "error: end-of-string, should not happen\n");
  101.                 return 1;
  102.         }
  103.  
  104.         /* cpv == "( foo=bar blabla=text )"*/
  105.         if (*s == ')') {
  106.                 (void)fprintf(stderr, "cpv_parse_name_val: end-of-cpv (OK)\n");
  107.                 return 1;
  108.         }
  109.  
  110. parse_varname:
  111.         /*
  112.          * start parsing variable name
  113.          */
  114.  
  115.         /* variable names MUST start with a letter! */
  116.         if (!isalpha(*s)) {
  117.                 (void)fprintf(stderr,
  118.                         "cpv_parse_name_val: parser error, first char "
  119.                         "in variable name not isalpha(c=%c)\n",
  120.                         *s);
  121.                 return 1;
  122.         }
  123.  
  124.         n = namebuff;
  125.         while((*s != '\0') && isalnum(*s))
  126.                 *n++ = *s++;
  127.         *n = '\0';
  128.  
  129.         /*
  130.          * skip typed member varables
  131.          * (e.g. "typeset ", "typeset -i ", "typeset -l -i2" etc.)
  132.          */
  133.         if (isspace(*s)) {
  134.                 if ((!strcmp(namebuff, "typeset")) ||
  135.                         (!strcmp(namebuff, "integer")) ||
  136.                         (!strcmp(namebuff, "float")) ||
  137.                         (!strcmp(namebuff, "compound"))) {
  138. skip_typeset_options:
  139.                         while(isspace(*s))
  140.                                 s++;
  141.                         if (*s == '-') {
  142.                                 s++;
  143.                                 while(isalnum(*s))
  144.                                         s++;
  145.                                 goto skip_typeset_options;
  146.                         }
  147.  
  148.                         goto parse_varname;
  149.                 }
  150.         }
  151.  
  152.         /* handle '=' */
  153.         if (*s != '=') {
  154.  
  155.                 (void)fprintf(stderr, "cpv_parse_name_val: "
  156.                         "parser error, expected '=', got '%c'.\n",
  157.                         *s);
  158.                 return 1;
  159.         }
  160.  
  161.         s++; /* skip '=' */
  162.  
  163.         /*
  164.          * start parsing variable value
  165.          */
  166.         bool in_doublequotes=false;
  167.         bool in_singlequotes=false;
  168.         v = valbuff;
  169. val_quotes:
  170.         if (in_singlequotes) {
  171.                 while(*s != '\0') {
  172.                         if (*s == '\'') {
  173.                                 in_singlequotes = false;
  174.                                 s++;
  175.                                 goto val_quotes;
  176.                         }
  177.  
  178.                         if ((*s == '\\') && (*(s+1) != '\0')) {
  179.                                 /*
  180.                                  * fixme: should support \ooo octals,
  181.                                  * \u[hex] unicode and \w[hex] wchar
  182.                                  */
  183.                                 s++;
  184.                         }
  185.                         *v++ = *s++;
  186.                 }
  187.         }
  188.         else if (in_doublequotes) {
  189.                 while(*s != '\0') {
  190.                         if (*s == '"') {
  191.                                 in_doublequotes = false;
  192.                                 s++;
  193.                                 goto val_quotes;
  194.                         }
  195.  
  196.                         if ((*s == '\\') && (*(s+1) != '\0')) {
  197.                                 /*
  198.                                  * fixme: should support \ooo octals,
  199.                                  * \u[hex] unicode and \w[hex] wchar
  200.                                  */
  201.                                 s++;
  202.                         }
  203.  
  204.                         *v++ = *s++;
  205.                 }
  206.         }
  207.         else
  208.         {
  209.                 while((*s != '\0') && (!isspace(*s))) {
  210.                         if (*s == '"') {
  211.                                 in_doublequotes = true;
  212.                                 s++;
  213.                                 goto val_quotes;
  214.                         }
  215.  
  216.                         if (*s == '\'') {
  217.                                 in_singlequotes = true;
  218.                                 s++;
  219.                                 goto val_quotes;
  220.                         }
  221.  
  222.                         if ((*s == '\\') && (*(s+1) != '\0')) {
  223.                                 /*
  224.                                  * fixme: should support \ooo octals,
  225.                                  * \u[hex] unicode and \w[hex] wchar
  226.                                  */
  227.                                 s++;
  228.                         }
  229.                         *v++ = *s++;
  230.                 }
  231.         }
  232.  
  233.         if (in_singlequotes) {
  234.                 (void)fprintf(stderr, "cpv_parse_name_val: "
  235.                         "parsererror, still in single quotes "
  236.                         "at the end\n");
  237.                 return 1;
  238.         }
  239.         if (in_doublequotes) {
  240.                 (void)fprintf(stderr, "cpv_parse_name_val: "
  241.                         "parser error, still in double quotes "
  242.                         "at the end\n");
  243.                 return 1;
  244.         }
  245.  
  246.         *v = '\0';
  247.  
  248. #if 0
  249.         (void)printf("cpv_parse_name_val: name='%s', value='%s'\n",
  250.                 namebuff, valbuff);
  251. #endif
  252.  
  253.         cpv_nv->cpv_name   = strdup(namebuff);
  254.         cpv_nv->cpv_value  = strdup(valbuff);
  255.  
  256.         if ((cpv_nv->cpv_name == NULL) || (cpv_nv->cpv_value == NULL)) {
  257.                 cpv_free_name_val_data(cpv_nv);
  258.                 (void)fprintf(stderr, "cpv_parse_name_val: "
  259.                         "parser error, out of memory\n");
  260.                 return 2;
  261.         }
  262.  
  263.         *instr_ptr = s;
  264.  
  265.         return 0;
  266. }
  267.  
  268. void cpv_free_name_val_data(cpv_name_val *cnv)
  269. {
  270.         if (!cnv)
  271.                 return;
  272.  
  273.         free((void *)cnv->cpv_name);
  274.         free((void *)cnv->cpv_value);
  275.         cnv->cpv_name = NULL;
  276.         cnv->cpv_value = NULL;
  277. }
  278.  
  279. int main(int ac, char *av[])
  280. {
  281.         const char *str =
  282.                 " ( \n"
  283.                 "x=y1 chicken=wing xchicken=w\\\"ing qcount=\"one two\" sq1='foo \" x \" bar'"
  284.                 "\n  # a comment x=y\n"
  285.                 "\n"
  286.                 " \n"
  287.                 "\t\n"
  288.                 "emptyval= \n"
  289.                 "emptyval2=\n"
  290.                 "emptyval3=''\n"
  291.                 " emptyval4="" \n"
  292.                 "aftercomment=yep\n"
  293.                 "typeset -i2 bintypedvar=1010\n"
  294.                 "typeset  -u  strtypedvar=UAUA\n"
  295.                 "integer lastvar=666 \n"
  296.                 "\n)";
  297.         const char *s = str;
  298.         int numcnv = 0;
  299.         int i = 0;
  300.         cpv_name_val cnv[256];
  301.  
  302.         (void)fputs("# parsing...\n", stderr);
  303.         if (cpv_read_cpv_header(&s, MAX_NAME_VAL_SIZE)) {
  304.                 (void)fprintf(stderr, "cpv_read_cpv_header failed\n");
  305.                 return EXIT_FAILURE;
  306.         }
  307.  
  308.         for (numcnv=0 ; cpv_parse_name_val(&s, MAX_NAME_VAL_SIZE, &cnv[numcnv]) == 0 ; numcnv++) {
  309.         }
  310.  
  311.         (void)fputs("# data:\n", stderr);
  312.         for (i=0 ; i < numcnv ; i++) {
  313.                 (void)printf("name='%s', value='%s'\n", cnv[i].cpv_name, cnv[i].cpv_value);
  314.         }
  315.  
  316.         (void)fputs("# free memory ...\n", stderr);
  317.         for (i=0 ; i < numcnv ; i++) {
  318.                 cpv_free_name_val_data(&cnv[i]);
  319.         }
  320.  
  321.         (void)fputs("# done.\n", stderr);
  322.  
  323.         return EXIT_SUCCESS;
  324. }

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at