/*
 * FvwmButtons, copyright 1996, Jarl Totland
 *
 * This module, and the entire GoodStuff program, and the concept for
 * interfacing this module to the Window Manager, are all original work
 * by Robert Nation
 *
 * Copyright 1993, Robert Nation. No guarantees or warantees or anything
 * are provided or implied in any way whatsoever. Use this program at your
 * own risk. Permission to use this program for any purpose is given,
 * as long as the copyright is kept intact.
 *
 */

/* This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

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

#include <X11/Xlib.h>
#include <X11/Intrinsic.h>

#include "libs/fvwmlib.h"
#include "libs/FScreen.h"
#include "libs/Module.h"
#include "libs/Colorset.h"
#include "FvwmButtons.h"
#include "button.h"
#include "parse.h"

extern int w,h,x,y,xneg,yneg; /* used in ParseConfigLine */
extern char *config_file;
extern int button_width;
extern int button_height;
extern int has_button_geometry;

/* contains the character that terminated the last string from seekright */
static char terminator = '\0';

/* ----------------------------------- macros ------------------------------ */

static char *trimleft(char *s)
{
  while (s && isspace((unsigned char)*s))
    s++;
  return s;
}

/**
*** seekright()
***
*** split off the first continous section of the string into a new allocated
*** string and move the old pointer forward. Accepts and strips quoting with
*** '," or `, and the current quote q can be escaped inside the string with \q.
**/
static char *seekright(char **s)
{
  char *token = NULL;
  char *line = *s;

  line = DoGetNextToken(line, &token, NULL, "),", &terminator);
  if (*s != NULL && line == NULL)
    line = strchr(*s, '\0');
  *s = line;

  return token;
}

static char *my_get_font(char **s)
{
  char *option;
  int len;

  *s = GetNextFullOption(*s, &option);
  if (option)
  {
    for (len = strlen(option) - 1; len >= 0 && isspace(option[len]); len--)
    {
      /* remove trailing whitespace */
      option[len] = 0;
    }
    for ( ; *option && isspace(*option); option++)
    {
      /* remove leading whitespace */
    }
  }

  len = strlen(option) -1;
  if (option[len] == ')' )
  {
    option[len] = 0;
  }

  return option;
}

/**
*** ParseBack()
*** Parses the options possible to Back
**/
static int ParseBack(char **ss)
{
  char *opts[] =
  {
    "icon",
    NULL
  };
  char *t,*s=*ss;
  int r=0;

  while(*s && *s!=')')
  {
    s = trimleft(s);
    if(*s==',')
      s++;
    else switch(GetTokenIndex(s,opts,-1,&s))
    {
    case 0: /* Icon */
      r=1;
      fprintf(stderr,"%s: Back(Icon) not supported yet\n",MyName);
      break;
    default:
      t=seekright(&s);
      fprintf(stderr,"%s: Illegal back option \"%s\"\n",MyName,(t)?t:"");
      if (t)
	free(t);
    }
  }
  if(*s)
    s++;
  *ss=s;

  return r;
}

/**
*** ParseBoxSize()
*** Parses the options possible to BoxSize
**/
static void ParseBoxSize(char **ss, unsigned long *flags)
{
  char *opts[] =
  {
    "dumb",
    "fixed",
    "smart",
    NULL
  };
  char *s = *ss;
  int m;

  if (!s)
    return;
  *ss = GetNextTokenIndex(*ss, opts, 0, &m);
  switch(m)
  {
  case 0:
    *flags &= ~(b_SizeFixed|b_SizeSmart);
    break;
  case 1:
    *flags |= b_SizeFixed;
    *flags &= ~b_SizeSmart;
    break;
  case 2:
    *flags |= b_SizeSmart;
    *flags &= ~b_SizeFixed;
    break;
  default:
    *flags &= ~(b_SizeFixed|b_SizeSmart);
    fprintf(stderr,"%s: Illegal boxsize option \"%s\"\n",MyName, s);
    break;
  }
  return;
}

/**
*** ParseTitle()
*** Parses the options possible to Title
**/
static void ParseTitle(char **ss, byte *flags, byte *mask)
{
  char *titleopts[] =
  {
    "left",
    "right",
    "center",
    "side",
    NULL
  };
  char *t,*s=*ss;

  while(*s && *s!=')')
  {
    s = trimleft(s);
    if(*s==',')
      s++;
    else switch(GetTokenIndex(s,titleopts,-1,&s))
    {
    case 0: /* Left */
      *flags&=~b_TitleHoriz;
      *mask|=b_TitleHoriz;
      break;
    case 1: /* Right */
      *flags&=~b_TitleHoriz;
      *flags|=2;
      *mask|=b_TitleHoriz;
      break;
    case 2: /* Center */
      *flags&=~b_TitleHoriz;
      *flags|=1;
      *mask|=b_TitleHoriz;
      break;
    case 3: /* Side */
      *flags|=b_Horizontal;
      *mask|=b_Horizontal;
      break;
    default:
      t=seekright(&s);
      fprintf(stderr,"%s: Illegal title option \"%s\"\n",MyName,(t)?t:"");
      if (t)
	free(t);
    }
  }
  if(*s) s++;
  *ss=s;
}

