/* MEMO: tried TABCHAR stuff, for various files */
#define LASTDITCH 1
/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1992 by Natrlich!                  */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
/* rev. history   1.0 --                                      */
/*                1.1 --                                      */
/*                1.2 --                                      */
/*                1.3 -- got a bit more userfriendly          */
/*                1.4 -- bug fix ?                            */
/*                1.5 -- merged with tos_unix                 */
/*                1.6 -- fixed little masking bug in <mode>   */
/*                       different tabchar                    */
/*                       last ditch code added                */
/*                1.7 -- increased portability                */
/*                       time date setting for everyone       */
/* ---------------------------------------------------------- */
#include "defines.h"
#undef VERSION
#undef REVISION
#define VERSION   1
#define REVISION  7

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include OSBIND
#include "nasm.h"

#ifndef __hp9000s300
# define MALLOCING 1
#endif

#if OS == AMIGA || defined( SUN)
# include <utime.h>
#endif

#if OS == MSDOS && __TURBOC__
# define perror( s)  fputs( s, stderr)
#endif

#if OS == TOS || OS == MSDOS
# if OS == TOS 
#  include <ext.h>      /* for stat */
# endif
static struct ffblk  fbuf;
#endif

#if OS == TOS
# define LOSER()     keypress()
  int   tossable;
  void  keypress()
  {
     if( tossable)
     {
        while( Bconstat( 2))
           Bconin( 2);
        Bconin( 2);
     }
  }

# ifdef __TURBOC__      /* PUREC fouls up too */
#  define stat( x, y)   mystat( y)

   void  mystat( p)
   struct stat *p;
   {
      p->st_size = fbuf.ff_fsize;
      p->st_mtime = *(long *) &fbuf.ff_ftime;   /* sorta hackish */
   }
# endif   
#else
# define LOSER()
#endif



#ifdef __TURBOC__
# define off_t    slword
#endif



#if LASTDITCH
# if OS == TOS || OS == MSDOS
   static char ditch[ 128]  = "f:\\tmp\\";
#  define DIR_SLASH '\\'
# else
   static char ditch[ 128] = "/tmp/";
#  define DIR_SLASH '/'
# endif


void last_ditch( to_file, abuffer, size)
char      *to_file;
void huge *abuffer;
lword     size;
{
   int   fd;
   char  *z;

   strcat( ditch, (z = strrchr( to_file, DIR_SLASH)) ? z + 1 : to_file);
   if( (fd = (int) Fkreate( ditch, 0644)) >= 0)
   {
# if OS == /* fucking */ MSDOS
      _fmode = O_BINARY;
# endif
      Fwrite( fd, size, abuffer);
      Fclose( fd);
      fprintf( stderr, "*** Backup in \"%s\" ***\n", ditch);
   }
}
#else
# define last_ditch( x, y, z)
#endif


void nerror( s)
char  *s;
{
   static char   buf[256];

   sprintf( buf, "crlf155: Error \"%s\"\n", s);
   perror( buf);
   LOSER();
   exit(1);
}

void nserror( s, t)
char   *s, *t;
{
   static char   buf[256];

   sprintf( buf, s, t);
   nerror( buf);
}



#define MAXBUF  0x40000    /* sorry but the fucking HP malloc commits */
                           /* suicide when fragmenting ... (or what ?)*/
#if ! MALLOCING
static char foobuf[ MAXBUF],
            memerr2[] = "static buffer too small, recompile!";
#endif
static char huge *p;
static char memerr1[] = "out of memory",
            blurb[] =
{
'c','r','l','f','1','5','5',' ','v',VERSION+'0','.',REVISION+'0',' ',
'(','c',')',' ','1','9','9','2',' ','b','y',' ',
'N','a','t','u','e','r','l','i','c','h','!',
' ','-','-',' ','c','o','n','v','e','r','t','s',' ',
#if OS == UNIX || AMIGA
        'C','R','/','L','F',' ','t','o',' ','L','F',
#else
        'L','F',' ','t','o',' ','C','R','/','L','F',
#endif
         ' ','a','n','d',' ','m','o','r','e','.','.','.','\n'
};

#if OS == TOS || OS == MSDOS
# define UNIX_TOS    0
# define TOS_UNIX    4
# else
# define TOS_UNIX    0
# define UNIX_TOS    4
#endif

