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:05
raw | new post
view followups (newest first): cpvparser1.c - test code for ksh93 compound variable parsing by Anonymous
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_cpy_header(const char **instr_ptr);
  37. static void cpv_free_name_val_data(cpv_name_val *cnv);
  38. static int cpv_parse_name_val(const char **instr_ptr, cpv_name_val *cpv_nv);
  39.  
  40. static
  41. int cpv_read_cpy_header(const char **instr_ptr)
  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, "begin-of-cpv\n");
  64.                 return 0;
  65.         }
  66.  
  67.         (void)fprintf(stderr, "cpv_read_cpy_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, cpv_name_val *cpv_nv)
  73. {
  74.         char namebuff[MAX_NAME_VAL_SIZE];
  75.         char valbuff[MAX_NAME_VAL_SIZE];
  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, "end-of-string, should not happen\n");
  100.                 return 1;
  101.         }
  102.  
  103.         /* cpv == "( foo=bar blabla=text )"*/
  104.         if (*s == ')') {
  105.                 (void)fprintf(stderr, "end-of-cpv\n");
  106.                 return 1;
  107.         }
  108.  
  109. parse_varname:
  110.         /*
  111.          * start parsing variable name
  112.          */
  113.  
  114.         /* variable names MUST start with a letter! */
  115.         if (!isalpha(*s)) {
  116.                 (void)fprintf(stderr,
  117.                         "parser error, first char in variable name "
  118.                         "not isalpha(c=%c)\n",
  119.                         *s);
  120.                 return 1;
  121.         }
  122.  
  123.         n = namebuff;
  124.         while((*s != '\0') && isalnum(*s))
  125.                 *n++ = *s++;
  126.         *n = '\0';
  127.  
  128.         /*
  129.          * skip typed member varables
  130.          * (e.g. "typeset ", "typeset -i ", "typeset -l -i2" etc.)
  131.          */
  132.         if (isspace(*s)) {
  133.                 if ((!strcmp(namebuff, "typeset")) ||
  134.                         (!strcmp(namebuff, "integer")) ||
  135.                         (!strcmp(namebuff, "float")) ||
  136.                         (!strcmp(namebuff, "compound"))) {
  137. skip_typeset_options:
  138.                         while(isspace(*s))
  139.                                 s++;
  140.                         if (*s == '-') {
  141.                                 s++;
  142.                                 while(isalnum(*s))
  143.                                         s++;
  144.                                 goto skip_typeset_options;
  145.                         }
  146.  
  147.                         goto parse_varname;
  148.                 }
  149.         }
  150.  
  151.         /* handle '=' */
  152.         if (*s != '=') {
  153.  
  154.                 (void)fprintf(stderr, "parser error, expected '=', got '%c'.\n",
  155.                         *s);
  156.                 return 1;
  157.         }
  158.  
  159.         s++; /* skip '=' */
  160.  
  161.         /*
  162.          * start parsing variable value
  163.          */
  164.         bool in_doublequotes=false;
  165.         bool in_singlequotes=false;
  166.         v = valbuff;
  167. val_quotes:
  168.         if (in_singlequotes) {
  169.                 while(*s != '\0') {
  170.                         if (*s == '\'') {
  171.                                 in_singlequotes = false;
  172.                                 s++;
  173.                                 goto val_quotes;
  174.                         }
  175.  
  176.                         if ((*s == '\\') && (*(s+1) != '\0')) {
  177.                                 /*
  178.                                  * fixme: should support \ooo octals,
  179.                                  * \u[hex] unicode and \w[hex] wchar
  180.                                  */
  181.                                 s++;
  182.                         }
  183.                         *v++ = *s++;
  184.                 }
  185.         }
  186.         else if (in_doublequotes) {
  187.                 while(*s != '\0') {
  188.                         if (*s == '"') {
  189.                                 in_doublequotes = false;
  190.                                 s++;
  191.                                 goto val_quotes;
  192.                         }
  193.  
  194.                         if ((*s == '\\') && (*(s+1) != '\0')) {
  195.                                 /*
  196.                                  * fixme: should support \ooo octals,
  197.                                  * \u[hex] unicode and \w[hex] wchar
  198.                                  */
  199.                                 s++;
  200.                         }
  201.  
  202.                         *v++ = *s++;
  203.                 }
  204.         }
  205.         else
  206.         {
  207.                 while((*s != '\0') && (!isspace(*s))) {
  208.                         if (*s == '"') {
  209.                                 in_doublequotes = true;
  210.                                 s++;
  211.                                 goto val_quotes;
  212.                         }
  213.  
  214.                         if (*s == '\'') {
  215.                                 in_singlequotes = true;
  216.                                 s++;
  217.                                 goto val_quotes;
  218.                         }
  219.  
  220.                         if ((*s == '\\') && (*(s+1) != '\0')) {
  221.                                 /*
  222.                                  * fixme: should support \ooo octals,
  223.                                  * \u[hex] unicode and \w[hex] wchar
  224.                                  */
  225.                                 s++;
  226.                         }
  227.                         *v++ = *s++;
  228.                 }
  229.         }
  230.  
  231.         if (in_singlequotes) {
  232.                 (void)fprintf(stderr, "parser error, still in single quotes at the end\n");
  233.                 return 1;
  234.         }
  235.         if (in_doublequotes) {
  236.                 (void)fprintf(stderr, "parser error, still in double quotes at the end\n");
  237.                 return 1;
  238.         }
  239.  
  240.         *v = '\0';
  241.  
  242. //      (void)printf("name='%s', value='%s'\n", namebuff, valbuff);
  243.  
  244.         cpv_nv->cpv_name   = strdup(namebuff);
  245.         cpv_nv->cpv_value  = strdup(valbuff);
  246.  
  247.         if ((cpv_nv->cpv_name == NULL) || (cpv_nv->cpv_value == NULL)) {
  248.                 cpv_free_name_val_data(cpv_nv);
  249.                 (void)fprintf(stderr, "parser error, out of memory\n");
  250.                 return 2;
  251.         }
  252.  
  253.         *instr_ptr = s;
  254.  
  255.         return 0;
  256. }
  257.  
  258. void cpv_free_name_val_data(cpv_name_val *cnv)
  259. {
  260.         if (!cnv)
  261.                 return;
  262.  
  263.         free((void *)cnv->cpv_name);
  264.         free((void *)cnv->cpv_value);
  265.         cnv->cpv_name = NULL;
  266.         cnv->cpv_value = NULL;
  267. }
  268.  
  269. int main(int ac, char *av[])
  270. {
  271.         const char *str =
  272.                 " ( \n"
  273.                 "x=y1 chicken=wing xchicken=w\\\"ing qcount=\"one two\" sq1='foo \" x \" bar'"
  274.                 "\n  # a comment x=y\n"
  275.                 "\n"
  276.                 " \n"
  277.                 "\t\n"
  278.                 "emptyval= \n"
  279.                 "emptyval2=\n"
  280.                 "emptyval3=''\n"
  281.                 " emptyval4="" \n"
  282.                 "aftercomment=yep\n"
  283.                 "typeset -i2 bintypedvar=1010\n"
  284.                 "typeset  -u  strtypedvar=UAUA\n"
  285.                 "integer lastvar=666 \n"
  286.                 "\n)";
  287.         const char *s = str;
  288.         int numcnv = 0;
  289.         int i = 0;
  290.         cpv_name_val cnv[256];
  291.  
  292.         (void)fputs("# parsing...\n", stderr);
  293.         if (cpv_read_cpy_header(&s)) {
  294.                 (void)fprintf(stderr, "cpv_read_cpy_header failed\n");
  295.                 return EXIT_FAILURE;
  296.         }
  297.  
  298.         for (numcnv=0 ; cpv_parse_name_val(&s, &cnv[numcnv]) == 0 ; numcnv++) {
  299.         }
  300.  
  301.         (void)fputs("# data:\n", stderr);
  302.         for (i=0 ; i < numcnv ; i++) {
  303.                 (void)printf("name='%s', value='%s'\n", cnv[i].cpv_name, cnv[i].cpv_value);
  304.         }
  305.  
  306.         (void)fputs("# free memory ...\n", stderr);
  307.         for (i=0 ; i < numcnv ; i++) {
  308.                 cpv_free_name_val_data(&cnv[i]);
  309.         }
  310.  
  311.         (void)fputs("# done.\n", stderr);
  312.  
  313.         return EXIT_SUCCESS;
  314. }

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