/**
*** ParseSwallow()
*** Parses the options possible to Swallow
**/
static void ParseSwallow(char **ss,byte *flags,byte *mask)
{
  char *swallowopts[] =
  {
    "nohints", "hints",
    "nokill","kill",
    "noclose", "close",
    "respawn","norespawn",
    "useold", "noold",
    "usetitle", "notitle",
    NULL
  };
  char *t,*s=*ss;

  while(*s && *s!=')')
  {
    s = trimleft(s);
    if(*s==',')
      s++;
    else switch(GetTokenIndex(s,swallowopts,-1,&s))
    {
    case 0: /* NoHints */
      *flags|=b_NoHints;
      *mask|=b_NoHints;
      break;
    case 1: /* Hints */
      *flags&=~b_NoHints;
      *mask|=b_NoHints;
      break;
    case 2: /* NoKill */
      *flags&=~b_Kill;
      *mask|=b_Kill;
      break;
    case 3: /* Kill */
      *flags|=b_Kill;
      *mask|=b_Kill;
      break;
    case 4: /* NoClose */
      *flags|=b_NoClose;
      *mask|=b_NoClose;
      break;
    case 5: /* Close */
      *flags&=~b_NoClose;
      *mask|=b_NoClose;
      break;
    case 6: /* Respawn */
      *flags|=b_Respawn;
      *mask|=b_Respawn;
      break;
    case 7: /* NoRespawn */
      *flags&=~b_Respawn;
      *mask|=b_Respawn;
      break;
    case 8: /* UseOld */
      *flags|=b_UseOld;
      *mask|=b_UseOld;
      break;
    case 9: /* NoOld */
      *flags&=~b_UseOld;
      *mask|=b_UseOld;
      break;
    case 10: /* UseTitle */
      *flags|=b_UseTitle;
      *mask|=b_UseTitle;
      break;
    case 11: /* NoTitle */
      *flags&=~b_UseTitle;
      *mask|=b_UseTitle;
      break;
    default:
      t=seekright(&s);
      fprintf(stderr,"%s: Illegal Swallow option \"%s\"\n",MyName,
	      (t)?t:"");
      if (t)
	free(t);
    }
  }
  if(*s) s++;
  *ss=s;
}