#define ATARI_UNIX   1
#define ATARI_TOS    2
#define UNIX_ATARI   5
#define TOS_ATARI    6


FILE  *fp, *fq, *fopen();
int   fd,
      showstuff,
      mode,
      untabify,
      tabchar,
      eliminate,
      tabs     = -1,
      xtabc    = -1,
      hardtabs = 1,
      unmatched= 1;


char huge *atari_tos( size)
register lword size;
{
   register byte huge *q;

#if MALLOCING
   if( ! (p = Malloc( size + size)))
      nerror( memerr1);
#else
   if( size >= MAXBUF)
      nerror( memerr2);
#endif

   for( q = (byte huge *) p; size--;)
   if( (*q++ = getc( fp)) == 155)
   {
      q[ -1] = '\r';
      *q++   = '\n';
   }
   return( (char huge *) q);
}


char huge *atari_unix( size)
register lword size;
{
   register byte  *q;
   register int   c;

#if MALLOCING
   if( ! (p = Malloc( size)))
      nerror( memerr1);
#else
   if( size >= MAXBUF)
      nerror( memerr2);
#endif
   for( q = (byte huge *) p; size--;)
      *q++ = ((c = getc( fp)) == 155) ? '\n' : c;
   return( (char huge *) q);
}


char huge *tos_atari( size)
register lword   size;
{
   register char huge *q;

#if MALLOCING
   if( ! (p = Malloc( size)))
      nerror( memerr1);
#else
   if( size >= MAXBUF)
      nerror( memerr2);
#endif
   for( q = p; size--;)
      if( (*q++ = getc( fp)) == '\r' &&
         (size-- ? ((*q++ = getc( fp)) == '\n') : (size = 0)))
         q--[ -2] = 155;
   return( q);
}


char huge *unix_atari( size)
register lword size;
{
   register char huge  *q;
   register int        c;

#if MALLOCING
   if( ! (p = Malloc( (lword) size)))
      nerror( memerr1);
#else
   if( size + size >= MAXBUF)
      nerror( memerr2);
#endif
   for( q = p; size--;)
      *q++ = (c = getc( fp)) == '\n' ? 155 : c;
   return( q);
}


char huge   *none( size)
register lword size;
{
   register char huge  *q;

#if MALLOCING
   if( ! (p = Malloc( (lword) size)))
      nerror( memerr1);
#else
   if( size + size >= MAXBUF)
      nerror( memerr2);
#endif
   for( q = p; size--;)
      *q++ = getc( fp);
   return( q);
}



char huge *unix_tos( size)
lword size;
{
   register char huge *q;
   register int       c;

#if MALLOCING
   if( ! (p = Malloc( (lword) (size + size))))
      nerror( memerr1);
#else
   if( size + size >= MAXBUF)
      nerror( memerr2);
#endif
   for( q = p, c = 'a'; c != EOF;)
      if( (*q++ = c = getc( fp)) == '\n')
      {
         q[ -1] = '\r';
         *q++ = '\n';
      }
   return( q - 1);
}


char huge *tos_unix( size)
lword size;
{
   register char huge *q;
   register int       c;

#if MALLOCING
   if( ! (p = Malloc( (lword) size)))
      nerror( memerr1);
#else
   if( size >= MAXBUF)
      nerror( memerr2);
#endif
   if( eliminate)
   {
      for( q = p, c = 'a'; c != EOF;)
      {
         while( (c = getc( fp)) == 032);
#if OS == UNIX || AMIGA
         if( (*q++ = c) == '\r' &&
            (q[-1] = getc( fp)) != '\n')
         {
            *q = q[-1];
            q[ -1] = c;
            c = *q;
         }
#else
         *q++ = c;
#endif
      }
   }
   else
      for( q = p, c = 'a'; c != EOF;)
#if OS == UNIX || AMIGA
         if( (*q++ = c = getc( fp)) == '\r' &&
            (q[-1] = getc( fp)) != '\n')
         {
            *q = q[-1];
            q[ -1] = c;
            c = *q;
         }
#else
         *q++ = c = getc( fp);
#endif
   return( q - 1);
}


