/* crlf.c 1.3 by entropy@terminator.rs.itd.umich.edu

   PUBLIC DOMAIN -- NO RIGHTS RESERVED
   NO WARRANTY -- USE AT YOUR OWN RISK!!!!!!!!!
   strips/adds carriage returns from text files
*/
#ifndef __atarist__
#ifdef __TOS__   /* #defined by TurboC / PureC */
  #define __atarist__
#endif
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <utime.h>
#define UNUSED(x)
#ifdef __atarist__
#include <compiler.h>
#ifdef __TURBOC__
#include <sys\types.h>
#include <sys\stat.h>
#undef UNUSED
#define UNUSED(x) ((void)(x))
#else /* not __TURBOC__ */
#include <sys/types.h>
#include <sys/stat.h>
#endif /* not __TURBOC__ */
#else /* not __atarist__ */
#include <sys/types.h>
#include <sys/stat.h>
#ifdef sun
#include <utime.h>
#endif /* sun */
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 2
#endif /* EXIT_FAILURE */
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif /* EXIT_SUCCESS */
#ifdef __STDC__
#ifndef __NO___PROTO__
#define __PROTO(x) x
#endif /* not __NO___PROTO__ */
#define __EXTERN
#else /* not __STDC__ */
#define __EXTERN extern
#define __PROTO(x) ()
#endif /* not __STDC__ */
#endif /* not __atarist__ */

extern char *optarg;
extern int optind, opterr;

#define CR_STRIP 0
#define CR_ADD 1
#define CR_NONE 2

char *program = NULL;
int verbose = 0;

void perror2 __PROTO((char *msg1, char *msg2));
int crlf __PROTO((char *filename, int mode));
int simple_outchar __PROTO((FILE *ofp, int c));
int tounx_outchar __PROTO((FILE *ofp, int c));
int totos_outchar __PROTO((FILE *ofp, int c));
int smart_copy __PROTO((char *ifn, char *ofn,
                      int (*outcharfunc)(FILE *, int)));

void
perror2(msg1, msg2)
  char *msg1;
  char *msg2;
{
  if (msg1 && *msg1)
  {
    fputs(msg1, stderr);
    fputs(": ", stderr);
  }
  perror(msg2);
}

int
simple_outchar(ofp, c)
  FILE *ofp;
  int c;
{
  fputc(c, ofp);
  return (c);
}

int
totos_outchar(ofp, c)
  FILE *ofp;
  int c;
{
  if (c == '\r' ) /* first strip all CRs... */
    return c;
  if (c == '\n')
  {
    fputc('\r', ofp);
  }
  fputc(c, ofp);
  return (c);
}

int
tounx_outchar(ofp, c)
  FILE *ofp;
  int c;
{
  if (c != '\r')
  {
    fputc(c, ofp);
  }
  return (c);
}

int
smart_copy(ifn, ofn, outcharfunc)
  char *ifn;
  char *ofn;
  int (*outcharfunc) __PROTO((FILE *, int));
{
  FILE *ifp;
  FILE *ofp;
  int c;

  if (*ifn) 
  {
    if ((ifp = fopen(ifn, "rb")) == NULL)
    {
      perror2(program, ifn);
      return (-1);
    }
  }
  else
  {
    ifp = stdin;
  }
  if (*ofn) 
  {
    if ((ofp = fopen(ofn, "wb")) == NULL)
    {
      fclose(ifp);
      perror2(program, ofn);
      return (-1);
    }
  }
  else
  {
    ofp = stdout;
  }
  setvbuf(ofp, NULL, _IOFBF, 50*1024L);
  setvbuf(ifp, NULL, _IOFBF, 50*1024L);
  c = fgetc(ifp);
  while (!(feof(ifp)) && !(ferror(ifp)) && !(ferror(ofp)))
  {
    (*outcharfunc)(ofp, c);
    c = fgetc(ifp);
  }
  if (ferror(ifp))
  {
    perror2(program, ifn);
    if (*ifn)
    {
      fclose(ifp);
    }
    if (*ofn)
    {
      fclose(ofp);
    }
    return (-1);
  }
  if (ferror(ofp))
  {
    perror2(program, ofn);
    if (*ifn)
    {
      fclose(ifp);
    }
    if (*ofn)
    {
      fclose(ofp);
    }
    return (-1);
  }
  if (*ifn && fclose(ifp) == EOF)
  {
    perror2(program, ifn);
    if (*ofn)
    {
      fclose(ofp);
    }
    return (-1);
  }
  if (*ofn && fclose(ofp) == EOF)
  {
    perror2(program, ofn);
    return (-1);
  }
  return (0);
}