/**
*** ParsePanel()
*** Parses the options possible to Panel
**/
static void ParsePanel(char **ss, byte *flags, byte *mask, char *direction,
		       int *steps, int *delay, panel_flags_type *panel_flags,
		       int *indicator_size, int *rela_x, int *rela_y,
		       char *position, char *context)
{
  char *swallowopts[] =
  {
    "nohints", "hints",
    "nokill","kill",
    "noclose", "close",
    "respawn","norespawn",
    "useold", "noold",
    "usetitle", "notitle",
    "up", "down", "left", "right",
    "steps",
    "delay",
    "smooth",
    "noborder",
    "indicator",
    "position",
    NULL
  };

  char *positionopts[] =
  {
    "button", "module", "root", "center", "left", "top", "right",
    "bottom", "noplr", "noptb", "mlr", "mtb", NULL
  };

  char *t,*s=*ss;
  int n;

  while(*s && *s!=')')
  {
    s = trimleft(s);
    if(*s==',')
      s++;
    else switch(GetTokenIndex(s,swallowopts,-1,&s))
    {
    case 0: /* NoHints */
      *flags|=b_NoHints;
      *mask|=b_NoHints;
      break;
    case 1: /* Hints */
      *flags&=~b_NoHints;
      *mask|=b_NoHints;
      break;
    case 2: /* NoKill */
      *flags&=~b_Kill;
      *mask|=b_Kill;
      break;
    case 3: /* Kill */
      *flags|=b_Kill;
      *mask|=b_Kill;
      break;
    case 4: /* NoClose */
      *flags|=b_NoClose;
      *mask|=b_NoClose;
      break;
    case 5: /* Close */
      *flags&=~b_NoClose;
      *mask|=b_NoClose;
      break;
    case 6: /* Respawn */
      *flags|=b_Respawn;
      *mask|=b_Respawn;
      break;
    case 7: /* NoRespawn */
      *flags&=~b_Respawn;
      *mask|=b_Respawn;
      break;
    case 8: /* UseOld */
      *flags|=b_UseOld;
      *mask|=b_UseOld;
      break;
    case 9: /* NoOld */
      *flags&=~b_UseOld;
      *mask|=b_UseOld;
      break;
    case 10: /* UseTitle */
      *flags|=b_UseTitle;
      *mask|=b_UseTitle;
      break;
    case 11: /* NoTitle */
      *flags&=~b_UseTitle;
      *mask|=b_UseTitle;
      break;
    case 12: /* up */
      *direction = SLIDE_UP;
      break;
    case 13: /* down */
      *direction = SLIDE_DOWN;
      break;
    case 14: /* left */
      *direction = SLIDE_LEFT;
      break;
    case 15: /* right */
      *direction = SLIDE_RIGHT;
      break;
    case 16: /* steps */
      sscanf(s, "%d%n", steps, &n);
      s += n;
      break;
    case 17: /* delay */
      sscanf(s, "%d%n", delay, &n);
      s += n;
      break;
    case 18: /* smooth */
      (*panel_flags).smooth = 1;
      break;
    case 19: /* noborder */
      (*panel_flags).ignore_lrborder = 1;
      (*panel_flags).ignore_tbborder = 1;
      break;
    case 20: /* indicator */
      n = 0;
      (*panel_flags).panel_indicator = 1;
      *indicator_size = 0;
      sscanf(s, "%d%n", indicator_size, &n);
      if (*indicator_size < 0 || *indicator_size > 100)
      {
	*indicator_size = 0;
      }
      s += n;
      break;
    case 21: /* position */
      n = 0;
      *rela_x = *rela_y = 0;
      while(*s != ',' && *s != ')' && *s)
      {
	s = trimleft(s);
	/* get x and y offset */
	if((*s>='0' && *s<='9') || *s=='+' || *s=='-')
	{
	  sscanf(s, "%i%n", rela_x, &n);
	  s += n;
	  if (*s == 'p' || *s == 'P')
	  {
	    (*panel_flags).relative_x_pixel = 1;
	    s++;
	  }
	  n = 0;
	  s = trimleft(s);
	  sscanf(s, "%i%n", rela_y, &n);
	  s += n;
	  if (*s == 'p' || *s == 'P')
	  {
	    (*panel_flags).relative_y_pixel = 1;
	    s++;
	  }
	  s = trimleft(s);
	}
	switch(GetTokenIndex(s,positionopts,-1,&s))
	{
	case 0: /* button */
	  *context = SLIDE_CONTEXT_PB;
	  break;
	case 1: /* module */
	  *context = SLIDE_CONTEXT_MODULE;
	  break;
	case 2: /* root */
	  *context = SLIDE_CONTEXT_ROOT;
	  break;
	case 3: /* center */
	  *position = SLIDE_POSITION_CENTER;
	  break;
	case 4: /* left */
	  *position = SLIDE_POSITION_LEFT_TOP;
	  break;
	case 5: /* top  */
	  *position = SLIDE_POSITION_LEFT_TOP;
	  break;
	case 6: /* right */
	  *position = SLIDE_POSITION_RIGHT_BOTTOM;
	  break;
	case 7: /* bottom */
	  *position = SLIDE_POSITION_RIGHT_BOTTOM;
	  break;
	case 8: /*  noplr */
	  (*panel_flags).ignore_lrborder = 1;
	  break;
	case 9: /* noptb */
	  (*panel_flags).ignore_tbborder = 1;
	  break;
	case 10: /* mlr */
	  (*panel_flags).buttons_lrborder = 1;
	  break;
	case 11: /* mtb */
	  (*panel_flags).buttons_tbborder = 1;
	  break;
	default:
	  t=seekright(&s);
	  s--;
	  if (t)
	  {
	    fprintf(stderr,"%s: Illegal Panel position option \"%s\"\n",MyName,
		    (t)?t:"");
	    free(t);
	  }
	}
      }
      break;
    default:
      t=seekright(&s);
      fprintf(stderr,"%s: Illegal Panel option \"%s\"\n",MyName,
	      (t)?t:"");
      if (t)
	free(t);
    }
  }
  if(*s) s++;
  *ss=s;
}