void  do_it( to_file)
char  *to_file;
{
   off_t              size, oldsize;
   struct stat        sbuf;
   register char huge *q;
   register lword     y;

#if ! MALLOCING
   p = foobuf;
#endif
   if( q = getenv( "TMPDIR"))
      strcpy( ditch, (char *) q);
   if( ! (fp = fopen( to_file, (mode & 0xB) ? "rb" : "r" )))
      nserror("File open of \"%s\" failed", to_file);
   stat( to_file, &sbuf);

   if( ! (oldsize = sbuf.st_size))
   {
      printf("File \"%s\" is empty\n", to_file);
      return;
   }
   if( showstuff)
   {
      printf("Converting \"%s\" with %ld bytes ", to_file, (long) oldsize);
      fflush( stdout);
   }

   if( (size_t) oldsize != (off_t) oldsize)
      nerror("runtime: conversion problem");

   switch( mode)
   {
      default :
         q = none( oldsize);
         break;

      case UNIX_TOS :
         q = unix_tos( oldsize);
         break;

      case TOS_UNIX :
         q = tos_unix( oldsize);
         break;

      case ATARI_UNIX:
         q = atari_unix( oldsize);
         break;

      case UNIX_ATARI:
         q = unix_atari( oldsize);
         break;

      case TOS_ATARI:
         q = tos_atari( oldsize);
         break;

      case ATARI_TOS:
         q = atari_tos( oldsize);
   }
   fclose( fp);

   size = (off_t) ( q - p);
   if( untabify)
      for( q = p, y = size; *q++ != tabchar && y; y--);

   if( untabify && y)
   {
      register lword    i, len;
      register int      j, c;

      if( ! (fq = fopen( to_file, "wb")))
         nserror("Opening of \"%s\" failed", to_file);
      if( hardtabs)     /* won't expand last char in file */
         for( q = p, i = len = 0, j = tabs; i != size - 1; i++)
            if( (c = *q++) != tabchar)
            {
               if( putc( c, fq) == '\n' || ! --j)
                  j = tabs;
               len++;
            }
            else
            {
               while( j)
               {
                  putc( ' ', fq);
                  len++;
                  j--;
               }
               j = tabs;
            }
      else
         for( q = p, len = i = 0; i != size - 1; i++)
         {
            if( (c = *q++) != tabchar)
            {
               putc( c, fq);
               len++;
            }
            else
               for( j = tabs; j; putc( ' ', fq), len++, j--);
         }

      if( putc( *q, fq) == EOF)
      {
         fclose( fq);
         last_ditch( to_file, p, size);
         nerror("write failed");
      }
      if( showstuff)
         printf("%s to %ld bytes\n", (len > oldsize) ? "up" : "down",
                                        (long) len);
      fclose( fq);
   }
   else
      if( (mode & 0x3) || size != oldsize)
      {
         if( (fd = (int) Fkreate( to_file, 0644)) < 0)
            nserror("Creation of \"%s\" failed", to_file);
#if OS == /* fucking */ MSDOS
         _fmode = O_BINARY;
#endif
         if( showstuff)
            printf("%s to %ld bytes\n", (size > oldsize) ? "up" : "down",
                                (long) size);
         if( size != Fwrite( fd, size, p))
         {
            Fclose( fd);
            last_ditch( to_file, p, size);
            nerror("write failed");
         }
         Fclose( fd);
      }
      else
      {
         if( showstuff)
            puts( "to equal size. [no update]");
        goto free_em;
      }
        /* due to CLOSE writing the creation date, we got to do this */

#if OS == TOS
   if( (fd = (int) Fopen( to_file, 0)) < 0)
      fprintf( stderr, "Someone snarfed your output file (not me!)\n");
   else
   {
      Fdatime( (DOSTIME *) &sbuf.st_mtime, fd, 1);
      Fclose( fd);
   }
#else
   {
      struct utimbuf  time;
 
      time.actime  = sbuf.st_atime;
      time.modtime = sbuf.st_mtime;
      if( utime( to_file, &time))
         nserror("Timestamp update failed on \"%s\"", to_file);
   }
#endif           
free_em:
#if MALLOCING
   Mfree( (void *) p)
#endif
   ;
}


