/*
*  txt2gcal.c:  Creates a verbatim Gcal resource file from a text file.
*
*
*  Copyright (C) 1996 Thomas Esken
*
*  This software doesn't claim completeness, correctness or usability.
*  On principle I will not be liable for ANY damages or losses (implicit
*  or explicit), which result from using or handling my software.
*  If you use this software, you agree without any exception to this
*  agreement, which binds you LEGALLY !!
*
*  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, or (at your option)
*  any later version.
*
*  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
*/



static char rcsid[]="$Id: txt2gcal.c 2.40 1996/07/16 02:04:00 tom Exp $";



#include "gcal_tai.h"
#if HAVE_CTYPE_H
#  include <ctype.h>
#endif
#if HAVE_ERRNO_H
#  include <errno.h>
#endif
#if HAVE_SYS_STAT_H
#  if HAVE_SYS_TYPES_H
#    include <sys/types.h>
#  endif
#  include <sys/stat.h>
#endif
#ifndef USE_RC
#  define  USE_RC  1
#endif
#include "gcal.h"



/*
*  Function prototypes.
*/
#if __cplusplus
extern "C"
{
#endif
LOCAL void
usage_msg __P_((FILE *fp,
                char *prgr_name,
                int   exit_status));
LOCAL void
version_msg __P_((FILE *fp,
                  int   exit_status));
LOCAL VOID_PTR
my_malloc __P_((const int   amount,
                const int   exit_status,
                const char *module_name,
                const long  module_line,
                const char *var_name,
                const int   var_contents));
LOCAL VOID_PTR
my_realloc __P_((      VOID_PTR  ptr_memblock,
                 const int       amount,
                 const int       exit_status,
                 const char     *module_name,
                 const long      module_line,
                 const char     *var_name,
                 const int       var_contents));
LOCAL void
my_error __P_((const int   exit_status,
               const char *module_name,
               const long  module_line,
               const char *var_name,
               const int   var_contents));
EXPORT int
main __P_((int   argc,
           char *argv[]));
#if __cplusplus
}
#endif



/*
*  Define program global variables.
*/
#ifdef DJG
LOCAL Usint  testval;                 /* Set to SHRT_MAX for checking the maximum table range */
#else
LOCAL Uint   testval;                 /* Set to INT_MAX for checking the maximum table range */
#endif
LOCAL Uint   maxlen_max=MAXLEN_MAX;   /* Actual length of all strings */
LOCAL char  *prgr_name;               /* The name of this executable */



   LOCAL void
usage_msg (fp, prgr_name, exit_status)
   FILE *fp;
   char *prgr_name;
   int   exit_status;
/*
   Writes the program "usage" text to file `fp' and
     terminates the program with `exit_status'.
*/
{
#if USE_GER
   fprintf(fp, "Aufruf:  %s  [--help | --version] | [TEXTDATEI | -] [DATUMTEIL]\n", prgr_name);
#else /* !USE_GER */
   fprintf(fp, _("Usage:  %s  [--help | --version] | [TEXT-FILE | -] [DATE-PART]\n"), prgr_name);
#endif /* !USE_GER */
   exit(exit_status);
}



   LOCAL void
version_msg (fp, exit_status)
   FILE *fp;
   int   exit_status;
/*
   Writes the program "version" text to file `fp' and
     terminates the program with `exit_status'.
*/
{
   char *ptr_rcsid=rcsid+5;


   rcsid[40] = '\0';
   fprintf(fp, "%s\n", ptr_rcsid);
   exit(exit_status);
}



   LOCAL VOID_PTR
my_malloc (amount, exit_status, module_name, module_line, var_name, var_contents)
   const int   amount;
   const int   exit_status;
   const char *module_name;
   const long  module_line;
   const char *var_name;
   const int   var_contents;
/*
   Allocate AMOUNT bytes of memory dynamically, with error checking.  Calls `my_error()'
     and terminates program if any errors occur.  AMOUNT is limited to `int' range
     instead of `size_t' range; this is wanted!
*/
{
   auto VOID_PTR  ptr_memblock;


   if ((Uint)amount > testval)
     /*
        Error, table size overflow!
     */
     my_error (107, module_name, module_line, var_name, (int)testval);
   ptr_memblock = (VOID_PTR)malloc((int)amount);
   if (ptr_memblock == (VOID_PTR)NULL)
     /*
        Error, `malloc()' have failed.
     */
     my_error (exit_status, module_name, module_line, var_name, var_contents);

   return ptr_memblock;
}



   LOCAL VOID_PTR