/**
*** ParseContainer()
*** Parses the options possible to Container
**/
static void ParseContainer(char **ss,button_info *b)
{
  char *conts[] =
  {
    "columns",
    "rows",
    "font",
    "frame",
    "back",
    "fore",
    "padding",
    "title",
    "swallow",
    "nosize",
    "size",
    "boxsize",
    "colorset",
    NULL
  };
  char *t,*o,*s=*ss;
  int i,j;

  while(*s && *s!=')')
  {
    s = trimleft(s);
    if(*s==',')
      s++;
    else switch(GetTokenIndex(s,conts,-1,&s))
    {
    case 0: /* Columns */
      b->c->num_columns=max(1,strtol(s,&t,10));
      s=t;
      break;
    case 1: /* Rows */
      b->c->num_rows=max(1,strtol(s,&t,10));
      s=t;
      break;
    case 2: /* Font */
      if (b->c->font_string)
        free(b->c->font_string);
      b->font_string = my_get_font(&s);
      if(b->c->font_string)
      {
	b->c->flags|=b_Font;
      } else
	b->c->flags&=~b_Font;
      break;
    case 3: /* Frame */
      b->c->framew=strtol(s,&t,10);
      b->c->flags|=b_Frame;
      s=t;
      break;
    case 4: /* Back */
      s = trimleft(s);
      if(*s=='(' && s++)
	if(ParseBack(&s))
	  b->c->flags|=b_IconBack;
      if (b->c->back) free(b->c->back);
      b->c->back=seekright(&s);
      if(b->c->back)
      {
	b->c->flags|=b_Back;
      }
      else
	b->c->flags&=~(b_IconBack|b_Back);
      break;
    case 5: /* Fore */
      if (b->c->fore) free(b->c->fore);
      b->c->fore=seekright(&s);
      if(b->c->fore)
      {
	b->c->flags|=b_Fore;
      }
      else
	b->c->flags&=~b_Fore;
      break;
    case 6: /* Padding */
      i=strtol(s,&t,10);
      if(t>s)
      {
	b->c->xpad=b->c->ypad=i;
	s=t;
	i=strtol(s,&t,10);
	if(t>s)
	{
	  b->c->ypad=i;
	  s=t;
	}
	b->c->flags|=b_Padding;
      }
      else
	fprintf(stderr,"%s: Illegal padding argument\n",MyName);
      break;
    case 7: /* Title - flags */
      s = trimleft(s);
      if(*s=='(' && s++)
      {
	b->c->justify=0;
	b->c->justify_mask=0;
	ParseTitle(&s,&b->c->justify,&b->c->justify_mask);
	if(b->c->justify_mask)
	  b->c->flags|=b_Justify;
      }
      else
      {
	char *temp;
	fprintf(stderr,"%s: Illegal title in container options\n",
		MyName);
	temp = seekright(&s);
	if (temp)
	  free(temp);
      }
      break;
    case 8: /* Swallow - flags */
      {
	Bool failed = False;

	s = trimleft(s);
	if (b->c->flags & (b_Swallow | b_Panel))
	{
	  fprintf(stderr, "%s: Multiple Swallow or Panel options are not"
		" allowed in a signle button", MyName);
	  failed = True;
	}
	else if(*s=='(' && s++)
	{
	  b->c->swallow=0;
	  b->c->swallow_mask=0;
	  ParseSwallow(&s, &b->c->swallow, &b->c->swallow_mask);
	  if(b->c->swallow_mask)
	  {
	    b->c->flags |= b_Swallow;
	  }
	}
	else
	{
	  fprintf(stderr,
		  "%s: Illegal swallow or panel in container options\n",
		  MyName);
	  failed = True;
	}
	if (failed)
	{
	  char *temp;

	  temp = seekright(&s);
	  if (temp)
	    free(temp);
	}
      }
      break;
    case 9: /* NoSize */
      b->c->flags|=b_Size;
      b->c->minx=b->c->miny=0;
      break;

    case 10: /* Size */
      i=strtol(s,&t,10);
      j=strtol(t,&o,10);
      if(t>s && o>t)
      {
	b->c->minx=i;
	b->c->miny=j;
	b->c->flags|=b_Size;
	s=o;
      }
      else
	fprintf(stderr,"%s: Illegal size arguments\n",MyName);
      break;

    case 11: /* BoxSize */
      ParseBoxSize(&s, &b->c->flags);
      break;

    case 12: /* Colorset */
      i = strtol(s, &t, 10);
      if(t > s)
      {
	b->c->colorset = i;
	b->c->flags |= b_Colorset;
	AllocColorset(i);
	s = t;
      }
      else
      {
	b->c->flags &= ~b_Colorset;
      }
      break;

    default:
      t=seekright(&s);
      fprintf(stderr,"%s: Illegal container option \"%s\"\n",MyName,
	      (t)?t:"");
      if (t)
	free(t);
    }
  }
  if(*s)
    s++;
  *ss=s;
}