main( argc, argv)
int   argc;
char  **argv;
{
   int   i = 0,
         foo;

   if( argc == 1)
      goto usage;
   while( ++i < argc)
   {
      if( *argv[ i] == '-')
         switch( argv[ i][ 1])
         {
#ifdef __DATE__
            case ':' :
               fputs( __DATE__, stderr);
# ifdef __TIME__
               fprintf( stderr, " %s", __TIME__);
# endif
               putc( '\n', stderr);
               break;
#endif
#if OS == TOS
            case 'T' :
            case 't' :
               tossable = ! tossable;
               break;
#endif
            case 'V' :  /* at runtime would be better, but more painful */
            case 'v' :  /* to code */
               fputs( blurb, stderr);
               break;

            case 'h' :
            case 'H' :
               hardtabs = ! hardtabs;
               break;

       case 'z' :
            case 'Z' :
          eliminate = ! eliminate;
          break;

            case 'a' :
            case 'A' :
               mode = (mode & 0xFC) | ATARI_UNIX;
               break;
            
            case 'x' :
            case 'X' :
               mode = (mode & 0xFC) | ATARI_TOS;
               break;

            case 'b' :
            case 'B' :
               mode ^= UNIX_TOS | TOS_UNIX;
               break;

            case 'u' :
            case 'U' :
               untabify = ! untabify;
               if( argv[ i][ 2] && sscanf( &argv[ i][ 2],"%X", &foo))
                  tabs = foo;
               break;

            case 's' :
            case 'S' :
               showstuff = ! showstuff;
               break;

            case 'N' :
            case 'n' :
               mode ^= 0x8;
               break;

            case 'C' :
            case 'c' :
            {
               unsigned char  d = argv[ i][ 2];
               
               if( d == '\\')
                  switch( argv[ i][ 2])
                  {
                     case 't' :
                        d = '\t';
                        break;
                        
                     case 'r' :
                        d = '\r';
                        break;

                     case 'b' :
                        d = '\b';
                        break;

                     case 'n' :
                        d = '\b';
                        break;

                     default  :
                        goto usage;
                  }                      
               else
                  if( d >= '0' && d <= '9')
                     d = (char) atoi( &argv[ i][ 2]);
               xtabc = d;
               break;
            }
                                        
            default  :
               goto usage;
         }
      else
      {
         static char xbuf[ 256];
         static char fname[ 13];
         char        *p, *q;

         if( tabs < 0)
            tabs = (mode == TOS_UNIX || mode == TOS_ATARI) ? 3 : 8;
         tabchar = (xtabc < 0) 
                     ? ((mode == ATARI_UNIX || mode == ATARI_TOS) 
                           ? 127 : '\t') 
                     : xtabc;

#if OS == TOS || OS == MSDOS     /* if findfirst works */
         q = argv[ i];
         p = xbuf;
         strcpy( p, q);
         if( ! (p = strrchr( q, '\\')))
            p = xbuf;
         else
            p = xbuf + (int) ((q = p + 1) - argv[ i]);
         strcpy( p, "*.*");
         if( findfirst( xbuf, &fbuf, 0)) /* only normal files ? */
            nserror("Directory search failed for \"%s\"", xbuf);
         {
            register char  *x, *y;

            do
            {
               for( x = fbuf.ff_name, y = fname; *x; y++, x++)
                  *y = tolower( *x);
               *y = 0;

               if( wildmat( fname, q))
               {
                  unmatched = 0;
                  strcpy( p, fname);
                  do_it( xbuf);
               }
            }
            while( ! findnext( &fbuf));
    }
#else
         do_it( argv[ i]);
    unmatched = 0;
#endif
      }
   }
   return( unmatched);

usage:
   fputs( "\
usage: crlf155 ([flags] <pattern>)+\n\
\t-a\t: convert 155 to LF\n\
\t-n\t: just untabify\n\
\t-x\t: convert 155 to CR/LF\n\
\t-c<chr>\t: tab character\n\
\t-h\t: no hardtabs\n\
\t-z\t: eliminates ^Zs from MSDOS files\n\
\t-b\t: backwards, change conversion direction, f.e. LF to 155\n\
\t-u[val]\t: untabify, default spaces: TOS->UNIX : 3, UNIX->TOS : 8\n\
\t-s\t: show some statistics for user amusement\n", stderr);

#if OS == TOS
   fputs( "\
\t-t\t: halt screen before exiting\n\n\
\t\t* Warning: filenames and patterns: all lowercase!\n\
\t\t  *.C won't work, *.c will\n", stderr);
#endif
   fputs( "\n\
\t\t  Insure that there is enough discspace, else one file might\n\
\t\t  get truncated\n", stderr);
   LOSER();
   exit(1);
}

