/* This module, and the entire ModuleDebugger program, and the concept for
 * interfacing this module to the Window Manager, are all original work
 * by Robert Nation
 *
 * Copyright 1994, 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. */

#define TRUE 1
#define FALSE

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#include <ctype.h>
#ifdef ISC			/* Saul */
#include <sys/bsdtypes.h>	/* Saul */
#endif /* Saul */

#include <stdlib.h>
#if defined ___AIX || defined _AIX || defined __QNX__ || defined ___AIXV3 || defined AIXV3 || defined _SEQUENT_
#include <sys/select.h>
#endif

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>

#include "constant.h"
#include "utils.h"
#include "module.h"
#include "../xfwm/xfwm.h"

#include "XfwmPager.h"

#ifdef DMALLOC
#  include "dmalloc.h"
#endif

char *MyName;
int fd_width;
int fd[2];

/*************************************************************************
 * 
 * Screen, font, etc info
 *
 **************************************************************************/
ScreenInfo Scr;
PagerWindow *Start = NULL;
PagerWindow *FocusWin = NULL;

extern void DispatchEvent (XEvent * Event);

Display *dpy;			/* which display are we talking to */
int x_fd, fd_width;

char *PagerFore = "black";
char *PagerBack = "grey50";
char *font_string = "fixed";
char *smallFont = "5x8";
char *HilightC = "grey90";
char *WindowBack = NULL;
char *WindowFore = NULL;
char *WindowHiBack = NULL;
char *WindowHiFore = NULL;

int window_w = 100, window_h = 100, window_x = 0, window_y = 0;
int icon_x = -10000, icon_y = -10000, icon_w = 50, icon_h = 50;
int usposition = 0, uselabel = 1;
int xneg = 0, yneg = 0;
extern DeskInfo *Desks;
int StartIconic = 0;
int Rows = 2, Columns = -1;
int desk2 = 9;
int ndesks = 0;
Pixel win_back_pix = -1;
Pixel win_fore_pix = -1;
Pixel win_hi_back_pix = -1;
Pixel win_hi_fore_pix = -1;
/***********************************************************************
 *
 *  Procedure:
 *	main - start of module
 *
 ***********************************************************************/