/**
*** ParseButton()
***
*** parse a buttonconfig string.
*** *FvwmButtons(option[ options]) title iconname command
**/
/*#define DEBUG_PARSER*/
static void ParseButton(button_info **uberb,char *s)
{
  button_info *b,*ub=*uberb;
  int i,j;
  char *t,*o;

  b = alloc_button(ub, (ub->c->num_buttons)++);
  s = trimleft(s);

  if(*s=='(' && s++)
  {
    char *opts[] =
    {
      "back",
      "fore",
      "font",
      "title",
      "icon",
      "frame",
      "padding",
      "swallow",
      "panel",
      "action",
      "container",
      "end",
      "nosize",
      "size",
      "left",
      "right",
      "center",
      "colorset",
      NULL
    };
    s = trimleft(s);
    while(*s && *s!=')')
    {
      Bool is_swallow = False;

      if (*s == ',')
      {
	      s++;
	      s = trimleft(s);
	      continue;
      }
      if((*s>='0' && *s<='9') || *s=='+' || *s=='-')
      {
	char *geom;
	int x,y,flags;
	unsigned int w,h;

	geom=seekright(&s);
	if (geom)
	{
	  flags=XParseGeometry(geom, &x, &y, &w, &h);
	  if(flags&WidthValue)
	    b->BWidth=w;
	  if(flags&HeightValue)
	    b->BHeight=h;
	  if(flags&XValue)
	  {
	    b->BPosX=x;
	    b->flags|=b_PosFixed;
	  }
	  if(flags&YValue)
	  {
	    b->BPosY=y;
	    b->flags|=b_PosFixed;
	  }
	  if(flags&XNegative)
	    b->BPosX=-1-x;
	  if(flags&YNegative)
	    b->BPosY=-1-y;
	  free(geom);
	}
	s = trimleft(s);
	continue;
      }
      switch(GetTokenIndex(s,opts,-1,&s))
      {
      case 0: /* Back */
	s = trimleft(s);
	if(*s=='(' && s++)
	  if(ParseBack(&s))
	    b->flags|=b_IconBack;
	if(b->flags&b_Back && b->back)
	  free(b->back);
	b->back=seekright(&s);
	if(b->back)
	{
	  b->flags|=b_Back;
	}
	else
	  b->flags&=~(b_IconBack|b_Back);
	break;

      case 1: /* Fore */
	if(b->flags&b_Fore && b->fore)
	  free(b->fore);
	b->fore=seekright(&s);
	if(b->fore)
	{
	  b->flags|=b_Fore;
	}
	else
	  b->flags&=~b_Fore;
	break;

      case 2: /* Font */
	if(b->flags&b_Font && b->font_string)
	  free(b->font_string);
        b->font_string = my_get_font(&s);
	if(b->font_string)
	{
	  b->flags|=b_Font;
	}
	else
	  b->flags&=~b_Font;
	break;

	/* --------------------------- Title ------------------------- */

      case 3: /* Title */
	s = trimleft(s);
	if(*s=='(' && s++)
	{
	  b->justify=0;
	  b->justify_mask=0;
	  ParseTitle(&s,&b->justify,&b->justify_mask);
	  if(b->justify_mask)
	    b->flags|=b_Justify;
	}
	t=seekright(&s);
	if(t && *t && (t[0]!='-' || t[1]!=0))
	{
	  if (b->title)
	    free(b->title);
	  b->title=t;
#ifdef DEBUG_PARSER
	  fprintf(stderr,"PARSE: Title \"%s\"\n",b->title);
#endif
	  b->flags|=b_Title;
	}
	else
	{
	  fprintf(stderr,"%s: Missing title argument\n",MyName);
	  if(t)free(t);
	}
	break;

	/* ---------------------------- icon ------------------------- */

      case 4: /* Icon */
	t=seekright(&s);
	if(t && *t && (t[0] != '-' || t[1] != 0))
	{
	  if (b->flags & b_Swallow)
	  {
	    fprintf(
	      stderr,"%s: a button can not have an icon and a swallowed window"
	      " at the same time. Ignoring icon", MyName);
	  }
	  else
	  {
	    if (b->icon_file)
	      free(b->icon_file);
	    b->icon_file=t;
	    b->IconWin=None;
	    b->flags|=b_Icon;
	  }
	}
	else
	{
	  fprintf(stderr,"%s: Missing icon argument\n",MyName);
	  if(t)free(t);
	}
	break;

	/* --------------------------- frame ------------------------- */

      case 5: /* Frame */
	i=strtol(s,&t,10);
	if(t>s)
	{
	  b->flags|=b_Frame;
	  b->framew=i;
	  s=t;
	}
	else
	  fprintf(stderr,"%s: Illegal frame argument\n",MyName);
	break;

	/* -------------------------- padding ------------------------ */

      case 6: /* Padding */
	i=strtol(s,&t,10);
	if(t>s)
	{
	  b->xpad=b->ypad=i;
	  b->flags |= b_Padding;
	  s=t;
	  i=strtol(s,&t,10);
	  if(t>s)
	  {
	    b->ypad=i;
	    s=t;
	  }
	}
	else
	  fprintf(stderr,"%s: Illegal padding argument\n",MyName);
	break;

	/* -------------------------- swallow ------------------------ */

      case 7: /* Swallow */
	is_swallow = True;
	/* fall through */
      case 8: /* Panel */
	s = trimleft(s);
	if (is_swallow)
	{
	  b->swallow=0;
	  b->swallow_mask=0;
	}
	else
	{
	  /* set defaults */
	  b->swallow = b_Respawn;
	  b->swallow_mask = b_Respawn;
	  b->slide_direction = SLIDE_UP;
	  b->slide_position = SLIDE_POSITION_CENTER;
	  b->slide_context = SLIDE_CONTEXT_PB;
	  b->relative_x = 0;
	  b->relative_y = 0;
	  b->slide_steps = 12;
	  b->slide_delay_ms = 5;
	}
	if(*s=='(' && s++)
	{
	  if (is_swallow)
	    ParseSwallow(&s, &b->swallow,&b->swallow_mask);
	  else
	    ParsePanel(&s, &b->swallow, &b->swallow_mask, &b->slide_direction,
		       &b->slide_steps, &b->slide_delay_ms, &b->panel_flags,
		       &b->indicator_size, &b->relative_x, &b->relative_y,
		       &b->slide_position, &b->slide_context);
	}
	t=seekright(&s);
	o=seekright(&s);
	if(t)
	{
	  if (b->hangon)
	    free(b->hangon);
	  b->hangon=t;
	  if (is_swallow)
	  {
	    if (b->flags & b_Icon)
	    {
	      fprintf(
		stderr,"%s: a button can not have an icon and a swallowed "
		" window at the same time. Ignoring icon", MyName);
	      b->flags &= ~ b_Icon;
	    }

	    b->flags |= (b_Swallow | b_Hangon);
	  }
	  else
	  {
	    b->flags |= (b_Panel | b_Hangon);
	    b->newflags.is_panel = 1;
	    b->newflags.panel_mapped = 0;
	  }
	  b->swallow|=1;
	  if(!(b->swallow&b_NoHints))
	    b->hints=(XSizeHints*)mymalloc(sizeof(XSizeHints));
	  if(o)
	  {
	    char *p;

	    p = expand_action(o, NULL);
	    if (p)
	    {
	      if(!(buttonSwallow(b)&b_UseOld))
		SendText(fd,p,0);
	      if (b->spawn)
		free(b->spawn);
	      b->spawn=o;  /* Might be needed if respawning sometime */
	      free(p);
	    }
	  }
	}
	else
	{
	  fprintf(stderr,"%s: Missing swallow argument\n",MyName);
	  if(t)
	    free(t);
	  if(o)
	    free(o);
	}
	break;

	/* --------------------------- action ------------------------ */

      case 9: /* Action */
	s = trimleft(s);
	i=0;
	if(*s=='(')
	{
	  s++;
	  if(strncasecmp(s,"mouse",5)!=0)
	  {
	    fprintf(stderr,"%s: Couldn't parse action\n",MyName);
	  }
	  s+=5;
	  i=strtol(s,&t,10);
	  s=t;
	  while(*s && *s!=')')
	    s++;
	  if(*s==')')
            s++;
	}
        {
          char *r;
          char *u = s;

          s = GetQuotedString(s, &t, ",)", NULL, "(", ")");
          r = s;
          if (t && r > u + 1)
          {
            /* remove unquoted trailing spaces */
            r -= 2;
            while (r >= u && isspace(*r))
              r--;
            r++;
            if (isspace(*r))
            {
              t[strlen(t) - (s - r - 1)] = 0;
            }
          }
        }
	if(t)
	{
	  AddButtonAction(b,i,t);
	  free(t);
	}
	else
	  fprintf(stderr,"%s: Missing action argument\n",MyName);
	break;

	/* -------------------------- container ---------------------- */

      case 10: /* Container */
	b->flags&=b_Frame|b_Back|b_Fore|b_Padding|b_Action;
	MakeContainer(b);
	*uberb=b;
	s = trimleft(s);
	if(*s=='(' && s++)
	  ParseContainer(&s,b);
	break;

      case 11: /* End */
	*uberb=ub->parent;
	ub->c->buttons[--(ub->c->num_buttons)]=NULL;
	free(b);
	if(!ub->parent)
	{
	  fprintf(stderr,"%s: Unmatched END in config file\n",MyName);
	  exit(1);
	}
	return;

      case 12: /* NoSize */
	b->flags|=b_Size;
	b->minx=b->miny=0;
	break;

      case 13: /* Size */
	i=strtol(s,&t,10);
	j=strtol(t,&o,10);
	if(t>s && o>t)
	{
	  b->minx=i;
	  b->miny=j;
	  b->flags|=b_Size;
	  s=o;
	}
	else
	  fprintf(stderr,"%s: Illegal size arguments\n",MyName);
	break;

      case 14: /* Left */
	b->flags |= b_Left;
	b->flags &= ~b_Right;
	break;

      case 15: /* Right */
	b->flags |= b_Right;
	b->flags &= ~b_Left;
	break;

      case 16: /* Center */
	b->flags &= ~(b_Right|b_Left);
	break;

      case 17: /* Colorset */
	i = strtol(s, &t, 10);
	if(t > s)
	{
	  b->colorset = i;
	  b->flags |= b_Colorset;
	  s=t;
	  AllocColorset(i);
	}
	else
	{
	  b->flags &= ~b_Colorset;
	}
	break;

      default:
	t=seekright(&s);
	fprintf(stderr,"%s: Illegal button option \"%s\"\n",MyName,
		(t)?t:"");
	if (t)
	  free(t);
	break;
      }
      s = trimleft(s);
    }
    if (s && *s)
    {
      s++;
      s = trimleft(s);
    }
  }

  /* get title and iconname */
  if(!(b->flags&b_Title))
  {
    b->title=seekright(&s);
    if(b->title && *b->title && ((b->title)[0]!='-'||(b->title)[1]!=0))
      b->flags |= b_Title;
    else
      if(b->title)free(b->title);
  }
  else
  {
    char *temp;
    temp = seekright(&s);
    if (temp)
      free(temp);
  }

  if(!(b->flags&b_Icon))
  {
    b->icon_file=seekright(&s);
    if(b->icon_file && b->icon_file &&
       ((b->icon_file)[0]!='-'||(b->icon_file)[1]!=0))
    {
      b->flags|=b_Icon;
      b->IconWin=None;
    }
    else
      if(b->icon_file)free(b->icon_file);
  }
  else
  {
    char *temp;
    temp = seekright(&s);
    if (temp)
      free(temp);
  }

  s = trimleft(s);

  /* Swallow hangon command */
  if (strncasecmp(s,"swallow",7)==0 || strncasecmp(s,"panel",7)==0)
  {
    if(b->flags & (b_Swallow | b_Panel))
    {
      fprintf(stderr,"%s: Illegal with both old and new swallow!\n",
	      MyName);
      exit(1);
    }
    s+=7;
    /*
     * Swallow old 'swallowmodule' command
     */
    if (strncasecmp(s,"module",6)==0)
    {
      s+=6;
    }
    if (b->hangon)
      free(b->hangon);
    b->hangon=seekright(&s);
    if (!b->hangon)
      b->hangon = safestrdup("");
    if (tolower(*s) == 's')
      b->flags |= b_Swallow | b_Hangon;
    else
      b->flags |= b_Panel | b_Hangon;
    b->swallow|=1;
    s = trimleft(s);
    if(!(b->swallow&b_NoHints))
      b->hints=(XSizeHints*)mymalloc(sizeof(XSizeHints));
    if(*s)
    {
      if(!(buttonSwallow(b)&b_UseOld))
	SendText(fd,s,0);
      b->spawn=safestrdup(s);
    }
  }
  else if(*s)
    AddButtonAction(b,0,s);
  return;
}

