/************************************************************************/
/*                                                                      */
/* Description: Ascii to PostScript printer program.                    */
/* File: imag:/users/local/a2ps/a2ps.c                                  */
/* Created: Mon Nov 28 15:22:15 1988 by miguel@imag (Miguel Santana)    */
/* Version: 2.0                                                         */
/*                                                                      */
/* Edit history:                                                        */
/* 1) Derived of shell program written by evan@csli (Evan Kirshenbaum). */
/*    Written in C for improve speed execution and portability. Many    */
/*    improvements have been added.                                     */
/* 2) Ported to the Amiga Jan 24, 1989 (Samuel Paolucci)                */
/*                                                                      */
/************************************************************************/

/*
 * Copyright (c) 1988, Miguel Santana, miguel@imag.imag.fr
 *
 * Permission is granted to copy and distribute this file in modified
 * or unmodified form, for noncommercial use, provided (a) this copyright
 * notice is preserved, (b) no attempt is made to restrict redistribution
 * of this file, and (c) this file is not distributed as part of any
 * collection whose redistribution is restricted by a compilation copyright.
*/


#include <stdio.h>
#ifdef ANSIC
#include <time.h>
#else
#ifdef UNIX
#include <sys/time.h>
#else
#ifdef SYSV
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#else
error !
#endif
#endif
#endif

#ifndef HEADER_PS
#define HEADER_PS       "./header.ps"
#endif
#define LINESPERPAGE    66
#define COLUMNSPERLINE  86

#define FALSE           0
#define TRUE            1

#define PS_EOF          04

int fold_line();
void print_file();
char cut_line();


int column = 0;                 /* Column number (in current line) */
int line = 0;                   /* Line number (in current page) */
int line_number = 0;            /* Source line number */
int first_page;                 /* First page for a file */
int nonprinting_chars, chars;   /* Number of nonprinting and total chars */
int prefix_width;               /* Width in characters for line prefix */
int numbering = TRUE;           /* Line numbering option */
int folding = TRUE;             /* Line folding option */
int restart = TRUE;             /* Restart page number at each file option */
int only_printable = FALSE;     /* Replace non printable char by space option */
int interpret = TRUE;           /* Interpret TAB, FF and BS chars option */
int print_binaries = FALSE;     /* Force printing for binary files */ 

main(argc, argv)
int argc;
char *argv[];
{
   int narg;
   char *arg;

   /* Option processing */
   arg = argv[narg = 1];
   while (narg < argc && arg[0] == '-')
   {
      switch (arg[1])
      {
      case 'b':
         if (arg[2] != NULL)
            goto usage;
         print_binaries = TRUE;
         break;
      case 'f':
         if (arg[2] != NULL)
            goto usage;
         folding = TRUE;
         break;
      case 'i':
         if (arg[2] != NULL)
            goto usage;
         interpret = TRUE;
         break;
      case 'n':
         if (arg[2] == NULL)
         {
            numbering = TRUE;
            break;
         }
         if (arg[3] != NULL)
            goto usage;
         switch (arg[2])
         {
         case 'b':
            print_binaries = FALSE;
            break;
         case 'f':
            folding = FALSE;
            break;
         case 'i':
            interpret = FALSE;
            break;
         case 'n':
            numbering = FALSE;
            break;
         case 'r':
            restart = FALSE;
            break;
         case 'v':
            only_printable = TRUE;
            break;
         default:
            goto usage;
         }
         break;
      case 'r':
         if (arg[2] != NULL)
            goto usage;
         restart = TRUE;
         break;
      case 'v':
         if (arg[2] != NULL)
            goto usage;
         only_printable = FALSE;
         break;
      default:
      usage:
         fprintf(stderr,
         "usage: a2ps [-n{bfinrv}] [-b] [-f] [-i] [-n] [-r] [-v] [f1 f2 ... fn]\n");
         exit(1);
      }
      arg = argv[++narg];
   }
   if (narg >= argc)
      goto usage;

   /* Header printing (postcript prolog) */
   print_header();

   /* Print files designated or standard input */
   prefix_width = numbering ? 6 : 1;
   if (narg >= argc)
      print_file("stdin");
   else
   {
      while (narg < argc)
      {
         if (freopen(arg, "r", stdin) == NULL)
         {
            fprintf(stderr, "Error opening %s\n", arg);
            printf("cleanup\n");
            exit(1);
         }
         print_file(arg);
         arg = argv[++narg];
      }
   }

   printf("cleanup\n");

   putchar(PS_EOF);
}