int
main (int argc, char **argv)
{
  char *temp, *s;
  char *display_name = NULL;
  int i;
  char line[100];

  /* Save our program  name - for error messages */
  temp = argv[0];
  s = strrchr (argv[0], '/');
  if (s != NULL)
    temp = s + 1;

  MyName = safemalloc (strlen (temp) + 2);
  strcpy (MyName, temp);

  if (argc < 7)
  {
    fprintf (stderr, "%s Version %s should only be executed by xfwm!\n", MyName, VERSION);
    exit (1);
  }

  /* Initialize X connection */
  if (!(dpy = XOpenDisplay (display_name)))
  {
    fprintf (stderr, "%s: can't open display %s", MyName, XDisplayName (display_name));
    exit (1);
  }
  x_fd = XConnectionNumber (dpy);

  Scr.screen = DefaultScreen (dpy);
  Scr.Root = RootWindow (dpy, Scr.screen);

  if (Scr.Root == None)
  {
    fprintf (stderr, "%s: Screen %d is not valid ", MyName, (int) Scr.screen);
    exit (1);
  }
  Scr.d_depth = DefaultDepth (dpy, Scr.screen);

  /* Check number of desks */
  if (argc > 7)
  {
    desk2 = atoi (argv[7]) - 1;
    if (desk2 < 1)
      desk2 = 1;
    else if (desk2 > NBSCREENS)
      desk2 = NBSCREENS;
  }
  else
  {
    Atom atype;
    int aformat;
    unsigned long nitems, bytes_remain;
    unsigned char *prop;
    Atom wm_workspace_count = XInternAtom (dpy, "_WIN_WORKSPACE_COUNT", False);
    if ((XGetWindowProperty (dpy, Scr.Root, wm_workspace_count, 0L, 1L, False, XA_CARDINAL, &atype, &aformat, &nitems, &bytes_remain, &prop)) == Success)
    {
      if (prop != NULL)
      {
	desk2 = *(unsigned long *) prop - 1;
	if (desk2 < 1)
	  desk2 = 1;
	if (desk2 > NBSCREENS)
	  desk2 = NBSCREENS;
	XFree (prop);
      }
      else
	desk2 = NBSCREENS;
    }
    else
      desk2 = NBSCREENS;
  }

  /* Dead pipe == XFwm died */
  signal (SIGPIPE, DeadPipe);

  fd[0] = atoi (argv[1]);
  fd[1] = atoi (argv[2]);

  fd_width = GetFdWidth ();

  ndesks = desk2 + 1;

  Desks = (DeskInfo *) malloc (ndesks * sizeof (DeskInfo));
  for (i = 0; i < ndesks; i++)
  {
    sprintf (line, "%d", i + 1);
    CopyString (&Desks[i].label, line);
    CopyString (&Desks[i].Dcolor, PagerBack);
  }

  SetMessageMask (fd, XFCE_M_ADD_WINDOW | XFCE_M_CONFIGURE_WINDOW | XFCE_M_DESTROY_WINDOW | XFCE_M_FOCUS_CHANGE | XFCE_M_NEW_DESK | XFCE_M_RAISE_WINDOW | XFCE_M_LOWER_WINDOW | XFCE_M_ICONIFY | XFCE_M_ICON_LOCATION | XFCE_M_DEICONIFY | XFCE_M_SHADE | XFCE_M_UNSHADE | XFCE_M_ICON_NAME | XFCE_M_CONFIG_INFO | XFCE_M_END_CONFIG_INFO | XFCE_M_END_WINDOWLIST | XFCE_M_RESTACK);
  window_w = 40 * ((desk2 + 1) / Rows);
  window_h = 40 * (Rows);
  ParseOptions ();
  /* open a pager window */
  initialize_pager ();

  /* Create a list of all windows */
  /* Request a list of all windows,
   * wait for ConfigureWindow packets */
  SendInfo (fd, "Send_WindowList", 0);

  Loop (fd);

  return (0);
}

/***********************************************************************
 *
 *  Procedure:
 *	Loop - wait for data to process
 *
 ***********************************************************************/
void
Loop (int *fd)
{
  XEvent Event;

  while (1)
  {
    if (My_XNextEvent (dpy, &Event))
      DispatchEvent (&Event);
  }
}


/***********************************************************************
 *
 *  Procedure:
 *	Process message - examines packet types, and takes appropriate action
 *
 ***********************************************************************/
void
process_message (unsigned long type, unsigned long *body)
{
  switch (type)
  {
  case XFCE_M_ADD_WINDOW:
    list_configure (body);
    break;
  case XFCE_M_CONFIGURE_WINDOW:
    list_configure (body);
    break;
  case XFCE_M_DESTROY_WINDOW:
    list_destroy (body);
    break;
  case XFCE_M_FOCUS_CHANGE:
    list_focus (body);
    break;
  case XFCE_M_NEW_DESK:
    list_new_desk (body);
    break;
  case XFCE_M_RAISE_WINDOW:
    list_raise (body);
    break;
  case XFCE_M_LOWER_WINDOW:
    list_lower (body);
    break;
  case XFCE_M_ICONIFY:
  case XFCE_M_ICON_LOCATION:
    list_iconify (body);
    break;
  case XFCE_M_DEICONIFY:
    list_deiconify (body);
    break;
  case XFCE_M_ICON_NAME:
    list_icon_name (body);
    break;
  case XFCE_M_END_WINDOWLIST:
    list_restack ();
    break;
  case XFCE_M_RESTACK:
    list_restack ();
    break;
  default:
    list_unknown (body);
    break;
  }
}


/***********************************************************************
 *
 *  Procedure:
 *	SIGPIPE handler - SIGPIPE means xfwm is dying
 *
 ***********************************************************************/
void
DeadPipe (int nonsense)
{
  exit (0);
}

