Logo Search packages:      
Sourcecode: ledcontrol version File versions  Download package

parse.c

/*
 * Parses the command from the pipe.
 */

#include "ledd.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>



/* Helper functions */
static gchar *parse_getnumber(gchar *str,gint *n);
static gchar *parse_getfloat(gchar *str,gfloat *n);

static GSList *parse_function_opts(gchar *string,gchar **end,gchar **cmd);



/* Functions for actual doing of stuff. */
static gboolean parse_set_constant(options *opts, gint type,
                           GSList *arglist,gchar *string);
static gboolean parse_set_blink(options *opts, gint type,
                        GSList *arglist,gchar *string);
static gboolean parse_set_frequency(options *opts, gint type,
                            GSList *arglist,gchar *string);
static gboolean parse_set_dutycycle(options *opts, gint type,
                            GSList *arglist,gchar *string);
static gboolean parse_anim(options *opts, gint type,
                     GSList *arglist, gchar *string);
static gboolean parse_nop(options *opts, gint type,
                    GSList *arglist, gchar *string);

/* Helper functions for those: */
static GSList *parse_ledlist(options *opts, gchar *str);
static GSList *parse_blinkopts(gchar *str);
static GSList *parse_make_dutycycle(gint total, gfloat min, gfloat max,
                            gfloat set);
static GSList *parse_make_frequency(gfloat min, gint freq1,
                            gfloat max, gint freq2, gfloat value);



/* Function list typedefinition */

/* MAXCMDPIECES is the maximum amount of elements allowed in cmd.
 * If you need more, feel free to enlarge it. It is used only below
 * in the definition. */
#define MAXCMDPIECES 16

typedef struct {
      gchar *cmd[MAXCMDPIECES];
      gint type;
      gint (*function)(options *, gint, GSList *, gchar *);
} function_list;

/*
 * List of the functions.
 * These are gone through in order (from the top down). When a match is
 * found, the function given is called and no further checks are done.
 *
 * First argument is a list of strings corresponding to the commands to
 * be found. Everything is taken literally (but case insensitively),
 * except lines beginning with '%':
 *     %s   String, ended by a space (data in GSList is gchar *).
 *     %d   Integer, both positive and negative accepted (gint *).
 *     %f   Float, positive and negative accepted (gfloat *).
 *     %.   End of line. If any options follow, line is rejected.
 * The last item MUST be NULL (also if you have a "%." as the last opt).
 *
 * The second value is passed to the function in the second argument.
 * It is so that you can use one function for several purposes.
 *
 * The third value is the function to be called. This function will be
 * given as arguments the options, the second argument of the list (type),
 * the list of arguments gathered from the %x's and the remaining list
 * of arguments (in that order). The function should not free anything
 * in the list and should return FALSE in case of bad arguments, otherwise
 * TRUE.
 *
 * The whole list is ended in a line full of NULL's or 0's.
 */
static function_list parse_function_list[]={
      {{"set","%s","on","%.",NULL},
       LEDTYPE_ON,parse_set_constant},
      {{"set","%s","off","%.",NULL},
       LEDTYPE_OFF,parse_set_constant},
      {{"set","%s","normal","%.",NULL},
       LEDTYPE_NORMAL,parse_set_constant},
      {{"set","%s","blink",NULL},
       0,parse_set_blink},
      /* Accept a few different spellings... */
      {{"set","%s","dutycycle","%d","%f","%f","%f","%.",NULL},
       0,parse_set_dutycycle},
      {{"set","%s","duty_cycle","%d","%f","%f","%f","%.",NULL},
       0,parse_set_dutycycle},
      {{"set","%s","duty-cycle","%d","%f","%f","%f","%.",NULL},
       0,parse_set_dutycycle},
      {{"set","%s","duty","cycle","%d","%f","%f","%f","%.",NULL},
       0,parse_set_dutycycle},
      {{"set","%s","frequency","%f","%d","%f","%d","%f","%.",NULL},
       0,parse_set_frequency},
      {{"anim",NULL},
       0,parse_anim},
      {{"nop",NULL},
       0,parse_nop},
      {{NULL},0,NULL}
};