/**
*** ParseConfigLine
**/
static void ParseConfigLine(button_info **ubb,char *s)
{
  button_info *ub=*ubb;
  char *opts[] =
  {
    "geometry",
    "buttongeometry",
    "font",
    "padding",
    "columns",
    "rows",
    "back",
    "fore",
    "frame",
    "file",
    "pixmap",
    "boxsize",
    "colorset",
    NULL
  };
  int i,j,k;

  switch(GetTokenIndex(s,opts,-1,&s))
  {
  case 0:/* Geometry */
  {
    char geom[64];

    i=sscanf(s,"%63s",geom);
    if(i==1)
    {
      parse_window_geometry(geom, 0);
    }
    break;
  }
  case 1:/* ButtonGeometry */
  {
    char geom[64];

    i=sscanf(s,"%63s",geom);
    if(i==1)
    {
      parse_window_geometry(geom, 1);
    }
    break;
  }
  case 2:/* Font */
    if (ub->c->font_string)
      free(ub->c->font_string);
    CopyStringWithQuotes(&ub->c->font_string, s);
    break;
  case 3:/* Padding */
    i=sscanf(s,"%d %d",&j,&k);
    if(i>0)
      ub->c->xpad=ub->c->ypad=j;
    if(i>1)
      ub->c->ypad=k;
    break;
  case 4:/* Columns */
    i=sscanf(s,"%d",&j);
    if(i>0)
      ub->c->num_columns=j;
    break;
  case 5:/* Rows */
    i=sscanf(s,"%d",&j);
    if(i>0)
      ub->c->num_rows=j;
    break;
  case 6:/* Back */
    if (ub->c->back)
      free(ub->c->back);
    CopyString(&(ub->c->back),s);
    break;
  case 7:/* Fore */
    if (ub->c->fore)
      free(ub->c->fore);
    CopyString(&(ub->c->fore),s);
    break;
  case 8:/* Frame */
    i=sscanf(s,"%d",&j);
    if(i>0) ub->c->framew=j;
    break;
  case 9:/* File */
    s = trimleft(s);
    if (config_file)
      free(config_file);
    config_file=seekright(&s);
    break;
  case 10:/* Pixmap */
    s = trimleft(s);
    if (strncasecmp(s,"none",4)==0)
      ub->c->flags|=b_TransBack;
    else
    {
      if (ub->c->back_file)
	free(ub->c->back_file);
      CopyString(&(ub->c->back_file),s);
    }
    ub->c->flags|=b_IconBack;
    break;
  case 11: /* BoxSize */
    ParseBoxSize(&s, &ub->c->flags);
    break;
  case 12: /* Colorset */
    i = sscanf(s, "%d", &j);
    if (i > 0)
    {
      ub->c->colorset = j;
      ub->c->flags |= b_Colorset;
      AllocColorset(j);
    }
    else
    {
      ub->c->flags &= ~b_Colorset;
    }
    break;
  default:
    s = trimleft(s);
    ParseButton(ubb,s);
    break;
  }
}