/***********************************************************************
 *
 *  Procedure:
 *	list_add - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_add (unsigned long *body)
{
  PagerWindow *t, **prev;
  int i = 0;

  t = Start;
  prev = &Start;
  while (t != NULL)
  {
    prev = &(t->next);
    t = t->next;
    i++;
  }
  *prev = (PagerWindow *) safemalloc (sizeof (PagerWindow));
  (*prev)->w = body[0];
  (*prev)->t = (char *) body[2];
  (*prev)->frame = body[1];
  (*prev)->desk = body[7];
  (*prev)->next = NULL;
  (*prev)->flags = body[8];
  (*prev)->pager_view_width = 0;
  (*prev)->pager_view_height = 0;
  (*prev)->icon_view_width = 0;
  (*prev)->icon_view_height = 0;
  (*prev)->icon_name = NULL;
  (*prev)->title_height = body[9];
  (*prev)->border_width = body[10];
  (*prev)->icon_w = body[19];
  (*prev)->icon_pixmap_w = body[20];
  if ((win_fore_pix != -1) && (win_back_pix != -1))
  {
    (*prev)->text = win_fore_pix;
    (*prev)->back = win_back_pix;
  }
  else
  {
    (*prev)->text = body[22];
    (*prev)->back = body[23];
  }
  (*prev)->shade_x = body[24];
  (*prev)->shade_y = body[25];
  (*prev)->shade_width = body[26];
  (*prev)->shade_height = body[27];
  if ((*prev)->flags & SHADED)
  {
    (*prev)->x = body[24];
    (*prev)->y = body[25];
    (*prev)->width = body[26];
    (*prev)->height = body[27];
  }
  else
  {
    (*prev)->x = body[3];
    (*prev)->y = body[4];
    (*prev)->width = body[5];
    (*prev)->height = body[6];
  }
  AddNewWindow (*prev);
}

/***********************************************************************
 *
 *  Procedure:
 *	list_configure - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_configure (unsigned long *body)
{
  PagerWindow *t;
  Window target_w;

  target_w = body[0];
  t = Start;
  while ((t != NULL) && (t->w != target_w))
  {
    t = t->next;
  }
  if (t == NULL)
  {
    list_add (body);

  }
  else
  {
    t->t = (char *) body[2];
    t->frame = body[1];
    t->frame_x = body[3];
    t->frame_y = body[4];
    t->frame_width = body[5];
    t->frame_height = body[6];
    t->title_height = body[9];
    t->border_width = body[10];
    t->flags = body[8];
    t->icon_w = body[19];
    t->icon_pixmap_w = body[20];
    t->shade_x = body[24];
    t->shade_y = body[25];
    t->shade_width = body[26];
    t->shade_height = body[27];
    if ((win_fore_pix != -1) && (win_back_pix != -1))
    {
      t->text = win_fore_pix;
      t->back = win_back_pix;
    }
    else
    {
      t->text = body[22];
      t->back = body[23];
    }
    if (t->flags & ICONIFIED)
    {
      t->x = t->icon_x;
      t->y = t->icon_y;
      t->width = t->icon_width;
      t->height = t->icon_height;
      if (t->flags & SUPPRESSICON)
      {
	t->x = -10000;
	t->y = -10000;
      }
    }
    else if (t->flags & SHADED)
    {
      t->x = t->shade_x;
      t->y = t->shade_y;
      t->width = t->shade_width;
      t->height = t->shade_height;
    }
    else
    {
      t->x = t->frame_x;
      t->y = t->frame_y;
      t->width = t->frame_width;
      t->height = t->frame_height;
    }
    if (t->desk != body[7])
    {
      ChangeDeskForWindow (t, body[7]);
    }

    else
      MoveResizePagerView (t);
    if (FocusWin == t)
      Hilight (t, ON);
    else
      Hilight (t, OFF);
  }
}

/***********************************************************************
 *
 *  Procedure:
 *	list_destroy - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_destroy (unsigned long *body)
{
  PagerWindow *t, **prev;
  Window target_w;

  target_w = body[0];
  t = Start;
  prev = &Start;
  while ((t != NULL) && (t->w != target_w))
  {
    prev = &(t->next);
    t = t->next;
  }
  if (t != NULL)
  {
    if (prev != NULL)
      *prev = t->next;
    /* remove window from the chain */
    if (t->PagerView != None)
      XDestroyWindow (dpy, t->PagerView);
    XDestroyWindow (dpy, t->IconView);
    if (FocusWin == t)
      FocusWin = NULL;

    free (t);
  }
}