/*
 * Parses a command that is coming in from the pipe.
 */


void parse_pipe_command(options *opts,gchar *string,File *src) {
      gchar *str;
      gint n;
      GSList *list;
      gchar *type;

      /* For error messages: */
      if (src->type==TYPE_SCRIPT)
            type="script";
      else if (src->type==TYPE_PIPE)
            type="pipe";
      else
            type="boogie-woogie";
      
      str=parse_jumpspace(string);
      /* Accept empty strings without a hassle. */
      if (str[0]==0)
            return;

      /* Search for a match. */
      for (n=0, list=NULL; parse_function_list[n].cmd[0]; n++) {
            list=parse_function_opts(string,&str,
                               parse_function_list[n].cmd);
            if (list!=NULL)
                  break;
      }
      
      if (parse_function_list[n].cmd[0]==NULL) {
            /* No match was found. */
            g_warning("error parsing command \"%s\" from %s %s",
                    string,type,src->name);
            return;
      }

      /* We have a match, number n. Call it. */
      if (parse_function_list[n].function(opts,parse_function_list[n].type,
                                  list,str)==FALSE) {
            /* Function failed. */
            g_warning("error parsing command \"%s\" from %s %s",
                    string,type,src->name);
            gslist_free_all(list);
            return;
      }

      /* We're done. */
      gslist_free_all(list);
      
      return;
}



/*
 * Parses a command list and makes a list of the appropriate pieces of
 * string. Returns NULL on error (string doesn't match cmd).
 */
static GSList *parse_function_opts(gchar *string,gchar **end,gchar **cmd) {
      gint i;
      gchar buf[MAXPIECELENGTH];
      gchar *str;
      gint *d;
      gfloat *f;
      GSList *list=NULL;   /* This way the first one is always filled too. */

      str=string;
      for (i=0; cmd[i]!=NULL; i++) {
            if (cmd[i][0]=='%') {
                  switch (cmd[i][1]) {
                  case 's':
                        str=parse_getpiece(str,buf);
                        if (str==NULL) {
                              gslist_free_all(list);
                              return NULL;
                        }
                        list=g_slist_append(list,g_strdup(buf));
                        break;
                  case 'd':
                        d=g_new(gint,1);
                        str=parse_getnumber(str,d);
                        if (str==NULL) {
                              gslist_free_all(list);
                              g_free(d);
                              return NULL;
                        }
                        list=g_slist_append(list,d);
                        break;
                  case 'f':
                        f=g_new(gfloat,1);
                        str=parse_getfloat(str,f);
                        if (str==NULL) {
                              gslist_free_all(list);
                              g_free(f);
                              return NULL;
                        }
                        list=g_slist_append(list,f);
                        break;
                  case '.':
                        str=parse_jumpspace(str);
                        if (str[0]) {
                              gslist_free_all(list);
                              return NULL;
                        }
                        break;
                  default:
                        G_ERROR("Internal bug!!! Testing in "
                              "parse_function_opts for \"%s\"!\n",
                              cmd[i]);
                        break;
                  }
            } else {
                  /* Check text matching. */
                  str=parse_getpiece(str,buf);
                  if (str==NULL ||
                      strcasecmp(buf,cmd[i])!=0) {
                        gslist_free_all(list);
                        return NULL;
                  }
            }                 
      }

      /* We're successful. */
      *end=str;   /* Mark the ending point. */

      /* Check for no argument requested. */
      if (list==NULL)
            return g_slist_alloc();

      return list;
}






/*
 * The functions that actually DO something...
 */


/*
 * Set a constant setting (in type, value is one of LEDTYPE_xxx) to the
 * list of LEDs in the first (and only) argument (list->data).
 */
static gboolean parse_set_constant(options *opts, gint type, GSList *arglist,
                           gchar *string) {
      GSList *ledlist;
      GSList *led;
      LedControl *cnt;

      ledlist=parse_ledlist(opts,(gchar *)arglist->data);
      if (ledlist==NULL)
            return FALSE;

      for (led=ledlist; led; led=g_slist_next(led)) {
            if (led->data==NULL)
                  continue;
            cnt=(LedControl *)led->data;
            led_free(cnt);
            cnt->type=type;   /* LED setting is in type. */
      }
      g_slist_free(ledlist);

      return TRUE;
}


