/* wmDrawer (c) 2002-2004 Valery Febvre <vfebvre@vfebvre.lautre.net>
 *
 * wmDrawer is a dock application (dockapp) which provides a
 * drawer (button bar) to launch applications from.
 *
 * 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 <sys/types.h>
#include <sys/stat.h>

#if defined USE_GETOPT
#include <unistd.h>
#else
#include <getopt.h>
#endif

#include "types_defs.h"
#include "config.h"

static void parseLine (char *line, int line_num);
static void usage (void);
static void freeUnusedMemory (void);

enum section {
  general,
  images_paths,
  column
};

drawerConfig config;
drawerButton **entries;
static char configFilePath[FILENAME_MAX];
struct stat buf;
static unsigned int currentSection;
static unsigned int rowsCnt;
static unsigned int *nbRowsPerCol;
static char *line;
unsigned int nbEntries, nbImagesPaths;
unsigned int nbCols, nbRows;
unsigned int useDefaultIconsBg, useDefaultDockIcon, useDefaultHighlightImg;
unsigned int drawerOK;

void parseConfig () {
  FILE *f = NULL;
  const char *home;
  unsigned char c;
  unsigned int col, row;
  unsigned int line_num = 0;
  unsigned int line_point = 0;
  /* init vars */
  currentSection = -1;
  nbEntries = nbCols = rowsCnt = nbRows = nbImagesPaths = 0;
  useDefaultIconsBg = useDefaultDockIcon = useDefaultHighlightImg = drawerOK = 1;
  entries = NULL;
  freeAllMemory ();

  /* default adjustments */
  config.iconsExpand    = 1;
  config.transparency   = 0;
  config.dockW          = 64;
  config.dockH          = 64;
  config.btnsSize       = 0;
  config.direction      = -1;
  config.animationSpeed = 1;
  config.cursor         = 30; /* XC_hand2 / 2 */
  config.borderSize     = 1;
  config.showOnHover    = 0;
  config.hideOnOut      = 0;
  config.hideTimeout    = 800;
  config.highlight      = 2;
  config.highlightShading.shading = 60;
  config.highlightShading.tintColor.red   = 255;
  config.highlightShading.tintColor.green = 255;
  config.highlightShading.tintColor.blue  = 255;
  config.tooltips = 1;
  config.tooltipsFont = (char *) malloc (6 * sizeof (char));
  sprintf (config.tooltipsFont, "fixed");
  
  if (*configFilePath == '\0') {
    if ((home = getenv ("HOME")) != NULL && *home != '\0')
      sprintf (configFilePath, "%s/%s", home, ".wmdrawerrc");
    else {
      printf ("%s error: can't find HOME directory\n", PACKAGE);
      exit (EXIT_FAILURE);
    }
  }
  if ((f = fopen (configFilePath, "r")) == NULL) {
    printf ("%s error: can't open config file %s\n", PACKAGE, configFilePath);
    exit (EXIT_FAILURE);
  }

  if (stat (configFilePath, &buf) == 0) {
    config.lastModif = buf.st_mtime;
  }
  
  line = xcalloc (FILENAME_MAX, sizeof (char));
  /* fgets ??? */
  while (fscanf (f, "%c", &c) != EOF) {
    if (c != '\n') {
      line[line_point++] = c;
    }
    else {
      line_num++;
      parseLine (line, line_num);
      free (line);
      line_point = 0;
      line = xcalloc (FILENAME_MAX, sizeof (char));
    }
  }
  fclose (f);
  if (config.direction < 0) {
    freeUnusedMemory ();
    exit (EXIT_FAILURE);
  }
  if (nbRows * nbCols > 0) {
    /* create and alloc an (nbCols x nbRows) array */
    config.entries = (drawerButton **) xcalloc (nbCols,
						sizeof (drawerButton *));
    for (col = 0; col < nbCols; col++)
      config.entries[col] = (drawerButton *) xcalloc (nbRows,
						      sizeof (drawerButton));
    /* Add empty buttons to array entries */
    for (col = 0; col < nbCols; col++) {
      if (nbRowsPerCol[col] < nbRows) {
	entries[col] = xrealloc (entries[col], sizeof (drawerButton) * nbRows);
	for (row = nbRowsPerCol[col]; row < nbRows; row++) {
	  /* alloc empty string for tooltip, image, command */
	  entries[col][row].tooltip = (char *) malloc (sizeof (char));
	  entries[col][row].image   = (char *) malloc (sizeof (char));
	  entries[col][row].command = (char *) malloc (sizeof (char));
	  entries[col][row].isEmpty = 1;
	}
      }
    }
    /* rotate array */
    for (col = 0; col < nbCols; col++) {
      for (row = 0; row < nbRows; row++) {
	switch (config.direction) {
	case leftToRight:
	case topToBottom:
	  config.entries[col][row].isEmpty = entries[col][row].isEmpty;
	  config.entries[col][row].tooltip = (char *) malloc ((strlen (entries[col][row].tooltip)+1) * sizeof (char));
	  sprintf (config.entries[col][row].tooltip, "%s", entries[col][row].tooltip);
	  config.entries[col][row].image = (char *) malloc((strlen (entries[col][row].image)+1) * sizeof (char));
	  sprintf (config.entries[col][row].image, "%s", entries[col][row].image);
	  config.entries[col][row].command = (char *) malloc((strlen (entries[col][row].command)+1) * sizeof(char));
	  sprintf (config.entries[col][row].command, "%s", entries[col][row].command);
	  break;
	case rightToLeft:
	case bottomToTop:
	  config.entries[col][row].isEmpty = entries[col][nbRows - row - 1].isEmpty;
	  config.entries[col][row].tooltip = (char *) malloc ((strlen (entries[col][nbRows-row-1].tooltip)+1) * sizeof (char));
	  sprintf (config.entries[col][row].tooltip, "%s", entries[col][nbRows-row-1].tooltip);
	  config.entries[col][row].image = (char *) malloc((strlen (entries[col][nbRows-row-1].image)+1) * sizeof (char));
	  sprintf (config.entries[col][row].image, "%s", entries[col][nbRows-row-1].image);
	  config.entries[col][row].command = (char *) malloc((strlen (entries[col][nbRows-row-1].command)+1) * sizeof (char));
	  sprintf (config.entries[col][row].command, "%s", entries[col][nbRows - row - 1].command);
	  break;
	}
	dbg_msg (1, "(%d,%d) %s %s %s %d\n", col, row,
		 config.entries[col][row].tooltip,
		 config.entries[col][row].image,
		 config.entries[col][row].command,
		 config.entries[col][row].isEmpty);
      }
    }
  }
  else {
    printf ("%s warning: any button defined !!!\n", PACKAGE);
    drawerOK = nbCols = 0;
  }
  freeUnusedMemory ();
}