/***********************************************************************
 *
 *  Procedure:
 *	list_focus - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_focus (unsigned long *body)
{
  PagerWindow *t, *temp;
  Window target_w;
  extern Pixel focus_pix, focus_fore_pix;
  target_w = body[0];

  if ((win_hi_fore_pix != -1) && (win_hi_back_pix != -1))
  {
    focus_pix = win_hi_back_pix;
    focus_fore_pix = win_hi_fore_pix;
  }
  else
  {
    focus_pix = body[4];
    focus_fore_pix = body[3];
  }
  t = Start;
  while ((t != NULL) && (t->w != target_w))
  {
    t = t->next;
  }
  if (t != FocusWin)
  {
    temp = FocusWin;
    FocusWin = t;

    if (temp != NULL)
      Hilight (temp, OFF);
    if (FocusWin != NULL)
      Hilight (FocusWin, ON);
  }
}

/***********************************************************************
 *
 *  Procedure:
 *	list_new_desk - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_new_desk (unsigned long *body)
{
  int oldDesk;

  oldDesk = Scr.CurrentDesk;
  Scr.CurrentDesk = (long) body[0];

  MovePage ();

  DrawGrid (oldDesk, 1);
  DrawGrid (Scr.CurrentDesk, 1);
  MoveStickyWindows ();
  Hilight (FocusWin, OFF);
  Hilight (FocusWin, ON);
}

/***********************************************************************
 *
 *  Procedure:
 *	list_raise - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_raise (unsigned long *body)
{
  /* Nothing to do, all handle by new RESTACK message */
}


/***********************************************************************
 *
 *  Procedure:
 *	list_lower - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_lower (unsigned long *body)
{
  /* Nothing to do, all handle by new RESTACK message */
}

/***********************************************************************
 *
 *  Procedure:
 *	list_unknow - handles an unrecognized packet.
 *
 ***********************************************************************/
void
list_unknown (unsigned long *body)
{
  /*  fprintf(stderr,"Unknown packet type\n"); */
}

/***********************************************************************
 *
 *  Procedure:
 *	list_iconify - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_iconify (unsigned long *body)
{
  PagerWindow *t;
  Window target_w;

  target_w = body[0];
  t = Start;
  while ((t != NULL) && (t->w != target_w))
  {
    t = t->next;
  }
  if (t == NULL)
  {
    return;
  }
  else
  {
    t->t = (char *) body[2];
    t->frame = body[1];
    t->icon_x = body[3];
    t->icon_y = body[4];
    t->icon_width = body[5];
    t->icon_height = body[6];
    t->flags |= ICONIFIED;
    t->x = t->icon_x;
    t->y = t->icon_y;
    if (t->flags & SUPPRESSICON)
    {
      t->x = -10000;
      t->y = -10000;
    }
    t->width = t->icon_width;
    t->height = t->icon_height;
    MoveResizePagerView (t);
  }
}


/***********************************************************************
 *
 *  Procedure:
 *	list_deiconify - displays packet contents to stderr
 *
 ***********************************************************************/

void
list_deiconify (unsigned long *body)
{
  PagerWindow *t;
  Window target_w;

  target_w = body[0];
  t = Start;
  while ((t != NULL) && (t->w != target_w))
  {
    t = t->next;
  }
  if (t == NULL)
  {
    return;
  }
  else
  {
    t->flags &= ~ICONIFIED;
    if (t->flags & SHADED)
    {
      t->x = t->shade_x;
      t->y = t->shade_y;
      t->width = t->shade_width;
      t->height = t->shade_height;
    }
    else
    {
      t->x = t->frame_x;
      t->y = t->frame_y;
      t->width = t->frame_width;
      t->height = t->frame_height;
    }
    MoveResizePagerView (t);
    if (FocusWin == t)
      Hilight (t, ON);
    else
      Hilight (t, OFF);
  }
}