/*
 * Set a "blink" setting. Blink sequence is totally parsed from string.
 * Only argument already parsed is list of LEDs to set.
 */
static gboolean parse_set_blink(options *opts, gint type, GSList *arglist,
                        gchar *string) {
      GSList *list;
      GSList *ledlist;
      GSList *led;
      LedControl *cnt;

      ledlist=parse_ledlist(opts,(gchar *)arglist->data);
      if (ledlist==NULL)
            return FALSE;

      list=parse_blinkopts(string);
      if (list==NULL) {
            g_slist_free(ledlist);
            return FALSE;
      }

      for (led=ledlist; led; led=g_slist_next(led)) {
            if (led->data==NULL)
                  continue;
            cnt=(LedControl *)led->data;
            led_free(cnt);

            /* Set the type */
            cnt->type=LEDTYPE_BLINK;

            /* Start the timer... */
            cnt->timer=g_timer_new();
            g_timer_start(cnt->timer);

            cnt->mode=TRUE;

            /* Copy the list */
            cnt->blink=g_slist_copy(list);
      }
      g_slist_free(list);

      return TRUE;
}


/*
 * Makes a dutycycle setting. Read the MANUAL on how it works.
 * Arguments are already in arglist (LEDs total-time min max set)
 */
static gboolean parse_set_dutycycle(options *opts, gint type,
                            GSList *arglist,gchar *string) {
      GSList *ledlist;
      GSList *led;
      LedControl *cnt;
      GSList *list;
      gint total;
      gfloat min,max,set;
      LedType ledtype;

      /* Parse leds */
      ledlist=parse_ledlist(opts,(gchar *)arglist->data);
      if (ledlist==NULL)
            return FALSE;

      /* No segfault _should_ be able to happen here...
       * All the arguments _should_ be there... */
      total=*(gint *)g_slist_nth_data(arglist,1);
      min=*(gfloat *)g_slist_nth_data(arglist,2);
      max=*(gfloat *)g_slist_nth_data(arglist,3);
      set=*(gfloat *)g_slist_nth_data(arglist,4);

      list=parse_make_dutycycle(total,min,max,set);

      /* Check return */
      if (list==NULL) {
            g_slist_free(ledlist);
            return FALSE;
      }
      if (((gint)list)<0) {
            ledtype=-(gint)list;
            list=NULL;
      } else {
            ledtype=LEDTYPE_BLINK;
      }

      for (led=ledlist; led; led=g_slist_next(led)) {
            if (led->data==NULL)
                  continue;
            cnt=(LedControl *)led->data;
            led_free(cnt);

            /* Set the type */
            cnt->type=ledtype;

            /* First on */
            cnt->mode=TRUE;

            if (ledtype==LEDTYPE_BLINK) {
                  /* Start the timer... */
                  cnt->timer=g_timer_new();
                  g_timer_start(cnt->timer);

                  /* Copy the list */
                  cnt->blink=g_slist_copy(list);
            }
      }
      g_slist_free(list);   /* Accepts NULL gracefully */

      return TRUE;
}


/*
 * Makes a frequency setting. Read the MANUAL on how it works.
 * Arguments are already in arglist (LEDs min freq1 max freq2 value)
 */