void print_file(name)
char *name;
{
   register int c;
   int start_line, continue_exit;
   int char_width;

   /*
    * Printing binary files is not very useful. We stop printing
    * if we detect one of these files. Our heuristic to detect them:
    * if 50% characters of first page are non-printing characters,
    * the file is a binary file.
    * Option -b force binary files impression.
    */
   first_page = TRUE;
   nonprinting_chars = chars = 0;

   /*
    * Preprocessing (before printing):
    * - TABs expansion (see interpret option)
    * - FF and BS interpretation
    * - replace non printable characters by a space or a char sequence
    *   like:
    *     ^X for ascii codes < 0x20 (X = [@, A, B, ...])
    *     ^? for del char
    *     M-c for ascii codes > 0x3f
    * - prefix parents and backslash ['(', ')', '\'] by backslash
    *   (escape character in postcript)
    */
   column = 0;
   line = line_number = 0;
   start_line = TRUE;
   printf("(%s) newfile\n", name);
   if (restart)
      printf("/sheet 1 def\n");
   printf("startpage\n");

   c = getchar();
   while (c != EOF)
   {
      /* Form feed */
      if (c == '\f' && interpret)
      {
         if (!start_line)
            printf(") s\n");
         start_line = TRUE;
         printf("endpage startpage\n");
         if (first_page && is_binaryfile(name))
            return;
         line = 0;
         if ((c = getchar()) == EOF)
            break;
      }

      /* Start a new line? */
      if (start_line)
      {
         if (numbering)
            printf("(%-5d ", ++line_number);
         else
            printf("( ");
         start_line = FALSE;
      }

      /* Interpret each character */
      switch (c)
      {
      case '\b':
         if (!interpret)
            goto print;
         if (column)
            column--;
         putchar(c);
         break;
      case '\n':
         column = 0;
         start_line = TRUE;
         printf(") s\n");
         if (++line >= LINESPERPAGE)
         {
            printf("endpage startpage\n");
            if (first_page && is_binaryfile(name))
               return;
            line = 0;
         }
         break;
      case '\t':
         if (interpret)
         {
            continue_exit = FALSE;
            do
            {
               if (++column + prefix_width > COLUMNSPERLINE)
                  if (folding)
                  {
                     if (fold_line(name) == FALSE)
                        return;
                  }
                  else
                  {
                     c = cut_line();
                     continue_exit = TRUE;
                     break;
                  }
               putchar(' ');
            } while (column & 0x7);
            if (continue_exit)
               continue;
            break;
        }
      default:
      print:
         if (only_printable)
            char_width = 1;
         else
         {
            char_width = c > 0177 ? 2 : 0;
            char_width += c < ' ' || c == 0177 ? 2 : 1;
         }
         if (prefix_width + (column += char_width) > COLUMNSPERLINE)
            if (folding)
            {
               if (fold_line(name) == FALSE)
                  return;
            }
            else
            {
               c = cut_line();
               continue;
            }
         if (c == '(' || c == ')' || c == '\\')
            putchar('\\');
         if (c >= ' ' && c < 0177)
            putchar(c);
         else
         {
            nonprinting_chars++;
            if (only_printable)
               putchar(' ');
            else
            {
               if (c > 0177)
               {
                  printf("M-");
                  c &= 0177;
               }
               if (c < ' ')
                  printf("^%c", c+'@');
               else if (c == 0177)
                  printf("^?");
               else
                  putchar(c);
            }
         }
         chars++;
         break;
      }
      c = getchar();
   }

   if (!start_line)
      printf(") s\n");
   printf("endpage\n");
}

int fold_line(name)
char *name;
{
   column = 0;
   printf(") s\n");
   if (++line >= LINESPERPAGE)
   {
      printf("endpage startpage\n");
      if (first_page && is_binaryfile(name))
         return FALSE;
      line = 0;
   }
   if (numbering)
      printf("(      ");
   else
      printf("( ");

   return TRUE;
}

char cut_line()
{
   char c;

   while ((c = getchar()) != EOF && c != '\n' && c != '\f');
   return c;
}

is_binaryfile(name)
char *name;
{
   first_page = FALSE;
   if (!print_binaries && (nonprinting_chars*100 / chars) >= 75)
   {
      fprintf(stderr, "%s is a binary file: printing aborted\n", name);
      return TRUE;
   }
   return FALSE;
}

print_header()
{
   register int c;
   FILE *f;
   char *string;
#ifdef ANSIC
   time_t date;
#else
#ifdef UNIX
   struct timeval date;
   struct tm *p;
#else
#ifdef SYSV
    struct timeb date;
#endif
#endif
#endif

   if ((f = fopen(HEADER_PS, "r")) == NULL)
   {
      fprintf(stderr, "Postcript header missing\n");
      exit(1);
   }

   /* Header file printing */
   while ((c = getc(f)) != EOF)
      putchar(c);

   /* Retrieve date and hour */
#ifdef ANSIC
   if (time(&date) == -1)
   {
      fprintf(stderr, "Error calculing time\n");
      exit(1);
   }
   string = ctime(&date);

   /* and print them */
   printf("/date (%.6s %.4s %.8s) def\n", string+4, string+20, string+11);
#else
#ifdef UNIX
   (void) gettimeofday(&date, (struct timezone *)0);
   p = localtime(&date.tv_sec);
   string = asctime(p);

   /* and print them */
   printf("/date (%.6s %.4s %.8s) def\n", string+4, string+20, string+11);
#else
#ifdef SYSV
   (void)ftime(&date);
   string = ctime(&date.time);
   printf("/date (%.6s %.4s %.8s) def\n", string+4, string+20, string+11);
#endif
#endif
#endif

   /* Go on */
   printf("startdoc\n");
}