/***********************************************************************
 *
 *  Procedure:
 *	list_icon_name - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_icon_name (unsigned long *body)
{
  PagerWindow *t;
  Window target_w;

  target_w = body[0];
  t = Start;
  while ((t != NULL) && (t->w != target_w))
  {
    t = t->next;
  }
  if (t != NULL)
  {
    if (t->icon_name != NULL)
      free (t->icon_name);
    CopyString (&t->icon_name, (char *) (&body[3]));
    LabelWindow (t);
    LabelIconWindow (t);
  }
}



/***********************************************************************
 *
 *  Procedure:
 *	list_restack - displays packet contents to stderr
 *
 ***********************************************************************/
void
list_restack (void)
{
#if 0
  /* Broken code (kept for further analysis) */

  unsigned int nchildren, i, j, k;
  Window root, parent, *children;
  Window *wins;
  PagerWindow *ptr;

  if (!XQueryTree (dpy, Scr.Root, &root, &parent, &children, &nchildren))
    return;

  wins = (Window *) safemalloc ((nchildren + 1) * sizeof (Window));

  for (j = 0; j < ndesks; j++)
  {
    k = 0;
    for (i = 0; i < nchildren; i++)
    {
      ptr = Start;
      while (ptr != NULL)
      {
	if (ptr->desk == j)
	{
	  if ((ptr->frame == children[i]) || (ptr->icon_w == children[i]) || (ptr->icon_pixmap_w == children[i]))
	  {
	    if (ptr->PagerView != None)
	      XRaiseWindow (dpy, ptr->PagerView);
	    wins[k++] = ptr->IconView;
	  }
	}
	ptr = ptr->next;
      }
    }
    if (k > 0)
      XRaiseWindow (dpy, wins[0]);

    XRestackWindows (dpy, wins, k);
  }

  free (wins);
  if (nchildren > 0)
    XFree ((char *) children);
#endif

  unsigned int nchildren, i;
  Window root, parent, *children;
  PagerWindow *ptr;

  if (!XQueryTree (dpy, Scr.Root, &root, &parent, &children, &nchildren))
    return;

  for (i = 0; i < nchildren; i++)
  {
    ptr = Start;
    while (ptr != NULL)
    {
      if ((ptr->frame == children[i]) || (ptr->icon_w == children[i]) || (ptr->icon_pixmap_w == children[i]))
      {
	if (ptr->PagerView != None)
	  XRaiseWindow (dpy, ptr->PagerView);
	XRaiseWindow (dpy, ptr->IconView);
      }
      ptr = ptr->next;
    }
  }


  if (nchildren > 0)
    XFree ((char *) children);
}




/***************************************************************************
 *
 * Waits for next X event, or for an auto-raise timeout.
 *
 ****************************************************************************/
int
My_XNextEvent (Display * dpy, XEvent * event)
{
  fd_set in_fdset;
  unsigned long header[HEADER_SIZE];
  int count = 0;
  static int miss_counter = 0;
  unsigned long *body;

  if (XPending (dpy))
  {
    XNextEvent (dpy, event);
    return 1;
  }

  FD_ZERO (&in_fdset);
  FD_SET (x_fd, &in_fdset);
  FD_SET (fd[1], &in_fdset);

#ifdef __hpux
  select (fd_width, (fd_set *) & in_fdset, 0, 0, NULL);
#else
  select (fd_width, &in_fdset, 0, 0, NULL);
#endif

  if (FD_ISSET (x_fd, &in_fdset))
  {
    if (XPending (dpy))
    {
      XNextEvent (dpy, event);
      miss_counter = 0;
      return 1;
    }
    else
      miss_counter++;
    if (miss_counter > 100)
      DeadPipe (0);
  }

  if (FD_ISSET (fd[1], &in_fdset))
  {
    if ((count = ReadXfwmPacket (fd[1], header, &body)) > 0)
    {
      process_message (header[1], body);
      free (body);
    }
  }
  return 0;
}



/*****************************************************************************
 * 
 * This routine is responsible for reading and parsing the config file
 *
 ****************************************************************************/
