/*  file: utf7.l  */

%{

#ifdef __MSDOS__
#  include <dir.h>
#  include <fcntl.h>
#  include <io.h>
#else
#  include <unistd.h>
#endif
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

int
   printcode = 0;

unsigned int
   outcode [2],
   instep,
   outstep;

char
   *programname;

void
   get_programname (char const *argv0),
   syntax (void),
   errit (char const *format, ...),
   utf (void),
   nextout (void),
   outchar (long unsigned);

#ifdef __MSDOS__
#define strcasecmp(s1, s2) (stricmp(s1, s2))
#endif

#define YY_NO_UNPUT
#define YY_SKIP_YYWRAP
#ifdef yywrap
#  undef yywrap
#endif
int yywrap()
{
   return 1;
}

%}

%Start _utf

%%

<INITIAL>"+-"                   { fputc ('+', stdout); }
<INITIAL>"+"                    { instep = outstep = 0;
                                 BEGIN _utf; }

<_utf>[A-Za-z0-9+/]             { utf (); }
<_utf>"-"                       { BEGIN INITIAL; }
<_utf>.|\n                      { fputc (yytext [0], stdout);
                                 BEGIN INITIAL; }

%%

void utf ()
{
   unsigned
       i,
       c;

   i = yytext [0];
   if (i >= 'A' && i <= 'Z')
       c = i - 'A';
   else if (i >= 'a' && i <= 'z')
       c = i + 26 - 'a';
   else if (i >= '0' && i <= '9')
       c = i + 52 - '0';
   else if (i == '+')
       c = 62;
   else if (i == '/')
       c = 63;

   switch (instep) {
       case 0:
           outcode [outstep] = (c << 2);
           break;
       case 1:
           outcode [outstep] |= (c >> 4);
           nextout ();
           outcode [outstep] = (c << 4);
           break;
       case 2:
           outcode [outstep] |= (c >> 2);
           nextout ();
           outcode [outstep] = (c << 6);
           break;
       case 3:
           outcode [outstep] |= c;
           nextout ();
           break;
   }
   if (++instep == 4)
       instep = 0;
}

void nextout ()
{
   unsigned
       c;

   if (outstep == 0) {
       outstep = 1;
   } else {
       outstep = 0;
       c = ((outcode [0] & 0xFF) << 8) | (outcode [1] & 0xFF);
       outchar (c);
   }
}

void outchar (long unsigned c)
{
   int
       i;
   char
       *s;

   /*
    * iso-8859-1
    */
   if (c < 256) {
       fputc (c, stdout);
       return;
   }

   /*
    * iso-8859-15
    */
   i = 0;
   switch (c) {
       case 0x20Ac: i = 0xA4; break;    /* euro */
       case 0x0160: i = 0xA6; break;    /* S caron */
       case 0x0161: i = 0xA8; break;    /* s caron */
       case 0x017D: i = 0xB4; break;    /* Z caron */
       case 0x017E: i = 0xB8; break;    /* z caron */
       case 0x0152: i = 0xBC; break;    /* OE ligature */
       case 0x0153: i = 0xBD; break;    /* oe ligature */
       case 0x0178: i = 0xBE; break;    /* Y diaeresis */
   }
   if (i) {
       fputc (i, stdout);
       return;
   }

   /*
    * substitutions
    */
   s = NULL;
   switch (c) {
       case 0x0132: s = "IJ"; break;
       case 0x0133: s = "ij"; break;
   }
   if (s) {
       fputs (s, stdout);
       return;
   }

   if (printcode) {
       if (c < 0x10000)
           printf ("U+%04X", (unsigned) c);
       else
           printf ("U+%08lX", c);
   }  else
       fputc (191, stdout);
}

int main (int argc, char *argv [])
{
   get_programname (argv [0]);

   while (argc > 1)
       if (! strcmp (argv [1], "-c")) {
           printcode = 1;
           argv++;
           argc--;
       } else
           break;

   switch (argc) {
       case 1:
           if (isatty (fileno (stdin)))
               syntax ();
           yyin = stdin;
           break;
       case 2:
           yyin = fopen (argv [1], "r");
           if (! yyin)
               errit ("Opening file \"%s\": %s", argv [1], strerror (errno));
           break;
       default:
           syntax ();
   }

   yylex ();

   if (yyin != stdin)
       fclose (yyin);

   return 0;
}

void get_programname (char const *argv0)
{
#ifdef __MSDOS__
   char
       name [MAXFILE];
   fnsplit (argv0, NULL, NULL, name, NULL);
   programname = strdup (name);
#else   /* unix */
   char
       *p;
   p = strrchr (argv0, '/');
   if (p)
       programname = strdup (p + 1);
   else
       programname = strdup (argv0);
#endif
}

void errit (char const *format, ...)
{
   va_list
       list;

   fprintf (stderr, "\nError %s: ", programname);

   va_start (list, format);
   vfprintf (stderr, format, list);

   fprintf (stderr, "\n\n");

   exit (1);
}

void syntax ()
{
   fprintf (
       stderr,
       "\n"
       "Syntax: %s [-c] [utf-7 encoded file]\n"
       "\n"
       "The file will be translated to iso-8859-1 *and* iso-8859-15\n"
       "\n"
       "  -c : print U+code for characters not in iso-8859-1/15\n"
       "\n",
       programname
   );

   exit (1);
}