#if OS != UNIX
/*  $Revision: 1.7 $
**
**  Do shell-style pattern matching for ?, \, [], and * characters.
**  Might not be robust in face of malformed patterns; e.g., "foo[a-"
**  could cause a segmentation violation.  It is 8bit clean.
**
**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
**  Rich $alz is now <rsalz@bbn.com>.
**  April, 1991:  Replaced mutually-recursive calls with in-line code
**  for the star character.
**
**  Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code.
**  This can greatly speed up failing wildcard patterns.  For example:
**      pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-*
**      text 1:  -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1
**      text 2:  -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1
**  Text 1 matches with 51 calls, while text 2 fails with 54 calls.  Without
**  the ABORT, then it takes 22310 calls to fail.  Ugh.  The following
**  explanation is from Lars:
**  The precondition that must be fulfilled is that DoMatch will consume
**  at least one character in text.  This is true if *p is neither '*' nor
**  '\0'.)  The last return has ABORT instead of FALSE to avoid quadratic
**  behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx".  With
**  FALSE, each star-loop has to run to the end of the text; with ABORT
**  only the last one does.
**
**  Once the control of one instance of DoMatch enters the star-loop, that
**  instance will return either TRUE or ABORT, and any calling instance
**  will therefore return immediately after (without calling recursively
**  again).  In effect, only one star-loop is ever active.  It would be
**  possible to modify the code to maintain this context explicitly,
**  eliminating all recursive calls at the cost of some complication and
**  loss of clarity (and the ABORT stuff seems to be unclear enough by
**  itself).  I think it would be unwise to try to get this into a
**  released version unless you have a good test data base to try it out
**  on.
*/

#define TRUE                    1
#define FALSE                   0
#define ABORT                   -1


    /* What character marks an inverted character class? */
#define NEGATE_CLASS            '^'
    /* Is "*" a common pattern? */
#define OPTIMIZE_JUST_STAR
    /* Do tar(1) matching rules, which ignore a trailing slash? */
#undef MATCH_TAR_PATTERN


/*
**  Match text and p, return TRUE, FALSE, or ABORT.
*/
static int
DoMatch(text, p)
    char        *text;
    char        *p;
{
    int last;
    int matched;
    int reverse;

    for ( ; *p; text++, p++) {
        if (*text == '\0' && *p != '*')
            return ABORT;
        switch (*p) {
        case '\\':
            /* Literal match with following character. */
            p++;
            /* FALLTHROUGH */
        default:
            if (*text != *p)
                return FALSE;
            continue;
        case '?':
            /* Match anything. */
            continue;
        case '*':
            while (*++p == '*')
                /* Consecutive stars act just like one. */
                continue;
            if (*p == '\0')
                /* Trailing star matches everything. */
                return TRUE;
            while (*text)
                if ((matched = DoMatch(text++, p)) != FALSE)
                    return matched;
            return ABORT;
        case '[':
            reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
            if (reverse)
                /* Inverted character class. */
                p++;
            for (last = 0400, matched = FALSE; *++p && *p != ']'; last = *p)
                /* This next line requires a good C compiler. */
                if (*p == '-' ? *text <= *++p && *text >= last : *text == *p)
                    matched = TRUE;
            if (matched == reverse)
                return FALSE;
            continue;
        }
    }

#ifdef  MATCH_TAR_PATTERN
    if (*text == '/')
        return TRUE;
#endif  /* MATCH_TAR_ATTERN */
    return *text == '\0';
}


/*
**  User-level routine.  Returns TRUE or FALSE.
*/
int
wildmat(text, p)
    char        *text;
    char        *p;
{
#ifdef  OPTIMIZE_JUST_STAR
    if (p[0] == '*' && p[1] == '\0')
        return TRUE;
#endif  /* OPTIMIZE_JUST_STAR */
    return DoMatch(text, p) == TRUE;
}
#endif