static gboolean parse_set_frequency(options *opts, gint type,
                            GSList *arglist,gchar *string) {
      GSList *ledlist;
      GSList *led;
      LedControl *cnt;
      GSList *list;
      gint freq1, freq2;
      gfloat min,max,set;
      LedType ledtype;

      /* Parse leds */
      ledlist=parse_ledlist(opts,(gchar *)arglist->data);
      if (ledlist==NULL)
            return FALSE;

      /* No segfault _should_ be able to happen here...
       * All the arguments _should_ be there... */
      min=*(gfloat *)g_slist_nth_data(arglist,1);
      freq1=*(gint *)g_slist_nth_data(arglist,2);
      max=*(gfloat *)g_slist_nth_data(arglist,3);
      freq2=*(gint *)g_slist_nth_data(arglist,4);
      set=*(gfloat *)g_slist_nth_data(arglist,5);

      list=parse_make_frequency(min,freq1,max,freq2,set);

      /* Check return */
      if (list==NULL) {
            g_slist_free(ledlist);
            return FALSE;
      }
      if (((gint)list)<0) {
            ledtype=-(gint)list;
            list=NULL;
      } else {
            ledtype=LEDTYPE_BLINK;
      }

      for (led=ledlist; led; led=g_slist_next(led)) {
            if (led->data==NULL)
                  continue;
            cnt=(LedControl *)led->data;
            led_free(cnt);
            
            /* Set the type */
            cnt->type=ledtype;

            /* First on */
            cnt->mode=TRUE;
            
            if (ledtype==LEDTYPE_BLINK) {
                  /* Start the timer... */
                  cnt->timer=g_timer_new();
                  g_timer_start(cnt->timer);
                  
                  /* Copy the list */
                  cnt->blink=g_slist_copy(list);
            }
      }
      g_slist_free(list);   /* Accepts NULL gracefully */
      
      return TRUE;
}


/*
 * Parses a space-separated list of ncsNCSx and numbers into
 * a list of AnimActions types.
 */
static gboolean parse_anim(options *opts, gint type,
                     GSList *arglist, gchar *string) {
      gchar realbuf[MAXPIECELENGTH+1]={0};
      gchar *buf=realbuf + 1;  /* So that buf[0-1] is OK... */
      int i;
      GSList *list;
      AnimAction *anim;
      gchar *str;
      gint n;
      gboolean met_loop=FALSE;

      list=g_slist_alloc();
      /* Make the first one a useless timeout (for led_do_your_thing). */
      anim=g_new0(AnimAction,1);
      anim->type=ANIM_WAIT;
      anim->data=-1;
      list->data=anim;
      
      str=string;
      while ((str=parse_getpiece(str,buf))!=NULL) {
            anim=g_new0(AnimAction,1);

            if (strcasecmp(buf,"loop")==0) {
                  /* LOOP CONDITION */
                  /* Don't allow two LOOPs - assumed later on. */
                  if (met_loop) {
                        gslist_free_all(list);
                        g_free(anim);
                        return FALSE;
                  }
                  met_loop=TRUE;
                  anim->type=ANIM_LOOP;
            } else if (parse_getnumber(buf,&n)!=NULL) {
                  /* DELAY CONDITION */
                  anim->type=ANIM_WAIT;
                  anim->data=n;
            } else {
                  /* SET ON/OFF */
                  for (i=0; buf[i]; i++) {
                        if (buf[i]=='x' || buf[i]=='X')
                              i++;
                        switch (buf[i]) {
                        case 'n':
                        case 'N':
                              anim->data=LED_N;
                              break;
                        case 's':
                        case 'S':
                              anim->data=LED_S;
                              break;
                        case 'c':
                        case 'C':
                              anim->data=LED_C;
                              break;
                        default:
                              gslist_free_all(list);
                              g_free(anim);
                              return FALSE;
                        }
                        if (isupper(buf[i]))
                              anim->type=ANIM_ON;
                        else
                              anim->type=ANIM_OFF;
                        /* buf[-1] is OK... */
                        if (buf[i-1]=='x' || buf[i-1]=='X')
                              anim->type=ANIM_NORMAL;
                        if (buf[i+1]) {
                              g_slist_append(list,anim);
                              anim=g_new0(AnimAction,1);
                        }
                  }
            }
            g_slist_append(list,anim);
      }

      /* I guess we're done. Free possible old anim, set defaults
         and set this one. */
      gslist_free_all(opts->anim);
      led_flag_set(&opts->anim_and,&opts->anim_or,LED_S,LEDTYPE_NORMAL);
      led_flag_set(&opts->anim_and,&opts->anim_or,LED_C,LEDTYPE_NORMAL);
      led_flag_set(&opts->anim_and,&opts->anim_or,LED_N,LEDTYPE_NORMAL);
      opts->anim_loop=FALSE;
      opts->anim=list;

      return TRUE;
}



