/************************************************************************/
/*                                                                      */
/*    *****                       *****                                 */
/*      *****                   *****                                   */
/*        *****               *****                                     */
/*          *****           *****                                       */
/*  ***************       ***************                               */
/*  *****************   *****************                               */
/*  ***************       ***************                               */
/*          *****           *****           TheNetNode                  */
/*        *****               *****         Portable                    */
/*      *****                   *****       Network                     */
/*    *****                       *****     Software                    */
/*                                                                      */
/* File os/win32/win32.c (maintained by: DF6LN)                         */
/*                                                                      */
/* This file is part of "TheNetNode" - Software Package                 */
/*                                                                      */
/* Copyright (C) 1998 - 2008 NORD><LINK e.V. Braunschweig               */
/*                                                                      */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the NORD><LINK ALAS (Allgemeine Lizenz fuer    */
/* Amateurfunk Software) as published by Hans Georg Giese (DF2AU)       */
/* on 13/Oct/1992; either version 1, or (at your option) any later      */
/* version.                                                             */
/*                                                                      */
/* This program is distributed WITHOUT ANY WARRANTY only for further    */
/* development and learning purposes. See the ALAS (Allgemeine Lizenz   */
/* fuer Amateurfunk Software).                                          */
/*                                                                      */
/* You should have received a copy of the NORD><LINK ALAS (Allgemeine   */
/* Lizenz fuer Amateurfunk Software) along with this program; if not,   */
/* write to NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig   */
/*                                                                      */
/* Dieses Programm ist PUBLIC DOMAIN, mit den Einschraenkungen durch    */
/* die ALAS (Allgemeine Lizenz fuer Amateurfunk Software), entweder     */
/* Version 1, veroeffentlicht von Hans Georg Giese (DF2AU),             */
/* am 13.Oct.1992, oder (wenn gewuenscht) jede spaetere Version.        */
/*                                                                      */
/* Dieses Programm wird unter Haftungsausschluss vertrieben, aus-       */
/* schliesslich fuer Weiterentwicklungs- und Lehrzwecke. Naeheres       */
/* koennen Sie der ALAS (Allgemeine Lizenz fuer Amateurfunk Software)   */
/* entnehmen.                                                           */
/*                                                                      */
/* Sollte dieser Software keine ALAS (Allgemeine Lizenz fuer Amateur-   */
/* funk Software) beigelegen haben, wenden Sie sich bitte an            */
/* NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig            */
/*                                                                      */
/************************************************************************/

#define _TNN_WIN32_C

#include "tnn.h"

static  struct  timeval tv;
static  struct  timezone tz;

static  struct  timeval tv_old;

#ifndef NO_WATCHDOG
static  int     wdpid[2];
static  int     watch_dog[2];
#endif
int        consolefd = EOF;

#ifndef NO_WATCHDOG
static void     watchdog_init(void);
#endif
#ifdef DEBUG_MODUS
static void     sigterm(int);
static void     SigPipe(int);
#endif /* DEBUG_MODUS */

static void     setfiletime(struct ffblk *);

char    start_name[MAXPATH];
char    stop_name[MAXPATH];

UWORD   maxrounds = 1;          /* 1 = 100 Runden (default, wenn kein   */
                                /* "rounds"-Eintrag in tnn.ini          */

/* COM-Schnittststellen oeffnen. */
int open_win (char *name, int baud)
{ 
  HANDLE h;
  COMMTIMEOUTS ct;    
  DCB dcb;

  h = CreateFile (name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
                  FILE_ATTRIBUTE_NORMAL,// | FILE_FLAG_OVERLAPPED,
                  NULL);
  if ((int)h == EOF)
    return EOF;

  if (SetCommMask (h, 0) == FALSE)
  {
    printf ("SetCommMask", "%s", strerror (GetLastError ()));
    Break();
  }

  if (SetupComm (h, 4096, 4096) == FALSE)
  {
    printf ("SetupComm", "%s", strerror (GetLastError ()));
    Break();
  }
  
  if (PurgeComm (h, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR) == FALSE)
  {
    printf ("PurgeComm", "%s", strerror (GetLastError ()));
    Break();
  }

  ct.ReadIntervalTimeout = 0xFFFFFFFF;
  ct.ReadTotalTimeoutMultiplier = 0;
  ct.ReadTotalTimeoutConstant = 0;
  ct.WriteTotalTimeoutMultiplier = 0;
  ct.WriteTotalTimeoutConstant = 0;

  if (SetCommTimeouts (h, &ct) == FALSE)
  {
    printf ("SetCommTimeout", "%s", strerror (GetLastError ()));
    Break();
  }

  memset (&dcb, 0, sizeof (DCB));
  dcb.DCBlength = sizeof (DCB);
  GetCommState (h, &dcb);
  dcb.BaudRate = baud;
  dcb.fBinary = 1;
  dcb.fParity = 0;
  dcb.fOutxCtsFlow = 0;      // CTS output flow control
  dcb.fOutxDsrFlow = 0;      // DSR output flow control
  dcb.fDtrControl = 0;       // DTR flow control type
  dcb.fDsrSensitivity = 0;   // DSR sensitivity
  dcb.fTXContinueOnXoff = 1; // XOFF continues Tx
  dcb.fOutX = 0;             // XON/XOFF out flow control
  dcb.fInX = 0;              // XON/XOFF in flow control
  dcb.fErrorChar = 0;        // enable error replacement
  dcb.fNull = 0;             // enable null stripping
  dcb.fRtsControl = 0;       // RTS flow control
  dcb.fAbortOnError = 0;     // abort reads/writes on error
  dcb.fDummy2 = 0;           // reserved
  dcb.wReserved = 0;         // not currently used
  dcb.XonLim = 0;            // transmit XON threshold
  dcb.XoffLim = 0;           // transmit XOFF threshold
  dcb.ByteSize = 8;          // number of bits/byte, 4-8
  dcb.Parity = 0;            // 0-4=no,odd,even,mark,space
  dcb.StopBits = 0;          // 0,1,2 = 1, 1.5, 2
  dcb.XonChar = 0;           // Tx and Rx XON character
  dcb.XoffChar = 0;          // Tx and Rx XOFF character
  dcb.ErrorChar = 0;         // error replacement character
  dcb.EofChar = 0;           // end of input character
  dcb.EvtChar = 0;           // received event character

  if (SetCommState (h, &dcb) == FALSE)
  {
    printf ("Fehler: Baudrate kann nicht initalisiert werden!\n"
            "Eintrag \"speed xxx\" (xxx=Baudrate) fehlt in der tnn.ini!\n");
    Break();
  }

  return((int)h);
}

int w_read (int fd, void *data, int maxlen)
{
  DWORD ret = 0;
  static OVERLAPPED ovl = { 0, 0, 0, 0, 0 };

  ReadFile ((HANDLE) fd, data, maxlen, &ret, &ovl);
  return ret;
}

int w_write (int fd, void *data, int maxlen)
{
  DWORD ret = 0;
  static OVERLAPPED ovl = { 0, 0, 0, 0, 0 };

  WriteFile ((HANDLE) fd, data, maxlen, &ret, &ovl);
  return ret;
}

/*
   String in Kleinbuchstaben umwandeln - aus DJGPP GNU-Paket fuer MS-DOS
   von D.J. Delorie
*/

char *
strlwr(char *s)
{
  char *p = s;
  while (*s)
  {
    if ((*s >= 'A') && (*s <= 'Z'))
      *s += 'a'-'A';
    s++;
  }
  return(p);
}


/*
   String in Grossbuchstaben umwandeln - aus DJGPP GNU-Paket fuer MS-DOS
   von D.J. Delorie
*/

char *
strupr(char *s)
{
  char *p = s;
  while (*s)
  {
    if ((*s >= 'a') && (*s <= 'z'))
      *s += 'A'-'a';
    s++;
  }
  return(p);
}

/************************************************************************/
/*                                                                      */
/* Als Ersatz fuer die Funktionen "findfirst" und "findnext" (bei DOS)  */
/* werden die Funktionen "xfindfirst" und "xfindnext" verwendet. Hier   */
/* wird nur der fuer TNN benoetigte Teil in die Struktur ffblk einge-   */
/* tragen. Der zuletzt gefundene Filename muss in ffblk->ff_name erhal- */
/* ten bleiben, weil dieser beim Aufruf von "xfindnext" gesucht wird.   */
/*                                                                      */
/************************************************************************/

int
xfindfirst(const char *pathname, struct ffblk *ffblk, int attrib)
{
  char          *fnpoi;
  DIR           *dp;
  struct dirent *dirp;
  int           retval;

  strcpy(ffblk->ff_path, pathname);             /* Filename incl. Pfad  */
  normfname(ffblk->ff_path);                    /* '\\' -> '/'          */
  fnpoi = strrchr(ffblk->ff_path, FILE_SEP);    /* Pfad angegeben?      */
  if (fnpoi == NULL)                            /* - nein ..            */
   {
    strcpy(ffblk->ff_find, ffblk->ff_path);     /* Filename kopieren    */
    strcpy(ffblk->ff_path, textpath);           /* default: textpath    */
   }
  else                                          /* mit Pfad             */
   {
    if (fnpoi == ffblk->ff_path+strlen(ffblk->ff_path)) /* ohne Name    */
      return(-1);                                       /* Unsinn       */
    strcpy(ffblk->ff_find, ++fnpoi);    /* nur Filename                 */
    *fnpoi = NUL;                       /* Filename im Pfad loeschen    */
   }

  if ((dp = opendir(ffblk->ff_path)) == NULL) /* Directory vorhanden?   */
    return(-1);
  retval = -1;                          /* default: nix gefunden        */
  while ((dirp = readdir(dp)) != NULL)  /* Eintrag vorhanden?           */
   {
    if ((fnmatch(ffblk->ff_find, dirp->d_name,
                 FNM_PATHNAME|FNM_PERIOD) != 0))
      continue;
    strcpy(ffblk->ff_name, dirp->d_name);
    setfiletime(ffblk);
    retval = 0;
    break;
   }
  closedir(dp);
  return(retval);
}

/************************************************************************/
/*                                                                      */
/* Erst den zuletzt gefundenen Eintrag suchen (steht in ffblk->ff_name) */
/* und den darauffolgenden passenden Eintrag zurueckmelden.             */
/*                                                                      */
/************************************************************************/

int
xfindnext(struct ffblk *ffblk)
{
  DIR           *dp;
  struct dirent *dirp;
  int           retval;

  if ((dp = opendir(ffblk->ff_path)) == NULL) return(-1);
  retval = -1;                          /* default: nix gefunden        */
  while ((dirp = readdir(dp)) != NULL)
   {
    if ((fnmatch(ffblk->ff_name, dirp->d_name,
                 FNM_PATHNAME|FNM_PERIOD) != 0))
      continue;
    retval = 1;
    break;
   }
  if (retval == 1)
   {
    retval = -1;                          /* default: nix gefunden      */
    while ((dirp = readdir(dp)) != NULL)
     {
      if ((fnmatch(ffblk->ff_find, dirp->d_name,
                   FNM_PATHNAME|FNM_PERIOD) != 0))
        continue;
      strcpy(ffblk->ff_name, dirp->d_name);
      setfiletime(ffblk);
      retval = 0;
      break;
     }
   }
  closedir(dp);
  return(retval);
}

/************************************************************************/
/*                                                                      */
/* Bei xfindfirst und xfindnext Datum und Uhrzeit im Fileblock setzen   */
/*                                                                      */
/************************************************************************/

static void
setfiletime(struct ffblk *ffblk)
{
  struct stat  filestat;
  struct tm   *filetime;
  struct stat  FSize;
  char         fn[MAXPATH];

  sprintf(fn, "%s%s", ffblk->ff_path, ffblk->ff_name);
  stat(fn, &filestat);
  filetime = gmtime(&filestat.st_mtime);
  ffblk->ff_ftime =   ((filetime->tm_sec / 2) & 0x1f)
                    + ((filetime->tm_min & 0x3f) << 5)
                    + ((filetime->tm_hour & 0x1f) << 11);
  ffblk->ff_fdate =   (filetime->tm_mday & 0x1f)
                    + (((filetime->tm_mon & 0x0f) + 1) << 5)
                    + (((filetime->tm_year - 80) & 0x7f) << 9);

  if (!stat(ffblk->ff_name, &FSize))
    ffblk->ff_fsize = FSize.st_size;
}

/************************************************************************/
/*                                                                      */
/* Ermitteln des freien Festplattenplatzes in Bytes.                    */
/*                                                                      */
/* Damit es fuer grosse Festplatten keinen Ueberlauf gibt (Rueckgabe    */
/* ist vom Typ LONG), wird ab 1GB freiem Speicher immer 1GB zurueck-    */
/* gemeldet - sieht da jemand Probleme?                                 */
/*                                                                      */
/************************************************************************/

LONG
getdiskfree(char *path)
{
  DWORD free = 0;

  GetDiskFreeSpace("C:\\", NULL, NULL, NULL, &free);
  return(free);
}

/************************************************************************/
/*                                                                      */
/* Fuer das erweiterte AUTOBIN-Protokoll wird die File-Zeit in einem    */
/* Bitfeld gespeichert (s. Doku zu DPBOX), wie es BC++ bei dem Befehl   */
/* getftime verwendet. Hier wird die Win32-Zeit der letzten Aenderung   */
/* verwendet.                                                           */
/*                                                                      */
/************************************************************************/

int getftime(int handle, struct ftime *ftimep)
{
  struct stat filestat;
  struct tm   *filetime;

  if (fstat(handle, &filestat) == -1)
          return(-1);

  filetime = gmtime(&filestat.st_mtime);

  ftimep->ft_tsec  = filetime->tm_sec / 2;
  ftimep->ft_min   = filetime->tm_min;
  ftimep->ft_hour  = filetime->tm_hour;
  ftimep->ft_day   = filetime->tm_mday;
  ftimep->ft_month = filetime->tm_mon + 1;
  ftimep->ft_year  = filetime->tm_year - 80;

  return(0);
}

/************************************************************************/
/*                                                                      */
/* 10ms-Ticker updaten                                                  */
/*                                                                      */
/************************************************************************/
void
update_timer(void)
{
  fd_set             rmask;
  fd_set             wmask;
  struct timeval     timevalue;
  int                max_fd = 0;
  int                count;
  register int       i;
  int                len;
  char               buffer[1024];
  static UWORD       roundcounter = 1;
#ifndef NO_WATCHDOG
  int                lasttic10 = tic10;
#endif

  gettimeofday(&tv, &tz);

  tic10 = (tv.tv_sec - tv_old.tv_sec) * 100 +
          (tv.tv_usec - tv_old.tv_usec) / 10000;

#ifndef NO_WATCHDOG
  if (tic10 - lasttic10)
    if (write(watch_dog[1], "\0", 1) != 1)
      exit(1);
#endif

  FD_ZERO(&rmask);
  FD_ZERO(&wmask);

  FD_SET((unsigned)consolefd, &rmask);        /* Eingabe Console      */
  max_fd = 1;

  if (kiss_active)
  {
    for (i = 0; i < L1PNUM; ++i)
    {
      if (l1port[i].kisslink < 0)
        continue;

#ifdef VANESSA
      if (l1port[i].kisstype == KISS_VAN)
        continue;
#endif
#ifdef AX_IPX
      if (l1port[i].kisstype == KISS_IPX)
        continue;
#endif
#ifdef AX25IP
      if (l1port[i].kisstype == KISS_AXIP)
        continue;
#endif
#ifdef KERNELIF
      if (   (l1port[i].kisstype == KISS_KAX25)
          || (l1port[i].kisstype == KISS_KAX25KJD))
        continue;
#endif
#ifdef SIXPACK
      if (l1port[i].kisstype == KISS_6PACK)
        continue;
#endif

      if (l1port[i].port_active)
      {
        FD_SET((unsigned)l1port[i].kisslink, &wmask);
        if (l1port[i].kisslink > max_fd -1)
          max_fd = l1port[i].kisslink + 1;
      }
    }
  }

  /* fuer alle Interfaces die benutzten Filedescriptoren fuer select() */
  /* ermitteln und eintragen */
  for (i = 0; i < L1PNUM; ++i)
  {
    /* nur aktive Ports bearbeiten */
    if (   (l1port[i].kisslink < 0) || (l1port[i].port_active != TRUE)
#ifdef SIXPACK
        || (l1port[i].kisstype == KISS_6PACK)   /* der hat hier nichts zu suchen */
#endif
       )
       continue;


    /* Interfacetypen durchgehen */
  } /* for(...) */

  timevalue.tv_usec = 10000;
  timevalue.tv_sec = 0;

  if (++roundcounter < maxrounds)      /* schon genug Runden vergangen? */
    timevalue.tv_usec = 0;
  else
    roundcounter = 0;                  /* Durchlaufzaehler ruecksetzen  */

  count = select(max_fd, &rmask, NULL, NULL, &timevalue);

  if (count == -1)
    return;

  /* alle Ports durchgehen */
  for (i = 0; i < L1PNUM; ++i)
  {
    /* unbenutzte Ports nicht bearbeiten */
    if (   (l1port[i].kisslink < 0) || (l1port[i].port_active != TRUE)
#ifdef SIXPACK
        || (l1port[i].kisstype == KISS_6PACK)   /* der hat hier nichts zu suchen */
#endif
       )
       continue;
  }

  if (kiss_active)
  {
    for (i = 0; i < L1PNUM; ++i)
    {
      if (l1port[i].kisslink == -1)
        continue;
#ifdef AX25IP
      if (l1port[i].kisstype == KISS_AXIP)
        continue;
#endif
#ifdef SIXPACK
      if (l1port[i].kisstype == KISS_6PACK)
        continue;
#endif
      if (l1port[i].port_active)
        if (FD_ISSET(l1port[i].kisslink, &wmask))
          if ((len = read(l1port[i].kisslink, buffer, 1024)) != 0)
            framedata_to_queue(i, buffer, len);
    }
  }
}

/************************************************************************/
/*                                                                      */
/* Rechner neu starten                                                  */
/*                                                                      */
/************************************************************************/

void
reboot_system(void)
{
  exit_all();
/* Hierher wird man wohl nur kommen, wenn tnn nicht als root gestartet  */
/* wurde. Nun gibt es 2 Moeglichkeiten: entweder wir starten von vorne, */
/* weil die Resourcen ja breits freigegeben wurden, oder aber wir       */
/* beenden das Programm, weil das ja mehr oder weniger gefordert wurde. */
/* Sinnvoller ist es wohl, wenn wir das Programm verlassen, in der      */
/* Hoffnung, dass es danach regulaer von einem Batch erneut gestartet   */
/* wird. Das ist jedenfalls besser, als nix zu tun.                     */
  Break();
  exit(1);
}

/************************************************************************/
/*                                                                      */
/* Shell-Befehl - Win32-spezifischer Teil:                              */
/* Das gewuenschte Programm wird als Hintergrundprozess gestartet.      */
/*                                                                      */
/************************************************************************/

BOOLEAN
tnnshell(char *cmdline)
{
  return(TRUE);
}

/************************************************************************/
/*                                                                      */
/* Pruefen, ob Kind-Prozesse beendet sind (vom Sysop gestartete externe */
/* Programme). Bei Zeitueberschreitung wird das Programm mit einer      */
/* entsprechenden Fehlermeldung abgebrochen.                            */
/*                                                                      */
/************************************************************************/

void
shellsrv(void)
{
}

/************************************************************************/
/*                                                                      */
/* Daten von der Shell abholen und zum User bringen                     */
/*                                                                      */
/************************************************************************/

void
shell_to_user(void)
{
}

/************************************************************************/
/*                                                                      */
/* User-Eingabe in mhdp verarbeiten                                     */
/*                                                                      */
/************************************************************************/

BOOLEAN
l7tosh(MBHEAD *mhdp)
{
  if (userpo->status == US_EXTP)            /* externer Prozess aktiv?  */
  {
    if (userpo->child_iactive == FALSE)
    {
      dealmb(mhdp);                           /* Abbruchzeile ignorieren  */
      userpo->mbhd = NULL;
      kill((pid_t *)(userpo->child_pid), SIGKILL);    /* Abbruch (Watchdog)       */
      putmsg("SHELL ABORTED by User\r");
      userpo->child_pid = 0;                  /* Prozess ist fertig       */
      userpo->child_fd = -1;
      userpo->child_iactive = FALSE;
      userpo->child_timeout = 0;
      userpo->status = US_CCP;
      return(TRUE);
    }
    return (TRUE);
  }
  return (FALSE);
}

/************************************************************************/
/*                                                                      */
/* Filenamen pruefen                                                    */
/*                                                                      */
/************************************************************************/

BOOLEAN
good_file_name(const char *file)
{
  return(TRUE);
}

/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/

void
tnnexec(char *file)
{
  char filename[MAXPATH+1];

  strncpy(filename, file, MAXPATH);
  filename[MAXPATH] = 0;
  
  if (xaccess(filename,0)==0)
  {
    l1exit();
    /* bei DOS16 -> done_umb() */
    exit_timer();
    exit_hardware();
    execl(filename, filename, NULL);
    perror("EXEC");
    HALT("tnnexec");
  }
}

/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/

/* Winsocket Initialisieren. */
static int startWinsock(void)
{
  WSADATA wsa;
  return WSAStartup(MAKEWORD(2,0),&wsa);
}

void
init_console(void)
{
  console_titel();

#ifdef L1TCPIP
  /* Interface Initialisieren. */
  InitIFC();
#endif /* L1TCPIP */

  setvbuf(stdout, NULL, _IONBF, 0);           /* Ausgabe ungepuffert  */

  /* Winsocket Initialisieren. */
  if (startWinsock())
  {
#ifdef SPEECH
    printf(speech_message(334));
#else
    printf("Error: Winsock cannot be initialized!\r");
#endif
    return;
  }

  if ((consolefd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    return;

  consfile = stdout;
}

/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/

void
exit_console(void)
{
}

/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/

void hputc(char c)
{
  if (!ishmod)
  { /* keine Ausgaben im Hostmode */
    if (c == CR) /* nur fuer PC/Console notwenig! */
      putchar('\n');
    else
      putchar(c);
  }
}

/* return if character available */

BOOLEAN ishget()
{
  if (kbhit())
  {
    if (ishmod)
      hgetc();       /* im Hostmode Console ignorieren */
    else
      return(TRUE);  /* im Terminalmode melden, dass was da ist */
  }

  return(FALSE);     /* keine Umleitung, also auch nix da */
}

/* return if output to console is stopped */

BOOLEAN ishput(void)
{
  return(FALSE);
}

/* get one character from console */

char hgetc(void)
{
  #define Prefix 0
  #define ALT_X '-'
  char    retval = 0;

  if (kbhit())
  {
    if ((retval = getch()) == Prefix)
    {
      if (getch() == ALT_X)
        quit_program(0);
    }
  }

  return(retval);
}

/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/

BOOLEAN
init_hardware(int argc, char *argv[])
{
  int scanned = 1;

  umask(0);
  time(&sys_time);

  /* Kommandozeilenparameter auswerten */
  /* Den Verbose-Modus schon hier feststellen */
  while (scanned < argc)
  {
    /* Parameter "-v" (ausfuehrlichere Meldungen) */
    if (strcmp(argv[scanned], "-v") == 0)
    {
      bVerbose = TRUE;
    }
    ++scanned;
  }

  VMSG("--- Verbose logging started !\n");

  VMSG("--- Reading ini-file ...\n");
  if (read_init_file(argc, argv))
    return(TRUE);

/*
  if (tnn_errfile[0] != NUL)
  {
  }
*/
  VMSG("--- Allocating mem for %u buffers ...\n", tnn_buffers);

  RAMBOT = (char *)malloc(tnn_buffers * sizeof(MAX_BUFFER));
  RAMTOP = RAMBOT + tnn_buffers * sizeof(MAX_BUFFER);

  if (RAMBOT == NULL)
  {
    VMSG("!!! Allocation mem for %ld buffers failed, trying to allocate for %u buffers now ...\n", tnn_buffers, TNN_BUFFERS);
    RAMBOT = (char *)malloc(TNN_BUFFERS * sizeof(MAX_BUFFER));
    RAMTOP = RAMBOT + TNN_BUFFERS * sizeof(MAX_BUFFER);
    if (RAMBOT == NULL)
     {
      fprintf(stderr, "malloc for buffers failed\n");
      exit(1);
     }
    else
      tnn_buffers = TNN_BUFFERS;
   }

  if (kiss_active)
  {
    VMSG("--- Initializing KISS-interfaces ...\n");
    if (init_kisslink())
    {
      free(RAMBOT);
      Break();
      exit(1);
    }
  }

#ifndef NO_WATCHDOG
  VMSG("--- Initializing watchdog ...\n");
  watchdog_init();
#endif

  VMSG("--- Installing signal-handlers ...\n");
#ifdef DEBUG_MODUS
  signal(SIGSEGV, sigsegv);
  signal(SIGTERM, sigterm);
  signal(SIGPIPE, SigPipe);
#endif

  signal(SIGHUP, SIG_IGN);
  signal(SIGTSTP, SIG_IGN);
  signal(SIGTTIN, SIG_IGN);
  signal(SIGTTOU, SIG_IGN);

  gettimeofday(&tv, &tz);
  tv_old = tv;

  return(FALSE);
}

#ifndef NO_WATCHDOG
/************************************************************************/
/*                                                                      */
/* Watchdog - ein vom TNN-Hauptprogramm unabhaengiger Prozess muss      */
/* regelmaessig vom Hauptprogramm Daten erhalten. Bleiben diese Daten   */
/* laenger als 60s aus, wird TNN beendet.                               */
/*                                                                      */
/************************************************************************/
static void
watchdog_init(void)
{
}
#endif

/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/

void
exit_hardware(void)
{
  free(RAMBOT);

  if (kiss_active)
    exit_kisslink();
}

#ifdef DEBUG_MODUS
/************************************************************************/
/* Signalbehandlungsfunktion fuer das Signal SIGTERM                    */
/************************************************************************/
static void
sigterm(int signo)
{
  signal(SIGTERM, SIG_IGN);
  printf("sigterm\n");
  quit_program(-1);
}

void
sigsegv(int signo)
{
  signal(SIGSEGV, SIG_IGN);
  printf("\nTheNetNode verursachte einen unbekannten Systemfehler!!!\n");
  printf("Letzte Funktion war: %s\n", lastfunc);
  Break();

  quit_program(-1);
}

/************************************************************************/
/*                                                                      */
/* Signalbehandlungsfunktion fuer das Signal SIGPIPE                    */
/*                                                                      */
/* Ursache, in bereits geschlossene Socket-Verbindung schreiben/lesen.  */
/*                                                                      */
/************************************************************************/
void SigPipe(int signo)
{
  signal(SIGPIPE, SIG_IGN);
  Break();
}

#endif

/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/

int
exit_all(void)
{
  exit_mh();                            /* MH-Liste sichern             */
  save_stat();
  save_configuration();
  personalmanager(SAVE, NULL, NULL);    /* Pers. Daten fuer Convers     */

  /* noch laufende Shells killen */
  for (userpo  = (USRBLK *) usccpl.head;
       userpo != (USRBLK *) &usccpl;
       userpo  = (USRBLK *) userpo->unext)
  {
    if (userpo->status != US_EXTP)      /* externer Prozess aktiv?      */
      continue;

      kill((pid_t *)(userpo->child_pid), SIGKILL);      /* Abbruch              */
  }

  /* Buffer freigeben */
  free(RAMBOT);

  /* Ab hier stehen keine Buffer mehr zur Verfuegung, alles was mit Buffern */
  /* zu tun hat, gibt nun nen Segfault ! */

  if (kiss_active)
    exit_kisslink();

  printf("\r\n");
  return(0);
}

static void message_handler(void)
{
  printf("\a\a\n\n*** ACHTUNG ***\n"
         "TheNetNode beenden mit ESC QUIT Enter im Konsolen-Fenster !!!.\n");
}

BOOL CtrlHandler( DWORD fdwCtrlType ) 
{ 
  switch( fdwCtrlType ) 
  { 

    case CTRL_C_EVENT: 
      message_handler();
      return( TRUE );
 
    // CTRL-CLOSE: confirm that the user wants to exit. 
    case CTRL_CLOSE_EVENT: 
      message_handler();
      return( TRUE ); 
 
    // Pass other signals to the next handler. 
    case CTRL_BREAK_EVENT: 
      message_handler();
      return FALSE; 
 
    case CTRL_LOGOFF_EVENT: 
      message_handler();
      return FALSE; 
 
    case CTRL_SHUTDOWN_EVENT: 
      message_handler();
      return FALSE; 

#ifdef DEBUG_MDOUS
    case SIGSEGV: 
      sigsegv(fdwCtrlType);
      return FALSE; 
#endif 

    default: 
      return FALSE; 
  } 
} 

void console_titel(void)
{
  TCHAR szOldTitle[MAX_PATH];
  TCHAR szNewTitle[MAX_PATH];
  char call[10];

  call2str(call,myid);

  if( GetConsoleTitle(szOldTitle, MAX_PATH) )
  {
    wsprintf(szNewTitle, TEXT("%s%s)"), signon,call);

    if( !SetConsoleTitle(szNewTitle) )
      printf("SetConsoleTitle failed (%d)\n", GetLastError());
  }
}

/* Code vom Stefan DAC922. */
void gettimeofdaywindows(struct timeval *tv, struct timezone *tz)
{
  SYSTEMTIME systm;
  FILETIME   ftm;
  LONGLONG   ll;

  GetSystemTime(&systm);
  if (!SystemTimeToFileTime(&systm,&ftm))
    exit(1);

  ll = (LONGLONG) ftm.dwHighDateTime;
  ll <<= 32;
  ll |= (LONGLONG) ftm.dwLowDateTime;
  ll -= 116444736000000000;
  ll /= 10000000;
  tv->tv_sec = (long) ll;
  tv->tv_usec = (long) systm.wMilliseconds * 1000;
}

void kill(HANDLE Handle, WORD flag)
{
  TerminateProcess(Handle, flag);
  _endthread();
}

/* Ersatz fuer "system("pause"), bei Fernsteuerung      */
/* eines Knotens kann man schlecht eine Taste druecken. */
void Break(void)
{
  time_t lasttime;
  time_t now;

  now = time(NULL);
  lasttime = time(NULL);

  while (lasttime + 10 > now)
  {
    now = time(NULL);
  }
}

#ifdef L1TCPIP
/* Pruefe auf TCPIP-Port's */
int CheckPortTCP(int port)
{
  switch(kissmode(port))
  {
#ifdef L1TELNET
    case KISS_TELNET :
     return(TRUE);
#endif /* L1TELNET */

#ifdef L1HTTPD
    case KISS_HTTPD :
     return(TRUE);
#endif /* L1HTTPD */

#ifdef L1IPCONV
    case KISS_IPCONV :
     return(TRUE);
#endif /* L1IPCONV */

#ifdef L1IRC
    case KISS_IRC :
     return(TRUE);
#endif /* L1IRC */

    default :
     return(FALSE);
  }

  return(FALSE);
}
#endif /* L1TCPIP */

/*
  Dummy-Funktionen
*/

ULONG
coreleft(void) { return(FALSE); }
void
init_timer(void) {}
void
DIinc(void) {}
void
decEI(void) {}
void
exit_timer(void) {}
void
init_rs232(void) {}

/* End of os/win32/win32.c */