my_realloc (ptr_memblock, amount, exit_status, module_name, module_line, var_name, var_contents)
         VOID_PTR  ptr_memblock;
   const int       amount;
   const int       exit_status;
   const char     *module_name;
   const long      module_line;
   const char     *var_name;
   const int       var_contents;
/*
   Change the size of an allocated block of memory PTR_MEMBLOCK to AMOUNT bytes,
     with error checking.  Calls `my_error()' and terminates program if any errors
     occur.  AMOUNT is limited to `int' range instead of `size_t' range; this is
     wanted!  If PTR_MEMBLOCK is NULL, `my_malloc()' is called instead.
*/
{
   if ((Uint)amount > testval)
     /*
        Error, table size overflow!
     */
     my_error (107, module_name, module_line, var_name, (int)testval);
   if (ptr_memblock == (VOID_PTR)NULL)
     return my_malloc (amount, exit_status, module_name, module_line, var_name, var_contents);
   ptr_memblock = (VOID_PTR)realloc(ptr_memblock, (int)amount);
   if (ptr_memblock == (VOID_PTR)NULL)
     /*
        Error, `realloc()' have failed.
     */
     my_error (exit_status, module_name, module_line, var_name, var_contents);

   return ptr_memblock;
}



   LOCAL void
my_error (exit_status, module_name, module_line, var_name, var_contents)
   const int   exit_status;
   const char *module_name;
   const long  module_line;
   const char *var_name;
   const int   var_contents;
/*
   Displays a specific error message on STDERR channel
     and terminates the program with status `exit_status'.
*/
{
#if USE_GER
   fprintf(stderr, "\n%s: Abbruch, ", prgr_name);
#else /* !USE_GER */
   fprintf(stderr, _("\n%s: abort, "), prgr_name);
#endif /* !USE_GER */
   switch(exit_status)
    {
#if USE_GER
      case 124:
        fprintf(stderr, "`%s' Zeile %ld: virtueller Speicher ersch"OE"pft (%s=%d)",
                module_name, module_line, var_name, var_contents);
        break;
      case 107:
        fprintf(stderr, "`%s' Zeile %ld: (`%s') ung"UE"ltiger Wert f"UE"r Tabellengr"OE""SZ"e `sizeof %s>%d'",
                module_name, module_line, INTERNAL_TXT, var_name, var_contents);
      default:
        fprintf(stderr, "`%s' Zeile %ld: (`%s') unbehandelter Fehler (%d)",
                module_name, module_line, INTERNAL_TXT, exit_status);
        break;
#else /* !USE_GER */
      case 124:
        fprintf(stderr, _("`%s' line %ld: virtual memory exhausted (%s=%d)"),
                module_name, module_line, var_name, var_contents);
        break;
      case 107:
        fprintf(stderr, _("`%s' line %ld: (`%s') invalid value for table size `sizeof %s>%d'"),
                module_name, module_line, _("Internal"), var_name, var_contents);
        break;
      default:
        fprintf(stderr, _("`%s' line %ld: (`%s') unmanaged error (%d)"),
                module_name, module_line, _("Internal"), exit_status);
#endif /* !USE_GER */
    }
   S_NEWLINE(stderr);
   exit(exit_status);
}



   PUBLIC int
main (argc, argv)
   int   argc;
   char *argv[];