/*
 * Very useful...
 */
static gboolean parse_nop(options *opts, gint type,
                    GSList *arglist, gchar *str) {
      return TRUE;
}

















/*
 * A few external helper functions.
 */


/*
 * Returns pointer to next character in str that is not a space.
 */
gchar *parse_jumpspace(gchar *str) {
      gint i;
      
      for (i=0; str[i] && isspace(str[i]); i++)
            ;
      return str+i;
}

/*
 * Gets a space-separated piece from str and stores it into buf.
 * Returns pointer to str directly after the piece.
 * If piece is longer that MAXPIECELENGTH-1, returns NULL.
 * If no piece can be found, returns NULL.
 */
gchar *parse_getpiece(gchar *str,gchar *buf) {
      gint i;
      
      str=parse_jumpspace(str);
      if (str[0]==0)
            return NULL;

      for (i=0; i<(MAXPIECELENGTH-1) && str[i] && !isspace(str[i]); i++)
            buf[i]=str[i];
      if (i>=(MAXPIECELENGTH-1))
            return NULL;
      buf[i]=0;
      return str+i;
}

/*
 * Returns TRUE if str contains only whitespace etc. If str==NULL, returns
 * TRUE.
 */
gboolean parse_eol(gchar *str) {
      gint i;

      if (str==NULL)
            return TRUE;
      for (i=0; str[i]; i++)
            if (!isspace(str[i]))
                  return FALSE;
      return TRUE;
}


/*
 * A few internal helper functions:
 */



/*
 * Gets a space-separated non-negative number from str and stores it in *n.
 * Returns pointer to str directly after the piece.
 * If string is not a number returns NULL.
 */
static gchar *parse_getnumber(gchar *str,gint *n) {
      gint mul=1;
      *n=0;
      str=parse_jumpspace(str);
      if (str[0]=='+')
            str++;
      else if (str[0]=='-') {
            mul=-1;
            str++;
      }
      if (!str[0])
            return NULL;
      
      for (; str[0] && !isspace(str[0]); str++) {
            if (!isdigit(str[0]))
                  return NULL;
            *n=*n*10+mul*(str[0]-'0');
      }
      return str;
}

/*
 * Gets a space-separated floating point number from str and stores it
 * in *n. If string is not a number returns NULL.
 */
static gchar *parse_getfloat(gchar *str,gfloat *n) {
      gfloat d;
      gint sign=1;

      *n=0;

      /* Check for negative number */
      str=parse_jumpspace(str);
      if (str[0]==0)
            return NULL;
      if (str[0]=='-') {
            sign=-1;
            str++;
      }

      for (; str[0] && str[0]!='.' && str[0]!=',' &&
                 !isspace(str[0]); str++) {
            if (!isdigit(str[0]))
                  return NULL;
            *n=*n*10+str[0]-'0';
      }
      if (isspace(str[0]) || str[0]==0) {
            if (sign<0)
                  *n=-*n;
            return str;
      }
      /* Contains '.' or ',' */
      str++;
      d=10;
      for (; str[0] && !isspace(str[0]); str++) {
            if (!isdigit(str[0]))
                  return NULL;
            *n=*n+((gfloat)(str[0]-'0')/d);
            d*=10;
      }
      if (sign<0)
            *n=-*n;
      return str;
}


/*
 * Parses the first option field for a "set" command (affected led list).
 * Returns a GSList containing pointers to the affected structs.
 * Returns NULL on parse error.
 */
static GSList *parse_ledlist(options *opts,gchar *str) {
      gint i;
      LedName led;
      gint priority;
      GSList *list;

      list=g_slist_alloc();
      
      for (i=0; str[i]; i++) {
            priority=0;
            switch (str[i]) {
            case 's':
            case 'S':
                  led=LED_S;
                  break;
            case 'n':
            case 'N':
                  led=LED_N;
                  break;
            case 'c':
            case 'C':
                  led=LED_C;
                  break;
            default:
                  g_slist_free(list);
                  return NULL;
            }
            if (isdigit(str[i+1]) && (str[i+1]-'0'<=PRIORITY_MAX)) {
                  i++;
                  priority=str[i]-'0';
            }
            g_slist_append(list,opts->leds[led][priority]);
      }
      return list;
}