static void parseLine (char *line, int line_num) {
  unsigned int i, empty, error = 0, flag = 0, cntc = 0, cnte = 0;
  char c;
  const char *regex_general = "^[[:space:]]*[a-z_]+[[:space:]]+.+$";
  const char *regex_column  = "^[[:space:]]*\\(.*\\)[[:space:]]*\\(.*\\)[[:space:]]*\\(.*\\)[[:space:]]*$";
  char *param, *field;
  int field_point = 0;

  /* detect comment lines and empty lines */
  empty = 1;
  for(i=0; i<strlen (line); i++) {
    if (line[i] != ' ' && line[i] != '\t') {
      empty = 0;
      break;
    }
  }
  if (line[i] == '#' || line[i] == '!' || line[i] == ';' || line[i] == '\0') {
    return;
  }
  if (empty == 1) return;
  /* detect comment lines and sections*/
  if (strstr (line, "[general]") != NULL) {
    currentSection = general;
    return;
  }
  if (strstr (line, "[images_paths]") != NULL) {
    currentSection = images_paths;
    return;
  }
  if (strstr (line, "[column]") != NULL) {
    currentSection = column;
    nbCols++;
    if (entries == NULL && nbCols == 1) {
      /* alloc memory for first column */
      entries = (drawerButton **) xcalloc (1, sizeof (drawerButton *));
      nbRowsPerCol = (unsigned int *) xcalloc (1, sizeof (unsigned int));
    }
    else {
      /* realloc memory for a new column */
      entries = xrealloc (entries, sizeof (drawerButton *) * nbCols);
      nbRowsPerCol = xrealloc (nbRowsPerCol, sizeof (unsigned int) * nbCols);
    }
    nbRowsPerCol[nbCols - 1] = 0;
    rowsCnt = 0;
    return;
  }

  param = (char *) malloc (strlen (line) * sizeof (char));
  field = (char *) malloc (strlen (line) * sizeof (char));
  switch (currentSection) {
  case general:
    /* first verify if line match regexp else it's skipped */
    if (match_regex (line, regex_general) == 0) {
      printf ("%s warning: line %d is invalid (skipped)\n", PACKAGE, line_num);
      printf ("Line %d: %s\n", line_num, line);
      break;
    }
    if (strstr (line, "dock_icon")) {
      sscanf (line, "%s %s", param, field);
      config.dockIcon = (char *) malloc ((strlen (field)+1) * sizeof (char));
      sprintf(config.dockIcon, "%s", field);
      useDefaultDockIcon = 0;
      break;
    }
    else if (strstr (line, "icons_bg")) {
      sscanf (line, "%s %s", param, field);
      config.iconsBg = (char *) malloc ((strlen (field)+1) * sizeof (char));
      sprintf(config.iconsBg, "%s", field); 
      useDefaultIconsBg = 0;
      break;
    }
    else if (strstr (line, "icons_expand")) {
      sscanf (line, "%s %d", param, &(config.iconsExpand));
      if (config.iconsExpand != 0 && config.iconsExpand != 1) {
	printf ("%s error: bad value for 'icons_expand' param, it must be 0 or 1 (use default value: 1)\n", PACKAGE);
	config.iconsExpand = 1;
      }
      break;
    }
    else if (strstr (line, "transparency")) {
      sscanf (line, "%s %d", param, &(config.transparency));
      if (config.transparency != 0 && config.transparency != 1) {
	printf ("%s warning: bad value for 'transparency' param, it must be 0 or 1 (use default value: 0)\n", PACKAGE);
	config.transparency = 0;
      }
      break;
    }
    else if (strstr (line, "dock_size")) {
      printf ("%s warning: old option 'dock_size' found, use dock_width and dock_height instead\n", PACKAGE);
      break;
    }
    else if (strstr (line, "dock_width")) {
      sscanf (line, "%s %d", param, &(config.dockW));
      if (config.dockW > MAX_ICON_SIZE) {
	printf ("%s warning: bad value for 'dock_width' param, max=%d (use max value: %d)\n",
		PACKAGE, MAX_ICON_SIZE, MAX_ICON_SIZE);
	config.dockW = MAX_ICON_SIZE;
      }
      if (config.dockW < MIN_ICON_SIZE) {
        printf ("%s warning: bad value for 'dock_width' param, min=%d (use min value: %d)\n",
		PACKAGE, MIN_ICON_SIZE, MIN_ICON_SIZE);
        config.dockW = MIN_ICON_SIZE;
      }
      break;
    }
    else if (strstr (line, "dock_height")) {
      sscanf (line, "%s %d", param, &(config.dockH));
      if (config.dockH > MAX_ICON_SIZE) {
        printf ("%s warning: bad value for 'dock_height' param, max=%d (use max value: %d)\n",
                PACKAGE, MAX_ICON_SIZE, MAX_ICON_SIZE);
        config.dockH = MAX_ICON_SIZE;
      }
      if (config.dockH < MIN_ICON_SIZE) {
        printf ("%s warning: bad value for 'dock_height' param, min=%d (use min value: %d)\n",
                PACKAGE, MIN_ICON_SIZE, MIN_ICON_SIZE);
        config.dockH = MIN_ICON_SIZE;
      }
      break;
    }
    else if (strstr (line, "btns_size")) {
      sscanf (line, "%s %d", param, &(config.btnsSize));
      if (config.btnsSize > MAX_ICON_SIZE) {
	printf ("%s warning: bad value for 'btns_size' param, max=%d (use max value: %d)\n",
		PACKAGE, MAX_ICON_SIZE, MAX_ICON_SIZE);
	config.btnsSize = MAX_ICON_SIZE;
      }
      if (config.btnsSize < MIN_ICON_SIZE) {
	printf ("%s warning: bad value for 'btns_size' param, min=%d (use min value: %d)\n",
		PACKAGE, MIN_ICON_SIZE, MIN_ICON_SIZE);
	config.btnsSize = MIN_ICON_SIZE;
      }
      break;
    }
    else if (strstr (line, "direction")) {
      sscanf (line, "%s %d", param, &(config.direction));
      if (config.direction < 0 || config.direction > 3) {
	printf ("%s error: bad value for 'direction' param, it must be 0, 1, 2 or 3\n", PACKAGE);
	config.direction = -1;
      }
      break;
    }
    else if (strstr (line, "animation_speed")) {
      sscanf (line, "%s %d", param, &(config.animationSpeed));
      if (config.animationSpeed < 0) {
	printf ("%s warning: bad value for 'animation_speed' param, it must be 0, 1, 2, 3 or 4 (use default value: 1)\n", PACKAGE);
	config.animationSpeed = 1;
      }
      if (config.animationSpeed > 4) {
	printf ("%s warning: bad value for 'animation_speed' param, it must be 0, 1, 2, 3 or 4 (use max value: 4)\n", PACKAGE);
	config.animationSpeed = 4;
      }
      break;
    }
    else if (strstr (line, "cursor")) {
      sscanf (line, "%s %d", param, &(config.cursor));
      if (config.cursor < 0 || config.cursor > 76) {
	printf ("%s warning: bad value for 'cursor' param, it must be >= 0 and <= 76 (use default value: 30)\n", PACKAGE);
	config.cursor = 30; /* XC_hand2 / 2 */
      }
      break;
    }
    else if (strstr (line, "border_size")) {
      sscanf (line, "%s %d", param, &(config.borderSize));
      if (config.borderSize < 0 || config.borderSize > 8) {
	printf ("%s warning: bad value for 'border_size' param, it must be >= 0 and not too big (use default value: 1)\n", PACKAGE);
	config.borderSize = 1;
      }
      break;
    }
    else if (strstr (line, "show_on_hover") || strstr (line, "show_on_over")) {
      sscanf (line, "%s %d", param, &(config.showOnHover));
      if (config.showOnHover != 0 && config.showOnHover != 1) {
	printf ("%s warning: bad value for 'show_on_hover' param, it must be 0 or 1 (use default value: 0)\n", PACKAGE);
	config.showOnHover = 0;
      }
      break;
    }
    else if (strstr (line, "hide_on_out")) {
      sscanf (line, "%s %d", param, &(config.hideOnOut));
      if (config.hideOnOut != 0 && config.hideOnOut != 1) {
	printf ("%s warning: bad value for 'hide_on_out' param, it must be 0 or 1 (use default value: 0)\n", PACKAGE);
	config.hideOnOut = 0;
      }
      break;
    }
    else if (strstr (line, "hide_timeout")) {
      sscanf (line, "%s %d", param, &(config.hideTimeout));
      if (config.hideTimeout < 0) {
	printf ("%s warning: bad value for 'hide_timeout' param, it must be >= to 0 (use default value: 1000)\n", PACKAGE);
	config.hideTimeout = 1000;
      }
      break;
    }
    else if (strstr (line, "windowed_mode")) {
      if (config.windowedModeLocked == 0) {
	sscanf (line, "%s %d", param, &(config.windowedMode));
	if (config.windowedMode != 0 && config.windowedMode != 1) {
	  printf ("%s warning: bad value for 'windowed_mode' param, it must be 0 or 1 (use default value: 0)\n", PACKAGE);
	  config.windowedMode = 0;
	}
      }
      break;
    }
    else if (strstr (line, "instance_name")) {
      /* Ed Goforth <e.goforth@computer.org> */
      /* Allow to specify instance name for multiple clients in their */
      /* config file. This will override a -n given on the command line. */
      sscanf (line, "%s %s", param, field);
      config.instanceName = (char *) malloc ((strlen (field)+1)*sizeof (char));
      sprintf(config.instanceName, "%s", field);
      break;
    }
    else if (strstr (line, "highlight_img")) {
      sscanf (line, "%s %s", param, field);
      config.highlightImg = (char *) malloc ((strlen (field)+1)*sizeof (char));
      sprintf(config.highlightImg, "%s", field);
      useDefaultHighlightImg = 0;
      break;
    }
    else if (strstr (line, "highlight_sh")) {
      sscanf (line, "%s %d", param, &(config.highlightShading.shading));
      if (config.highlightShading.shading < 0 ||
	  config.highlightShading.shading > 100) {
	printf ("%s warning: bad value for 'highlight_sh' param, it must be >= 0 and <= 100 (use default value: 60)\n", PACKAGE);
	config.highlightShading.shading = 60;
      }
      break;
    }
    else if (strstr (line, "highlight_tint")) {
      unsigned int r,g,b;
      sscanf (line, "%s %c%02X%02X%02X", param, &c, &r, &g, &b);
      if (c != '#') {
	printf ("%s warning: bad value for 'highlight_tint' param (use default value: #ffffff)\n", PACKAGE);
	config.highlightShading.tintColor.red   = 255;
	config.highlightShading.tintColor.green = 255;
	config.highlightShading.tintColor.blue  = 255;
      }
      else {
	config.highlightShading.tintColor.red   = (unsigned short) r;
	config.highlightShading.tintColor.green = (unsigned short) g;
	config.highlightShading.tintColor.blue  = (unsigned short) b;
      }
      break;
    }
    else if (strstr (line, "highlight")) {
      sscanf (line, "%s %d", param, &(config.highlight));
      if (config.highlight < 0 || config.highlight > 2) {
	printf ("%s warning: bad value for 'highlight' param, it must be 0, 1 or 2 (use default value: 2)\n", PACKAGE);
	config.highlight = 2;
      }
      break;
    }
    else if (strstr (line, "tooltips_font")) {
      free (config.tooltipsFont);
      sscanf (line, "%s %s", param, field);
      config.tooltipsFont = (char *) malloc ((strlen (field)+1)*sizeof (char));
      sprintf(config.tooltipsFont, "%s", field);
      break;
    }
    else if (strstr (line, "tooltips")) {
      sscanf (line, "%s %d", param, &(config.tooltips));
      if (config.tooltips != 0 && config.tooltips != 1) {
	printf ("%s warning: bad value for 'tooltips' param, it must be 0 or 1 (use default value: 1)\n", PACKAGE);
	config.tooltips = 1;
      }
      break;
    }
    break;
  case images_paths:
    nbImagesPaths++;
    if (config.imagesPaths == NULL && nbImagesPaths == 1) {
      config.imagesPaths = (char **) xcalloc (1, sizeof (char *));
    }
    else {
      config.imagesPaths = (char **) xrealloc (config.imagesPaths, sizeof (char *) * nbImagesPaths);
    }
    config.imagesPaths[nbImagesPaths - 1] = (char *) xcalloc (strlen(line)+1,
							      sizeof (char));
    sscanf (line, "%s", config.imagesPaths[nbImagesPaths - 1]);
    break;
  case column:
    /* if a line don't match regexp => it's skipped */
    if (match_regex (line, regex_column) == 0) {
      printf ("%s warning: line %d is invalid (skipped)\n", PACKAGE, line_num);
      printf ("Line %d: %s\n", line_num, line);
      break;
    }
    rowsCnt++;
    if (rowsCnt == 1) {
      /* alloc memory for first button */
      entries[nbCols-1] = (drawerButton *) xcalloc (1, sizeof (drawerButton));
    }
    else {
      /* realloc memory for new button */
      entries[nbCols-1] = (drawerButton *) xrealloc (entries[nbCols - 1],
						     sizeof (drawerButton) * rowsCnt);
    }
    /* parse entry line */
    while (sscanf (line + cntc, "%c", &c)) {
      switch (c) {
      case '(':
	flag = 1;
	break;
      case ')':
	flag = 0;
	field[field_point] = '\0';
        switch (cnte++) {
	case 0:
	  entries[nbCols - 1][rowsCnt - 1].tooltip = (char *) xcalloc (strlen (field)+1, sizeof (char));
	  sprintf (entries[nbCols - 1][rowsCnt - 1].tooltip, "%s", field);
	  break;
	case 1:
	  entries[nbCols - 1][rowsCnt - 1].image = (char *) xcalloc (strlen (field)+1, sizeof (char));
	  sprintf (entries[nbCols - 1][rowsCnt - 1].image, "%s", field);
	  break;
	case 2:
	  entries[nbCols - 1][rowsCnt - 1].command = (char *) xcalloc (strlen (field)+1, sizeof (char));
	  sprintf (entries[nbCols - 1][rowsCnt - 1].command, "%s", field);
	  break;
        }
	field_point = 0;
	break;
      default:
	if (flag == 1) {
	  field[field_point++] = c;
	  field[field_point] = '\0';
	}
	break;
      }
      cntc++;
      dbg_msg (1, "%c %d %d %d %s\n", c, flag, cnte, cntc, field);
      if (cnte == 3) {
            dbg_msg (1, "%d %d %d | %s | %s | %s\n", flag, cnte, cntc,
		     entries[nbCols - 1][rowsCnt - 1].tooltip,
		     entries[nbCols - 1][rowsCnt - 1].image,
		     entries[nbCols - 1][rowsCnt - 1].command);
      }
      if (cntc >= strlen (line)) break;
    }
    dbg_msg (1, "%s\t%s\t%s\n", entries[nbCols - 1][rowsCnt - 1].tooltip,
	     entries[nbCols - 1][rowsCnt - 1].image,
	     entries[nbCols - 1][rowsCnt - 1].command);
    entries[nbCols - 1][rowsCnt - 1].isEmpty = 0;
    /* we count the number of buttons per column */
    nbRowsPerCol[nbCols - 1]++;
    if (rowsCnt > nbRows) {
      nbRows = rowsCnt;
    }
    break;
  default:
    error++;
  }
  free (field);
  free (param);
  if (error > 0) {
    dbg_msg (1, "%s\n", line);
    printf ("%s error: parsing error.\n", PACKAGE);
    printf ("Line %d: %s\n", line_num, line);
    freeAllMemory ();
    exit (EXIT_FAILURE);
  }
}

