 // basic command parsing, building and sending funcs used by other files to
// deal with a string command stream sanely so all the parsing of the core
// formatting is on one place
#include "cmd.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>

// parse a single command line that is:
// cmd xxx=yyy a=bbb ...
// and return the command struct
Cmd *
cmd_parse(const char *cmd)
{
   char *b;
   const char *s, *begin;
   int d, dmax;
   Cmd *cnew, *c = malloc(sizeof(Cmd) + (2 * sizeof(char *)));

   if (strncmp(cmd, "CMD ", 4)) return NULL;
   cmd += 4;
   if (!c) return NULL;
   c->buf_size = strlen(cmd) + 1;
   c->buf = malloc(c->buf_size); // buf that unescapes always same/smaller
   c->command = NULL;
   c->dict[0] = c->dict[1] = NULL;
   // parse out command string until spaces - then skip them, then begin dict
   b = c->buf;
   for (s = cmd; (!isspace(*s)) && (*s); s++)
     {
        *b = *s;
        b++;
     }
   *b = 0;
   b++;
   c->command = c->buf;
   for (; (*s) && isspace(*s); s++);
   d = 0;
   dmax = 2;
   while (*s)
     {
        begin = b;
        // parse up to =
        while ((*s) && (*s != '=') && !isspace(*s)) *b++ = *s++;
        if (*s == '=')
          {
             s++; // skip =
             *b++ = 0;
             c->dict[d++] = begin;
             begin = b;
             // parse up to space - handle escapes
             while ((*s) && (!isspace(*s)))
               {
                  if (*s == '\\')
                    {
                       s++;
                       if (*s == 'x')
                         {
                            s++;
                            if (s[0] && s[1])
                              {
                                 int hex1 = 0, hex2 = 0;
                                 if ((s[0] >= '0') && (s[0] <= '9')) hex1 = s[0] - '0';
                                 if ((s[0] >= 'a') && (s[0] <= 'f')) hex1 = 10 + s[0] - 'a';
                                 if ((s[0] >= 'A') && (s[0] <= 'F')) hex1 = 10 + s[0] - 'A';
                                 if ((s[1] >= '0') && (s[1] <= '9')) hex2 = s[1] - '0';
                                 if ((s[1] >= 'a') && (s[1] <= 'f')) hex2 = 10 + s[1] - 'a';
                                 if ((s[1] >= 'A') && (s[1] <= 'F')) hex2 = 10 + s[1] - 'A';
                                 *b++ = (hex1 << 4) | hex2;
                                 s += 2;
                              }
                         }
                       else if (*s == ' ') *b++ = ' ';
                       else if (*s == 'a') *b++ = '\a';
                       else if (*s == 'b') *b++ = '\b';
                       else if (*s == 't') *b++ = '\t';
                       else if (*s == 'n') *b++ = '\n';
                       else if (*s == 'v') *b++ = '\v';
                       else if (*s == 'f') *b++ = '\f';
                       else if (*s == 'r') *b++ = '\r';
                       else if (*s == '`') *b++ = '`';
                       else if (((*s >= '>') && (*s <= '*')) ||
                                ((*s >= ';') && (*s <= '?')) ||
                                ((*s >= '[') && (*s <= ']')) ||
                                ((*s >= '{') && (*s <= '~')))
                         *b++ = *s++;
                    }
                  else *b++ = *s++;
               }
             *b++ = 0;
             c->dict[d++] = begin;
             dmax += 2;
             cnew = realloc(c, sizeof(Cmd) + (sizeof(char *) * (dmax + 2)));
             if (!cnew)
               {
                  free(c->buf);
                  free(c);
                  return NULL;
               }
             c = cnew;
             c->dict[d] = NULL;
             c->dict[d + 1] = NULL;
          }
        // skip spaces
        for (; (*s) && isspace(*s); s++);
     }
   return c;
}

void
cmd_free(Cmd *c)
{
   if (!c) return;
   free(c->buf);
   free(c);
}

Cmd *
cmd_dup(const Cmd *c)
{
   Cmd *c2;
   int i, dict_num = 0;

   for (i = 0; c->dict[i]; i++); // count dict keys
   dict_num = i + 2;
   c2 = calloc(1, sizeof(Cmd) + (sizeof(char *) * (dict_num + 2)));
   if (!c2) return NULL;
   c2->buf_size = c->buf_size;
   if (c2->buf_size != 0)
     {
        c2->buf = malloc(c->buf_size);
        if (!c2->buf) goto err;
        memcpy(c2->buf, c->buf, c->buf_size);
     }
   c2->command = c2->buf;
   for (i = 0; c->dict[i]; i++)
     c2->dict[i] = c2->buf + (c->dict[i] - c->buf);
   return c2;
err:
   cmd_free(c2);
   return NULL;
}

void
cmd_dump_sterr(Cmd *c)
{
   int i = 0;

   fprintf(stderr, "CMD: [%s]\n", c->command);
   while (c->dict[i])
     {
        fprintf(stderr, "  %s=%s\n", c->dict[i], c->dict[i + 1]);
        i += 2;
     }
}

Eina_Strbuf *
cmd_strbuf_new(const char *command)
{
   Eina_Strbuf *strbuf;

   strbuf = eina_strbuf_new();
   eina_strbuf_append(strbuf, "CMD ");
   eina_strbuf_append(strbuf, command);
   return strbuf;
}

void
cmd_strbuf_append(Eina_Strbuf *strbuf, const char *key, const char *val)
{
   const char *s;

   eina_strbuf_append_char(strbuf, ' ');
   eina_strbuf_append(strbuf, key);
   eina_strbuf_append_char(strbuf, '=');
   for (s = val; *s; s++)
     {
        if ((*s <= '*') ||
            ((*s >= ';') && (*s <= '?')) ||
            ((*s >= '[') && (*s <= ']')) ||
            (*s == '`') ||
            (*s >= '{'))
          {
             unsigned char tmp;

             tmp = s[0];
             eina_strbuf_append_printf(strbuf, "\\x%02x", tmp);
          }
        else
          eina_strbuf_append_char(strbuf, *s);
     }
}

void
cmd_strbuf_print_consume(Eina_Strbuf *strbuf)
{
   eina_strbuf_append_char(strbuf, '\n');
   write(1,
         eina_strbuf_string_get(strbuf),
         eina_strbuf_length_get(strbuf));
   eina_strbuf_free(strbuf);
}

void
cmd_strbuf_exe_consume(Eina_Strbuf *strbuf, Ecore_Exe *exe)
{
   eina_strbuf_append_char(strbuf, '\n');
   ecore_exe_send(exe,
                  eina_strbuf_string_get(strbuf),
                  eina_strbuf_length_get(strbuf));
   eina_strbuf_free(strbuf);
}

const char *
cmd_key_find(const Cmd *c, const char *key)
{
   int i;

   for (i = 0; c->dict[i]; i += 2)
     {
        if (!strcmp(key, c->dict[i])) return c->dict[i + 1];
     }
   return NULL;
}