void
ParseOptions ()
{
  char *tline = NULL, *tmp;
  int Clength, n, desk;

  Scr.XfwmRoot = NULL;
  Scr.Hilite = NULL;
  Scr.VScale = 32;

  Scr.MyDisplayWidth = DisplayWidth (dpy, Scr.screen);
  Scr.MyDisplayHeight = DisplayHeight (dpy, Scr.screen);

  Clength = strlen (MyName);
  GetConfigLine (fd, &tline);

  while (tline != NULL)
  {
    int g_x, g_y, flags;
    unsigned width, height;

    if ((strlen (&tline[0]) > 1) && (mystrncasecmp (tline, CatString3 ("*", MyName, ".geometry"), Clength + 10) == 0))
    {
      tmp = &tline[Clength + 10];
      while (((isspace (*tmp)) && (*tmp != '\n')) && (*tmp != 0))
      {
	tmp++;
      }
      tmp[strlen (tmp) - 1] = 0;
      flags = XParseGeometry (tmp, &g_x, &g_y, &width, &height);
      if (flags & WidthValue)
      {
	window_w = width;
      }
      if (flags & HeightValue)
      {
	window_h = height;
      }
      if (flags & XValue)
      {
	window_x = g_x;
	usposition = 1;
      }
      if (flags & YValue)
      {
	window_y = g_y;
	usposition = 1;
      }
      if (flags & XNegative)
      {
	window_x = g_x;
	xneg = 1;
	usposition = 1;
      }
      if (flags & YNegative)
      {
	window_y = g_y;
	yneg = 1;
	usposition = 1;
      }
    }
    else if ((strlen (&tline[0]) > 1) && (mystrncasecmp (tline, CatString3 ("*", MyName, ".icongeometry"), Clength + 14) == 0))
    {
      tmp = &tline[Clength + 14];
      while (((isspace (*tmp)) && (*tmp != '\n')) && (*tmp != 0))
      {
	tmp++;
      }
      tmp[strlen (tmp) - 1] = 0;
      printf ("Icon %s\n", tmp);
      flags = XParseGeometry (tmp, &g_x, &g_y, &width, &height);
      if (flags & WidthValue)
      {
	icon_w = width;
      }
      if (flags & HeightValue)
      {
	icon_h = height;
      }
      if (flags & XValue)
      {
	icon_x = g_x;
	printf ("%s: Warning  - Icon position not supported by XFwm.\n" "Extra parameter (icon pos. x=%u) ignored\n", MyName, g_x);
      }
      if (flags & YValue)
      {
	icon_y = g_y;
	printf ("%s: Warning  - Icon position not supported by XFwm.\n" "Extra parameter (icon pos. y=%u) ignored\n", MyName, g_y);
      }
    }
    else if ((strlen (&tline[0]) > 1) && (mystrncasecmp (tline, CatString3 ("*", MyName, ".label"), Clength + 7) == 0))
    {
      desk = 0;
      sscanf (&tline[Clength + 7], "%d", &desk);
      desk--;
      if ((desk >= 0) && (desk <= desk2))
      {
	n = 0;
	while (isspace (tline[Clength + 7 + n]))
	  n++;
	while (!isspace (tline[Clength + 7 + n]))
	  n++;
	free (Desks[desk].label);
	CopyString (&Desks[desk].label, &tline[Clength + 7 + n]);
      }
    }
    else if ((strlen (&tline[0]) > 1) && (mystrncasecmp (tline, CatString3 ("*", MyName, ".font"), Clength + 6) == 0))
    {
      CopyString (&font_string, &tline[Clength + 6]);
      if (mystrncasecmp (font_string, "none", 4) == 0)
	uselabel = 0;

    }
    else if ((strlen (&tline[0]) > 1) && (mystrncasecmp (tline, CatString3 ("*", MyName, ".smallfont"), Clength + 11) == 0))
    {
      CopyString (&smallFont, &tline[Clength + 11]);
      if (mystrncasecmp (smallFont, "none", 4) == 0)
      {
	free (smallFont);
	smallFont = NULL;
      }
    }
    GetConfigLine (fd, &tline);
  }
  return;
}