int configChanged (void) {
  struct stat buf;

  if (stat (configFilePath, &buf) == 0 && buf.st_mtime > config.lastModif) {
    return 1;
  }
  else {
    return 0;
  }
}

static void usage (void) {
#if defined USE_GETOPT
  printf ("Usage: %s [OPTIONS]\n\n"
	  "Valid options are:\n"
	  "  -c FILE   config file to use (default ~/.wmdrawerrc)\n"
	  "  -n NAME   set dock instance name\n"
	  "  -w        runs the application in windowed mode\n"
	  "  -v        show program version and exit\n"
	  "  -h        show this help text and exit\n\n", PACKAGE);
#else
  printf ("Usage: %s [OPTIONS]\n\n"
	  "Valid options are:\n"
	  "  -c, --configfile=FILE     config file to use (default ~/.wmdrawerrc)\n"
	  "  -n, --instancename=NAME   set dock instance name\n"
	  "  -w, --windowed            runs the application in windowed mode\n"
	  "  -v, --version             show program version and exit\n"
	  "  -h, --help                show this help text and exit\n\n", PACKAGE);
#endif
}

void parseOptions (int argc, char **argv) {
  int opt;

#if defined USE_GETOPT
#else
  int longopt_index = 0;
  static struct option long_options[] = {
    {"configfile",   1, NULL, 'c'},
    {"help",         0, NULL, 'h'},
    {"instancename", 1, NULL, 'n'},
    {"version",      0, NULL, 'v'},
    {"windowed",     0, NULL, 'w'},
    {NULL, 0, NULL, 0} /* marks end-of-list */
  };
#endif

  config.windowedMode = 0;
  /* Ed Goforth <e.goforth@computer.org> */
  /* use the package name as the default instance name */
  config.instanceName=(char *)malloc((strlen(PACKAGE)+1)*sizeof(char));
  sprintf (config.instanceName, "%s", PACKAGE);

#if defined USE_GETOPT
  while ((opt = getopt (argc, argv, "hvw?c:n:")) != EOF) {
#else
  while ((opt = getopt_long (argc, argv, "c:h?n:vw", long_options,
			     &longopt_index)) > 0) {
#endif
    dbg_msg (1, "Between while & switch: opt = '%c'\n", opt);
    switch (opt) {
    case '?':
    case 'h':
      usage ();
      exit (EXIT_SUCCESS);
    case 'w':
      config.windowedMode = 1;
      config.windowedModeLocked = 1;
      break;
    case 'v':
      printf ("Version %s, %s\n"
	      "%s (c) 2002-2004 Valéry Febvre <vfebvre@vfebvre.lautre.net>\n\n",
	      VERSION, RELEASE_DATE, PACKAGE);
      exit (EXIT_SUCCESS);
    case 'c':
      sprintf (configFilePath, "%s", optarg);
      break;
    case 'n':
      /* Ed Goforth <e.goforth@computer.org> */
      /* allow on command-line, but config file overrides it if given there */
      config.instanceName = (char *) malloc ((strlen (optarg)+1)*sizeof(char));
      sprintf (config.instanceName, "%s", optarg);
      break;
    }
  }
}

void freeUnusedMemory (void) {
  unsigned int i, j;

  free (line);
  free (nbRowsPerCol);
  for (i=0; i<nbCols; i++) {
    for (j=0; j<nbRows; j++) {
      free (entries[i][j].tooltip);
      free (entries[i][j].image);
      free (entries[i][j].command);
    }
    free (entries[i]);
  }
  free (entries);
}

void freeAllMemory (void) {
  unsigned int i;

  for (i=0; i<nbImagesPaths; i++) {
    free (config.imagesPaths[i]);
  }
  free (config.imagesPaths);
  config.imagesPaths = NULL;
  for (i=0; i<nbCols; i++) {
    free (config.entries[i]);
  }
  free (config.entries);
  config.entries = NULL;
}