/**
*** ParseConfigFile()
*** Parses optional separate configuration file for FvwmButtons
**/
static void ParseConfigFile(button_info *ub)
{
  char s[1024],*t;
  FILE *f=fopen(config_file,"r");
  int l;
  if(!f)
  {
    fprintf(stderr,"%s: Couldn't open config file %s\n",MyName,config_file);
    return;
  }

  while (fgets(s, 1023, f))
  {
    /* Allow for line continuation: */
    while ((l=strlen(s)) < sizeof(s)
	   && l>=2 && s[l-1]=='\n' && s[l-2]=='\\')
      fgets(s+l-2, sizeof(s)-l, f);

    /* And comments: */
    t=s;
    while(*t)
    {
      if(*t=='#' && (t==s || *(t-1)!='\\'))
      {
	*t=0;
	break;
      }
      t++;
    }
    t = s;
    t = trimleft(t);
    if(*t)
      ParseConfigLine(&ub,t);
  }

  fclose(f);
}

char *expand_action(char *in_action, button_info *b)
{
  char *variables[] =
  {
    "$",
    "fg",
    "bg",
    "left",
    "-left",
    "right",
    "-right",
    "top",
    "-top",
    "bottom",
    "-bottom",
    "width",
    "height",
    NULL
  };
  char *action = NULL;
  char *src;
  char *dest;
  char *string = NULL;
  char *rest;
  int px;
  int py;
  int val = 0;
  int offset;
  int x;
  int y;
  int f;
  int i;
  unsigned int w = 0;
  unsigned int h = 0;
  Window win;
  extern int dpw;
  extern int dph;

  /* create a temporary storage for expanding */
  action = (char *)malloc(MAX_MODULE_INPUT_TEXT_LEN);
  if (!action)
  {
    /* could not alloc memory */
    return NULL;
  }

  /* calculate geometry */
  if (b)
  {
    w = buttonWidth(b);
    h = buttonHeight(b);
    buttonInfo(b, &x, &y, &px, &py, &f);
    XTranslateCoordinates(Dpy, MyWindow, Root, x, y, &x, &y, &win);
  }

  for (src = in_action, dest = action; *src != 0; src++)
  {
    if (*src != '$')
    {
      *(dest++) = *src;
    }
    else
    {
      char *dest_org = dest;
      Bool is_string = False;
      Bool is_value = False;

      *(dest++) = *(src++);
      i = GetTokenIndex(src, variables, -1, &rest);
      if (i == -1)
      {
	src--;
	continue;
      }
      switch (i)
      {
      case 0: /* $ */
	continue;
      case 1: /* fg */
	string = UberButton->c->fore;
	is_string = True;
	break;
      case 2: /* bg */
	string = UberButton->c->back;
	is_string = True;
	break;
      case 3: /* left */
	val = x;
	is_value = True;
	break;
      case 4: /* -left */
	val = dpw - x - 1;
	is_value = True;
	break;
      case 5: /* right */
	val = x + w;
	is_value = True;
	break;
      case 6: /* -right */
	val = dpw - x - w - 1;
	is_value = True;
	break;
      case 7: /* top */
	val = y;
	is_value = True;
	break;
      case 8: /* -top */
	val = dph - y - 1;
	is_value = True;
	break;
      case 9: /* bottom */
	val = y + h;
	is_value = True;
	break;
      case 10: /* -bottom */
	val = dph - y - h - 1;
	is_value = True;
	break;
      case 11: /* width */
	val = w;
	is_value = True;
	break;
      case 12: /* height */
	val = h;
	is_value = True;
	break;
      default: /* unknown */
	src--;
	continue;
      } /* switch */
      dest = dest_org;
      src = --rest;
      if (is_value)
      {
	if (MAX_MODULE_INPUT_TEXT_LEN - (dest - action) <= 16)
	{
	  /* out of space */
	  free(action);
	  return NULL;
	}
	/* print the number into the string */
	sprintf(dest, "%d%n", val, &offset);
	dest += offset;
      }
      else if (is_string)
      {
	if (MAX_MODULE_INPUT_TEXT_LEN - (dest - action) <= strlen(string))
	{
	  /* out of space */
	  free(action);
	  return NULL;
	}
	/* print the colour name into the string */
	if (string)
	{
	  sprintf(dest, "%s%n", string, &offset);
	  dest += offset;
	}
      }
    } /* if */
  } /* for */
  *dest = 0;
  return action;
}