int
crlf(filename, mode)
  char *filename;
  int mode; /* 0 == strip CR's, 1 == add CR's */
{
  char tempname[FILENAME_MAX];
  char *ev;
  int (*outcharfunc) __PROTO((FILE *, int));

  switch (mode)
  {
    case CR_ADD:
      outcharfunc = totos_outchar;
      break;
    case CR_STRIP:
    default:
      outcharfunc = tounx_outchar;
      break;
  }
  if (verbose)
  {
    fputs(filename, stderr);
    fputc('\n', stderr);
  }
  if (*filename)
  {
    *tempname = '\0';
    if ((ev = getenv("TEMP")) || (ev = getenv("TMP"))
        || (ev = getenv("TMPDIR")))
    {
      strcpy(tempname, ev);
      strcat(tempname, "/");
    }
    strcat(tempname, "crlfXXXX");
    if (mktemp(tempname) == NULL)
    {
      fputs(program, stderr);
      fputs(": could not get a temporary filename\n", stderr);
      return (-1);
    }
    if (smart_copy(filename, tempname, outcharfunc))
    {
      return (-1);
    }
    if (rename(tempname, filename))
    {
      if (smart_copy(tempname, filename, simple_outchar))
      {
        return (-1);
      }
      if (unlink(tempname))
      {
        perror2(program, tempname);
        return (-1);
      }
    }
  }
  else
  {
    if (smart_copy("", "", outcharfunc))
    {
      return (-1);
    }
  }
  return (0);
}

int
main(int argc, char *argv[], char *envp[])
{
  char *fn = NULL;
  int mode = CR_NONE;
  int c;
  int err = 0;
  struct stat st;
  struct utimbuf ut;

  UNUSED(envp);
  program = argv[0];
  while ((c = getopt(argc, argv, "asv")) != EOF)
  {
    switch (c)
    {
      case 'a':
        if (mode == CR_NONE)
        {
          mode = CR_ADD;
        }
        else
        {
          err++;
        }
        break;
      case 's':
        if (mode == CR_NONE)
        {
          mode = CR_STRIP;
        }
        else
        {
          err++;
        }
        break;
      case 'v':
        verbose = 1;
        break;
      case '?':
      default:
        err++;
        break;
    }
  }
  if (mode == CR_NONE)
  {
    err++;
  }
  if (err)
  {
    fputs("usage:  ", stderr);
    fputs(program, stderr);
    fputs(" -s[v] [file [file2 [...]]] (to strip carriage returns)\n", stderr);
    fputs("        ", stderr);
    fputs(program, stderr);
    fputs(" -a[v] [file [file2 [...]]] (to add carriage returns)\n", stderr);
    exit(EXIT_FAILURE);
  }
  if (optind == argc)
  {
    if (crlf("", mode) != 0)
    {
      err++;
    }
  }
  else
  {
    for ( ; optind < argc; optind++)
    {
      fn = argv[optind];
      if (access(fn, 4))
      {
        err++;
        perror2(program, fn);
        continue;
      }
      if (stat(fn, &st))
      {
        err++;
        perror2(program, fn);
      }
      if (!(S_ISREG(st.st_mode)))
      {
        if (verbose)
        {
          fputs("crlf:  ignoring non-regular file ", stderr);
          fputs(fn, stderr);
          fputc('\n', stdout);
        }
        continue;
      }
      ut.actime = st.st_atime;
      ut.modtime = st.st_mtime;
      if (crlf(fn, mode) != 0)
      {
        err++;
        continue;
      }
      if (utime(fn, &ut))
      {
        err++;
        perror2(program, fn);
      }
    }
  }
  if (err)
  {
    exit(EXIT_FAILURE);
  }
  exit(EXIT_SUCCESS);
  return (0);
}