/*
   Creates a verbatim Gcal resource file from a given text file
     (optional argument 1) with a given date-part (optional argument 2) and
     displays the results on the stdout channel.  If no text file name
     or only a dash `-' is given, read input from stdin channel.
*/
{
   auto     FILE  *fp=(FILE *)NULL;
   register int    i;
   register int    ch;
   register int    ch2;
   register int    ch3='\0';
   auto     char  *s1;
   auto     char  *ptr_char;
   auto     Bool   is_regular_file=TRUE;
   auto     Bool   is_trailing_whitespace=FALSE;


#ifdef GCAL_NLS
   /*
      Now initialize the NLS functions.
   */
#    if HAVE_SETLOCALE
   setlocale(LC_ALL, "");
#    endif
#    ifndef LOCALEDIR
#      define LOCALEDIR  NULL
#    endif
   bindtextdomain(PACKAGE, LOCALEDIR);
   textdomain(PACKAGE);
#endif
   /*
      Let's set `testval' to SHRT_MAX/INT_MAX if SHRT_MAX/INT_MAX itself isn't
        defined.  This solution only works on machines with internal arithmethics
        based on "two complements".
   */
#ifdef DJG
#  ifdef SHRT_MAX
   testval = SHRT_MAX;
#  else /* !SHRT_MAX */
   testval = ~0;
   testval >>= 1;
#  endif /* !SHRT_MAX */
#else /* !DJG */
#  ifdef INT_MAX
   testval = INT_MAX;
#  else /* !INT_MAX */
   testval = ~0;
   testval >>= 1;
#  endif /* !INT_MAX */
#endif /* !DJG */
   /*
      Initial memory allocation for global string.
   */
   s1 = (char *)my_malloc (MAXLEN_MAX, 124, __FILE__, (long)__LINE__, "s1", 0);
   /*
      Detect the own program name.
   */
   i = (int)strlen(argv[0]);
   if ((Uint)i >= maxlen_max)
     s1 = my_realloc ((VOID_PTR)s1, i+1, 124, __FILE__, (long)__LINE__, "s1", i+1);
   if (!i)
     strcpy(s1, "txt2gcal");
   else
     strcpy(s1, argv[0]);
#ifdef SUFFIX_SEP
   /*
      Eliminate version suffix under VMS.
   */
   ptr_char = strrchr(s1, *SUFFIX_SEP);
   if (ptr_char != (char *)NULL)
     *ptr_char = '\0';
#endif
   i = (int)strlen(s1);
#ifdef DJG
   ptr_char = strrchr(s1, *DIR2_SEP);
#else /* !DJG */
   ptr_char = strrchr(s1, *DIR_SEP);
#endif /* !DJG */
   if (ptr_char != (char *)NULL)
    {
      ptr_char++;
      i = (int)strlen(ptr_char);
    }
   else
     ptr_char = s1;
   if (tolower('A') == 'a')
    {
      auto char  *buf_ptr_char=ptr_char;


      for ( ; *ptr_char ; ptr_char++)
        *ptr_char = (char)tolower(*ptr_char);
      ptr_char = buf_ptr_char;
    }
   /*
      Suppress ".exe" suffix for MSDOS, OS/2 and VMS.
   */
   if (   (i > 4)
       && !strcmp(ptr_char+i-4, ".exe"))
    {
      i -= 4;
      *(ptr_char + i) = '\0';
    }
   prgr_name = (char *)my_malloc (i+1, 124, __FILE__, (long)__LINE__, "prgr_name", 0);
   strcpy(prgr_name, ptr_char);
   if (argc > 1)
    {
      if (   (strlen(argv[1]) == 1)
          && (*argv[1] == *SWITCH))
        /*
           Stdin channel explicitly wanted!
        */
        fp = stdin;
      else
       {
         if (   (strlen(argv[1]) > 2)
             && (*argv[1] == *SWITCH)
             && (*(argv[1]+1) == *SWITCH))
          {
            if (!strncmp(argv[1]+2, "help", strlen(argv[1]+2)))
              usage_msg (stdout, prgr_name, 0);
            if (!strncmp(argv[1]+2, "version", strlen(argv[1]+2)))
              version_msg (stdout, 0);
            /*
               Error, unknown long-style option given.  
            */
#if USE_GER
            fprintf(stderr, "%s: unbekannte Option `%s'", prgr_name, argv[1]);
#else /* !USE_GER */
            fprintf(stderr, _("%s: unrecognized option `%s'"), prgr_name, argv[1]);
#endif /* !USE_GER */
            S_NEWLINE(stderr);
            usage_msg (stderr, prgr_name, 1);
          }
         else
          {
#if HAVE_SYS_STAT_H && defined(S_IFMT) && defined(S_IFREG)
             auto struct stat  statbuf;


             /*
                Test if the file is a regular file, if not, this is an error!
             */
             if (!stat(argv[1], &statbuf))
              {
                if ((statbuf.st_mode & S_IFMT) == S_IFREG)
                  fp = fopen(argv[1], "r");
                else
                  is_regular_file = FALSE;
              }   
#else /* !HAVE_SYS_STAT_H || !S_IFMT || !S_IFREG */
             fp = fopen(argv[1], "r");
#endif /* !HAVE_SYS_STAT_H || !S_IFMT || !S_IFREG */
           }
       }
    }
   else
     fp = stdin;
   if (fp == (FILE *)NULL)
    {
      i = (int)strlen(prgr_name) + strlen(argv[1]) + 5;
      if ((Uint)i >= maxlen_max)
        s1 = (char *)my_realloc ((VOID_PTR)s1, i+1, 124,
                                 __FILE__, (long)__LINE__, "s1", 0);
      sprintf(s1, "%s: (%s) ", prgr_name, argv[1]);
#if HAVE_ERRNO_H
      if (!is_regular_file)
#  if USE_GER
        fprintf(stderr, "%s: keine regulaere Datei\n", s1);
#  else /* !USE_GER */
        fprintf(stderr, _("%s: no regular file\n"), s1);
#  endif /* !USE_GER */
      else
        perror(s1);
#else /* !HAVE_ERRNO_H */
#  if USE_GER
      fprintf(stderr, "%s: Datei nicht gefunden\n", s1);
#  else /* !USE_GER */
      fprintf(stderr, _("%s: file not found\n"), s1);
#  endif /* !USE_GER */
#endif /* !HAVE_ERRNO_H */
      exit(1);
    }
   if (argc >= 3)
     fprintf(stdout, "%s ", argv[2]);
   else
     fputs("0 ", stdout);
   while ((ch=ch2=fgetc(fp)) != EOF)
    {
      switch (ch)
       {
         case QUOTE_CHAR:
           ch2 = fgetc(fp);
           if (ch2 != EOF)
             switch (ch2)
              {
                case '\n':
                  fprintf(stdout, "%c %c%c%c", ch, RC_NL_CHAR, ch, ch2);
                  break;
                case RC_NL_CHAR:
                case RC_TVAR_CHAR:
                case RC_MACRO_CHAR:
                  fprintf(stdout, "%c%c%c", ch, ch, ch2);
                  break;
                default:
                  fprintf(stdout, "%c%c", ch, ch2);
              }
           else
             fprintf(stdout, "%c", ch);
           break;
         case RC_TVAR_CHAR:
           ch2 = fgetc(fp);
           if (ch2 != EOF)
            {
              if (   isalpha(ch2)
                  || ch3 == QUOTE_CHAR)
                fputc(QUOTE_CHAR, stdout);
              switch (ch2)
               {
                 case RC_NL_CHAR:
                   fprintf(stdout, "%c%c%c", ch, QUOTE_CHAR, ch2);
                   break;
                 case '\n':
                   fprintf(stdout, "%c%c%c%c", ch, RC_NL_CHAR, QUOTE_CHAR, ch2);
                   break;
                 default:
                   fprintf(stdout, "%c%c", ch, ch2);
               }
            }
           else
             fprintf(stdout, "%c", ch);
           break;
         case RC_MACRO_CHAR:
           ch2 = fgetc(fp);
           if (ch2 != EOF)
             switch (tolower(ch2))
              {
                case RC_SDATE_CHAR:
                case RC_EDATE_CHAR:
                case RC_WNAME_CHAR:
                case RC_WNUMBER_CHAR:
                case RC_BYEAR_CHAR:
                case RC_DATE_CHAR:
                case RC_DAY_CHAR:
                case RC_WEEK_CHAR:
                case RC_MONTH_CHAR:
                case RC_YEAR_CHAR:
                case RC_TIME_CHAR:
                case RC_JDAYS_CHAR:
                case RC_HLS1S_CHAR:
                case RC_HLS1E_CHAR:
                case RC_HLS2S_CHAR:
                case RC_HLS2E_CHAR:
                  fputc(QUOTE_CHAR, stdout);
                  ch3 = '\0';
                default:
                  if (ch3 == QUOTE_CHAR)
                    fputc(QUOTE_CHAR, stdout);
                  switch (ch2)
                   {
                     case RC_NL_CHAR:
                       fprintf(stdout, "%c%c%c", ch, QUOTE_CHAR, ch2);
                       break;
                     case '\n':
                       fprintf(stdout, "%c%c%c%c", ch, RC_NL_CHAR, QUOTE_CHAR, ch2);
                       break;
                     default:
                       fprintf(stdout, "%c%c", ch, ch2);
                   }
              }
           else
             fprintf(stdout, "%c", ch);
           break;
         case RC_NL_CHAR:
           fputc(QUOTE_CHAR, stdout);
         default:
           switch (ch)
            {
              case '\n':
                ch2 = fgetc(fp);
                if (ch2 != EOF)
                 {
                   switch (ch3)
                    {
                      case QUOTE_CHAR:
                        fprintf(stdout, " %c%c%c", RC_NL_CHAR, ch3, ch);
                        break;
                      default:
                        fprintf(stdout, "%c%c%c", RC_NL_CHAR, QUOTE_CHAR, ch);
                    }
                   ungetc(ch2, fp);
                 }
                break;
              default:
                if (   isspace(ch)
                    && !is_trailing_whitespace)
                  fprintf(stdout, "%c", QUOTE_CHAR);
                is_trailing_whitespace = TRUE;
                fprintf(stdout, "%c", ch);
            }
       }
      ch3 = ch2;
    }
   fclose(fp);

   return 0;
}