/*
 * Parses a list of times for light-flashing. Returns a new GSList.
 */
static GSList *parse_blinkopts(gchar *str) {
      GSList *list=NULL;
      gint n;

      while (1) {
            str=parse_jumpspace(str);
            if (str[0]==0) {    /* End of line */
                  return list;
            }
            str=parse_getnumber(str,&n);
            if (str==NULL || n<0) {
                  g_slist_free(list);
                  return NULL;
            }
            list=g_slist_append(list,GINT_TO_POINTER(n));
      } /* Never gets beyond this. */
}


/*
 * Makes a GSList as a dutycycle for the given settings.
 *
 * If set < min, then LED is totally off.
 * If set > max, then LED is totally on.
 * Otherwise, LED is on total-time * (set - min) / (max - min), off
 * the rest of total-time.
 *
 * If min > max, then everything is swapped around (the larger set gets,
 * the less time LED is on).
 *
 * Returns:
 *   NULL - Error occurred
 *    -n  - Set type to n.
 *     n  - Pointer to newly allocated GSList.
 */
static GSList *parse_make_dutycycle(gint total, gfloat min, gfloat max,
                            gfloat set) {
      gfloat a,b;
      gfloat tmp;
      GSList *list;

      /* Sanity check */
      if (total<=0)
            return NULL;
      if (((max-min)==0) || (max==min))  /* Both just to be sure (floats) */
            return NULL;

      /* All values sane. Just do it. */
      if (max<min) {
            /* Flip everything around (max+min)/2 = d.
             * Then set will be
             * -(x-d)+d = 2*d-x = max+min-x
             */
            tmp=max;
            max=min;
            min=tmp;
            set=max+min-set;
      }

      /* Calc the times */
      a=(gfloat)total*((gfloat)(set-min)/(max-min));
      b=(gfloat)total*((gfloat)(max-set)/(max-min));

      /* Check for ON and OFF */
      if (a<1)  /* ON time low */
            return (GSList *)GINT_TO_POINTER(-LEDTYPE_OFF);
      if (b<1)   /* OFF time low */
            return (GSList *)GINT_TO_POINTER(-LEDTYPE_ON);

      /* Were going to BLINK */
      list=NULL;
      list=g_slist_append(list,GINT_TO_POINTER((gint)a));
      list=g_slist_append(list,GINT_TO_POINTER((gint)b));

      return list;
}


/*
 * Makes a GSList as a frequency for the given settings.
 *
 * If value < min, then LED is totally OFF.
 * If value > max, then LED is blinking freq1/freq1.
 * Otherwise frequency of blinking is linearly interpolated between them.
 *
 * Returns:
 *   NULL - Error occurred
 *    -n  - Set type to n.
 *     n  - Pointer to newly allocated GSList.
 */
static GSList *parse_make_frequency(gfloat min, gint freq1,
                            gfloat max, gint freq2, gfloat value) {
      GSList *list;
      gint t;

      /* Sanity check */
      if (freq1<=0 || freq2<=0)
            return NULL;
      if (((max-min)==0) || (max<=min))  /* Both just to be sure (floats) */
            return NULL;
/* Yes it would!
 *     if (freq1<freq2)
 *             return NULL;
 */

      /* All values sane. Just do it. */

      /* Check for OFF */
      if (value<min)
            return (GSList *)GINT_TO_POINTER(-LEDTYPE_OFF);

      /* Calc the time (as 1/freq) */
      t=((gfloat)(value-min)*freq2+(gfloat)(max-value)*freq1)/(max-min);
          
      if (value>=max)
            t=freq2;

      /* We're going to BLINK */
      list=NULL;
      list=g_slist_append(list,GINT_TO_POINTER((gint)t));
      /* Unnecessary: */
/*    list=g_slist_append(list,GINT_TO_POINTER((gint)t)); */

      return list;
}


Generated by  Doxygen 1.6.0   Back to index