void parse_window_geometry(char *geom, int is_button_geometry)
{
  int flags;
  int g_x;
  int g_y;
  unsigned int width;
  unsigned int height;

  flags = FScreenParseGeometry(geom,&g_x,&g_y,&width,&height);
  UberButton->w = 0;
  UberButton->h = 0;
  if (is_button_geometry)
  {
    if (flags&WidthValue)
      button_width = width;
    if (flags&HeightValue)
      button_height = height;
  }
  else
  {
    if (flags&WidthValue)
      w = width;
    if (flags&HeightValue)
      h = height;
  }
  if (flags&XValue)
    UberButton->x = g_x;
  if (flags&YValue)
    UberButton->y = g_y;
  if (flags&XNegative)
    UberButton->w = 1;
  if (flags&YNegative)
    UberButton->h = 1;
  has_button_geometry = is_button_geometry;

  return;
}

/**
*** ParseOptions()
**/
extern int save_color_limit;            /* global for xpm color limiting */
void ParseConfiguration(button_info *ub)
{
  char *s;
  char *items[] =
  {
    NULL, /* filled out below */
    "imagepath",
    "colorlimit",
    "colorset",
    XINERAMA_CONFIG_STRING,
    NULL
  };

  items[0]=mymalloc(strlen(MyName)+2);
  sprintf(items[0],"*%s",MyName);

  InitGetConfigLine(fd,items[0]);       /* send config lines with MyName */
  GetConfigLine(fd,&s);
  while(s && s[0])
  {
    char *rest;
    switch(GetTokenIndex(s,items,-1,&rest))
    {
    case -1:
      break;
    case 0:
      if(rest && rest[0] && !config_file)
	ParseConfigLine(&ub,rest);
      break;
    case 1:
      if (imagePath)
	free(imagePath);
      CopyString(&imagePath,rest);
      break;
    case 2:                         /* colorlimit */
      sscanf(rest, "%d", &save_color_limit);
      break;
    case 3:
      /* store colorset sent by fvwm */
      LoadColorset(rest);
      break;
    case 4:
      /* Xinerama state */
      FScreenConfigureModule(rest);
      break;
    }
    GetConfigLine(fd,&s);
  }

  if(config_file)
    ParseConfigFile(ub);

  free(items[0]);
  return;
}


syntax highlighted by Code2HTML, v. 0.9.1