home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 19 Printer / 19-Printer.zip / REV.ZIP / rev.c < prev    next >
Text File  |  1993-02-26  |  33KB  |  999 lines

  1. #define VER     "1.0"
  2.  
  3. /*
  4.  * Text REVerter T.J. Domsalla, dommi@rz.tu-clausthal.de Version 0.93,
  5.  * Clausthal, 1991, 1993
  6.  */
  7.  
  8.  
  9. #ifdef READTEST
  10. #undef READTEST
  11. #define READTEST   1
  12. #else
  13. #define READTEST   0                     /* Eingabe gleich wieder ausgeben */
  14. #endif
  15.  
  16. #ifdef WRITETEST
  17. #undef WRITETEST
  18. #define WRITETEST  1
  19. #else
  20. #define WRITETEST  0                     /* Ausgabe der Seiten in Logfile */
  21. #endif
  22.  
  23.  
  24. char    *version_string = "PAGE REVERTER V "VER", "__DATE__", "__TIME__"\n"
  25.               " (l) T.J. Domsalla, TU Clausthal, catd@rz.tu-clausthal.de\n";
  26.  
  27.  
  28. /**************************************************************************/
  29.  
  30. #include <stdlib.h>
  31. #include <stdio.h>
  32. #include <string.h>
  33. #include <ctype.h>
  34. #include <sys/stat.h>
  35. #include <signal.h>
  36. #include <time.h>
  37. #include "getopt.h"
  38.  
  39. /**************************************************************************/
  40.  
  41. /* Environmentvariable f"ur vorbesetzte Optionen */
  42. #define REV_ENV_VAR         "REV_OPTS"
  43.  
  44. #define STD_LINES_PER_PAGE  65
  45. #define STD_COLS_PER_LINE   80
  46. #define STD_TABLENGTH       4
  47.  
  48. #define MAXPAGES        4096
  49. #define MINPAGES        128
  50.  
  51. #define BUFFERSIZE      0x80000          /* Lesebuffer */
  52. #define MINBUFFERSIZE   0x1000
  53.  
  54. /* mein Drucker Initialisierungsstring (HP Deskjet) f"ur undok. Option -~ */
  55. #define PRTINITSTRING   "E(s2q20h6V&l12d120F&a12l144M"
  56. #define MY_LINES_PER_PAGE   119
  57. #define MY_COLS_PER_LINE    120
  58.  
  59. typedef enum { FALSE, TRUE } Bool;
  60.  
  61. /**************************************************************************
  62.  * und einige systemspezifische Konstanten
  63.  */
  64. #define PROCESS_INDICATOR_CHANGE    100     /* nach wieviel eingelesenen
  65.                                              * Zeichen Processindikator
  66.                                              * "andern */
  67.  
  68. /**************************************************************************/
  69.  
  70. static void CtrlCExit (void);
  71. static void finish (void);
  72. static int readin (void);
  73. static int writeout (void);
  74. static int writepage (int page);
  75. static int writeline (char *);
  76. static int scanPageHeader(void);
  77. static int calcTab (int col);
  78. static int filelength (char *filename);
  79. void     show_process_indicator (void);
  80. static void usage (void);
  81.  
  82. /**************************************************************************/
  83.  
  84. Bool     LogProcess = FALSE;             /* write log file */
  85. Bool     ExtHelp = FALSE;                /* extended help */
  86. Bool     Verbose = FALSE;                /* Verbose mode */
  87. Bool     Version = FALSE;                /* Print Version */
  88. Bool     ForwardOutput = FALSE;          /* not revert output */
  89. Bool     DSided = FALSE;                 /* double sided */
  90. Bool     Evenforward = FALSE;            /* gerade Seiten vorw. */
  91. Bool     Evenbackward = FALSE;           /* gerade Seiten r"uckw. */
  92. Bool     Oddforward = FALSE;             /* ungerade Seiten vorw. */
  93. Bool     Oddbackward = FALSE;            /* ungerade Seiten r"uckw. */
  94. Bool     WithFF = TRUE;                  /* Seitenende mit \f */
  95. Bool     NoLastFF = TRUE;                /* bis auf nach letzter Seite */
  96. Bool     UndoCtrlChar = FALSE;           /* Ctrl-Zeichen in '.' umwandeln */
  97. Bool     PrintInitStr = FALSE;           /* f"ur meine HP DJ Initial. */
  98. unsigned int LinesPPage = STD_LINES_PER_PAGE;   /* Zeilen pro Seite */
  99. unsigned int ColsPLine = STD_COLS_PER_LINE; /* Spalten pro Zeile */
  100. int      StartPage = 0;                  /* erste Ausgabeseite */
  101. int      EndPage = -1;                   /* letzte Ausgabeseite */
  102. char    *InitFile = NULL;                /* Datei mit Initialisierungsstring */
  103. char    *InFile = NULL;                  /* Eingabedatei */
  104. char    *OutFile = NULL;                 /* Ausgabedatei */
  105. char    *PrgName;
  106.  
  107. FILE    *Stream;
  108. FILE    *LogF;
  109.  
  110. char    *Heading = NULL;                 /* Zeiger auf's Original"uberschrift */
  111. char     HeadString[512];                /* Platz f"ur "Uberschrift */
  112. Bool     PageNumbering = FALSE;
  113. /* folgende $Strings werden in Seiten"uberschrift ersetzt (s. scanPageHeader()) */
  114. char    *HeadVars[] = { "FILE", "DATE", "LINE", "PAGE" };
  115. #define HEAD_FILE   0
  116. #define HEAD_DATE   1
  117. #define HEAD_LINE   2
  118. #define HEAD_PAGE   3                    /* for page numbering */
  119.  
  120. unsigned long BufferSize = BUFFERSIZE;
  121. char    *Buffer = NULL;
  122.  
  123. unsigned int TabLength = STD_TABLENGTH;
  124.  
  125. unsigned int MaxPages = MAXPAGES;        /* Maximalzahl Seiten */
  126. unsigned int NPages = 0;                 /* gelesene Seiten */
  127. char   **Pages = NULL;                   /* Seitenzeigerliste */
  128.  
  129. /**************************************************************************/
  130.  
  131. /* f"ur und von getopt() */
  132.  
  133. /* hier mogle ich meine eigene Option -~ ein: HP DJ Initialisierungsstring */
  134.  
  135. char    *goptVektor = "~?b:c:d.e:FghH:i:l:no:p:s:t:uvVx."; /* Optionsliste */
  136. extern union gopt_Arg gopt_Argument;     /* f"ur Optionsargument */
  137.  
  138. /**************************************************************************/
  139.  
  140. /*
  141.  * Ausgabe von Meldungen (Warnungen, Fehlermeldungen, Info) nach stdout
  142.  * und/oder (abh. von WRITE_LOGFILE) LogF
  143.  */
  144. char     MsgString[2048];
  145.  
  146. #define PRINTMSG if(LogProcess)             \
  147.                    fputs( MsgString, LogF); \
  148.                  fputs( MsgString, stderr)
  149.  
  150.  
  151. /**************************************************************************/
  152.  
  153. int      main (int argc, char *argv[])
  154. {
  155.   char     ch;
  156.   int      i;
  157.  
  158.   if (LogProcess)
  159.     /* LOG File "offnen */
  160.     LogF = fopen ("rev.log", "w");
  161.  
  162.   /* bei CTRL-C Dateien sauber raus aus'm Programm */
  163.   if (signal (SIGINT, SIG_IGN) != SIG_IGN)
  164.     signal (SIGINT, CtrlCExit);
  165.  
  166.   PrgName = argv[0];
  167.  
  168.   if (LogProcess)
  169.     {
  170.     /* Argumentstring ins Logfile schreiben */
  171.       for (i = 0; i < argc; i++)
  172.         {
  173.           fprintf (LogF, "%s ", argv[i]);
  174.         }
  175.       fputc ('\n', LogF);
  176.     }
  177.  
  178.   /* Argument"ubergabe an das Programm auslesen */
  179.   for (i = 0, ch = getopt_env (REV_ENV_VAR, *argv);
  180.        i < 2;
  181.        ch = getopt (argc - 1, argv + 1, *argv), i++)
  182.     do
  183.       {
  184.         switch (ch)
  185.             {
  186.               case 'h':
  187.                 ExtHelp = TRUE;
  188.               case '?':
  189.                 usage ();                  /* Hilfe */
  190.                 exit (0);
  191.                 break;
  192.               case 'b':                /* Buffer f"ur Eingabe */
  193.                 BufferSize = atol (gopt_Argument.s);
  194.                 if (BufferSize < MINBUFFERSIZE) /* wir wollen's nicht */
  195.                   BufferSize = MINBUFFERSIZE;   /* "ubertreiben!       */
  196.                 break;
  197.               case 'c':                /* Zeilenl"ange */
  198.                 ColsPLine = atoi (gopt_Argument.s);
  199.                 break;
  200.               case 'd':                /* double sided output */
  201.                 switch (gopt_Argument.c)
  202.                     {
  203.                       case '0':
  204.                         /* nur geradzahlige Seiten vorw"arts ausgeben */
  205.                         Evenforward = TRUE;
  206.                         break;
  207.                       case '1':
  208.                         /* nur ungeradzahlige Seiten r"uckw"arts ausgeben */
  209.                         Oddbackward = TRUE;
  210.                         break;
  211.                       case '2':
  212.                         /* nur geradzahlige Seiten r"uckw"arts ausgeben */
  213.                         Evenbackward = TRUE;
  214.                         break;
  215.                       case '3':
  216.                         /* nur ungeradzahlige Seiten vorw"arts ausgeben */
  217.                         Oddforward = TRUE;
  218.                         break;
  219.                       default:
  220.                         if (LogProcess)
  221.                           fprintf (LogF, "Option -d : %c no permissible argument;"
  222.                                    " only 0, 1, 2 or 3 are allowed.\n",
  223.                                  gopt_Argument.c);
  224.                         fprintf (stderr, "Option -d : %c no permissible argument;"
  225.                                  " only 0, 1, 2 or 3 are allowed.\n",
  226.                                  gopt_Argument.c);
  227.                         exit (-6);
  228.                         break;
  229.                     }                      /* switch */
  230.                 DSided = TRUE;
  231.                 break;
  232.               case 'e':                /* letzte Seite */
  233.                 EndPage = atoi (gopt_Argument.s) - 1;
  234.                 break;
  235.               case 'F':
  236.                 ForwardOutput = TRUE;
  237.                 break;
  238.               case 'g':
  239.                 LogProcess = TRUE;
  240.                 break;
  241.               case 'H':                /* Headings */
  242.                 Heading = gopt_Argument.s;
  243.                 /* Scannen der "Uberschrift wird verschoben, bis bekannt
  244.                  * ist, wieviel Zeilen pro Seite benutzt werden sollen;
  245.                  * dann wird die Anzahl der "Uberschriftszeilen von dieser
  246.                  * Angabe abgezogen. */
  247.                 break;
  248.               case 'i':                /* Datei mit Initialisierungsstring;
  249.                                             * wird vor Ausgabe ausgegeben */
  250.                 InitFile = gopt_Argument.s;
  251.                 break;
  252.               case 'l':                /* Seitenl"ange */
  253.                 LinesPPage = atoi (gopt_Argument.s);
  254.                 break;
  255.               case 'n':
  256.                 NoLastFF = FALSE;
  257.                 break;
  258.               case 'o':                /* Ausgabedatei */
  259.                 OutFile = gopt_Argument.s;
  260.                 break;
  261.               case 'p':                /* max. Seitenzahl */
  262.                 NPages = atoi (gopt_Argument.s);
  263.                 if (MaxPages < MINPAGES)
  264.                   MaxPages = MINPAGES;
  265.                 break;
  266.               case 's':                /* erste Seite */
  267.                 StartPage = atoi (gopt_Argument.s) - 1;
  268.                 if (StartPage < 0)
  269.                   StartPage = 0;
  270.                 break;
  271.               case 't':
  272.                 TabLength = atoi (gopt_Argument.s);
  273.                 break;
  274.               case 'x':                /* \f an Seitenende? */
  275.                 WithFF = gopt_Argument.c == '-' ? FALSE : TRUE;
  276.                 if (gopt_Argument.c != '+' && gopt_Argument.c != '-')
  277.                   sprintf (MsgString, "%s: -f%c not valid; formfeed switched ON.\n",
  278.                            *argv, gopt_Argument.c);
  279.                 PRINTMSG;
  280.                 break;
  281.               case 'u':
  282.                 UndoCtrlChar = TRUE;
  283.                 break;
  284.               case 'v':                /* Plappermaul */
  285.                 Verbose = TRUE;
  286.                 break;
  287.               case 'V':                /* Version */
  288.                 Version = TRUE;
  289.                 break;
  290.               case '~':
  291.                 /* my private cheating for my HP Deskjet, hehe! */
  292.                 PrintInitStr = TRUE;
  293.                 LinesPPage = MY_LINES_PER_PAGE;
  294.                 ColsPLine  = MY_COLS_PER_LINE;
  295.                 break;
  296.               case GOPT_IS_SIMPLE_STRING: /* Eingabedateiname */
  297.                 InFile = gopt_Argument.s;
  298.                 break;
  299.               case GOPT_ERROR:             /* Fehler */
  300.                 usage ();
  301.                 sprintf (MsgString, "Program aborted; wrong option\n");
  302.                 PRINTMSG;
  303.                 exit (5);
  304.                 break;
  305.               default:                     /* H"AH? Ich nix verstehn ch */
  306.                 break;
  307.             }                              /* endswitch */
  308.       }
  309.     while ((ch = getopt (0, NULL, *argv)) != GOPT_FINISHED);
  310.  
  311.     if (ForwardOutput && DSided)
  312.       {
  313.         sprintf (MsgString, "%s: Can't use -F and -d# together. ABORT.\n", PrgName);
  314.         PRINTMSG;
  315.         exit (8);
  316.       }
  317.  
  318. #if WRITETEST
  319.   LogProcess = TRUE;
  320. #endif
  321.  
  322.   /*
  323.    * falls EndPage ein Wert zugewiesen wurde, ist dieser auch gr"o"ser als
  324.    * StartPage? Nein -> Abbruch!
  325.    */
  326.   if (EndPage != -1)
  327.     {
  328.       if (EndPage < StartPage)
  329.         {
  330.           sprintf (MsgString, "%s: end page < start page: ABORT.\n", PrgName);
  331.           PRINTMSG;
  332.           exit (7);
  333.         }
  334.     }
  335.  
  336.   if (InFile)                              /* Eingabedatei wurde angegeben */
  337.     {
  338.       if ((Stream = fopen (InFile, "r")) == NULL)
  339.         {
  340.           sprintf (MsgString, "%s: Can't open `%s' for reading\n",
  341.                    PrgName, InFile);
  342.           PRINTMSG;
  343.           exit (2);
  344.         }
  345.       BufferSize = filelength (InFile) + 1;
  346.     }
  347.   else
  348.     Stream = stdin;
  349.  
  350.   if ((Buffer = (char *)malloc (BufferSize)) == NULL)
  351.     {
  352.       sprintf (MsgString, "%s: Virtual memory exhausted\n", PrgName);
  353.       PRINTMSG;
  354.       exit (3);
  355.     }
  356.   if ((Pages = (char **)malloc (MaxPages + 1)) == NULL)
  357.     {
  358.       sprintf (MsgString, "%s: Virtual memory exhausted\n", PrgName);
  359.       PRINTMSG;
  360.       exit (3);
  361.     }
  362.  
  363.   if (OutFile)                             /* Eingabedatei wurde angegeben */
  364.     {
  365.       /* Datei als Standardausgabe behandeln */
  366.       if (freopen (OutFile, "w", stdout) == NULL)
  367.         {
  368.           sprintf (MsgString, "%s: Can't open `%s' for writing\n",
  369.                    PrgName, OutFile);
  370.           PRINTMSG;
  371.           exit (4);
  372.         }
  373.     }
  374.  
  375.   if (Version && !Verbose)
  376.     fprintf (stderr, "This is Rev version " VER "\n%s", version_string);
  377.  
  378.   sprintf (MsgString, "This is Rev version " VER "\n%s\n"
  379.            "lines/page : %d\t\tcols/line : %d"
  380.            "\ntab length : %d"
  381.            "\n"
  382.            "input : %s\n"
  383.            "output : %s\n"
  384.            "init file : %s\n"
  385.            "write log file : %s\n"
  386.            "start page : %d\t\tend page : %d\n"
  387.            "buffer size : %ld\tmax pages : %d\n"
  388.            "page header : %d lines\n"
  389.            "verbose : %s\t\tUndo CTRL chars : %s\n"
  390.            "extra formfeed : %s\t\\f after last page : %s\n"
  391.            "double sided : %s",
  392.            Version ? version_string : "",
  393.            LinesPPage, ColsPLine,
  394.            TabLength,
  395.            InFile ? InFile : "stdin", OutFile ? OutFile : "stdout",
  396.            InitFile ? InitFile : "--",
  397.            LogProcess ? "YES" : "NO",
  398.            StartPage + 1, EndPage != -1 ? EndPage + 1 : 99999,
  399.            BufferSize, MaxPages,
  400.   /* --> */Heading ? (i=scanPageHeader()) : (i=0),
  401.            Verbose ? "ON" : "OFF", UndoCtrlChar ? "ON" : "OFF",
  402.            NoLastFF ? "NO" : "YES", WithFF ? "ON" : "OFF",
  403.            DSided ? "ON" : "OFF");
  404.   if (LogProcess)
  405.     fputs (MsgString, LogF);
  406.   if (Verbose)
  407.     fputs (MsgString, stderr);
  408.   if (DSided)
  409.     sprintf (MsgString, "\toutput %s sides %s\n\n",
  410.              Evenforward || Evenbackward ? "EVEN" : "ODD",
  411.              Evenforward || Oddforward ? "FORWard" : "BACKWard");
  412.   else
  413.     sprintf (MsgString, "\n\n");
  414.   if (LogProcess)
  415.     fputs (MsgString, LogF);
  416.   if (Verbose)
  417.     fputs (MsgString, stderr);
  418.  
  419.   /* Anzahl Zeilen der "Uberschrift abziehen */
  420.   LinesPPage -= i;
  421.  
  422.   readin ();                               /* Datei bzw. Eingabe einlesen */
  423.   writeout ();                             /* Datei pervertiert ausgeben */
  424.  
  425.   finish ();
  426. }
  427.  
  428. /**************************************************************************/
  429.  
  430. static void CtrlCExit ()
  431. {
  432.   fprintf (stderr, "\n\nAaaeerghhhrrrlllchh... KILLED!\n");
  433.   if (LogProcess)
  434.     fprintf (LogF, "\nProcess has been killed!\n");
  435.   finish ();
  436. }
  437.  
  438. static void finish ()
  439. {
  440.   if (Buffer)
  441.     free (Buffer);
  442.   if (Stream != stdin)
  443.     fclose (Stream);
  444.   fclose (stdout);
  445.  
  446.   if (LogProcess)
  447.     /* LOG File schlie"sen */
  448.     fclose (LogF);
  449.  
  450.   exit (0);
  451. }
  452.  
  453. /**************************************************************************
  454.  *
  455.  * Datei oder aus Standardeingabe einlesen und bereits jetzt in Seiten
  456.  * gliedern.
  457.  */
  458. static int readin ()
  459. {
  460.   int      col;                          /* Zeichenz"ahler f"ur orig. Zeile */
  461.   int      pos;                          /* Zeichenz"ahler f"ur neue Zeile */
  462.   unsigned int lines = 0;                /* Zeilenz"ahler pro Seite */
  463.   unsigned int alllines = 0;             /* Zeilen-Gesamtzahl */
  464.   unsigned long int restbufsize = BufferSize;
  465.   char    *buf = Buffer;
  466.   Bool     NEWLINE;
  467.   Bool     NEWPAGE;
  468.  
  469.   Pages[NPages++] = buf;                   /* erste Seite setzen */
  470.  
  471.   if (Verbose && InFile)
  472.     fprintf (stderr, "Loading %s ...  ", InFile);
  473.  
  474.   while (!feof (Stream))
  475.     {
  476.       /* Zeile (i.allg. mit \n\0 abschlie"send) einlesen */
  477.       if (fgets (buf, restbufsize, Stream) == NULL)
  478.         break;
  479.  
  480.       /*
  481.        * falls nichts mehr eingelesen wurde, weil Datei nicht mit Ctrl-Z
  482.        * abgeschlossen wurde, raus. Ist dies der Fall, denkt feof() nicht
  483.        * daran, 0 zur"uckzugeben ...
  484.        */
  485.       if (!*buf)
  486.         break;
  487.  
  488.       if (!(alllines % PROCESS_INDICATOR_CHANGE)
  489.           && Verbose && InFile)
  490.         show_process_indicator ();
  491.  
  492.       col = pos = 0;
  493.       while (buf[col] && restbufsize > 0)
  494.         {
  495.           NEWLINE = NEWPAGE = FALSE;
  496.           --restbufsize;                   /* BufferSize schrumpft mit */
  497.  
  498. #if READTEST
  499.           if (buf[col] != '\n')
  500.             fprintf (stderr, "%c", buf[col]);
  501. #endif
  502.  
  503.           switch (buf[col])
  504.               {
  505.                 case '\t':                 /* Tabulator */
  506.                   pos += calcTab (pos) + 1;
  507.                   col++;
  508.                   break;
  509.                 case '\f':
  510.                   if (buf[-1] == '\n')     /* \n vor \f l"oschen */
  511.                     {
  512.                       strcpy (buf + col - 1, buf + col);
  513.                       /* ggf. vorherigen Seitenwechsel vorschieben */
  514.                       if (Pages[NPages - 1] == buf + col - 1)
  515.                         Pages[NPages - 1] = buf + col - 2;
  516.                     }
  517.                   else
  518.                     col++;
  519.                   NEWLINE = TRUE;
  520.                   NEWPAGE = TRUE;
  521.                   break;
  522.                 case '\n':
  523.                   col++;
  524.                   NEWLINE = TRUE;
  525.                   break;
  526.                 case '\b':
  527.                   col++, --pos;
  528.                   break;
  529.                 default:
  530.                   col++, pos++;
  531.                   break;
  532.               }                            /* switch */
  533.           if (pos >= ColsPLine)
  534.             NEWLINE = TRUE;
  535.           if (NEWLINE)
  536.             {
  537.  
  538. #if READTEST
  539.               fprintf (stderr, "\n");
  540. #endif
  541.  
  542.               lines++, alllines++;
  543.               if (buf[col - 1] != '\f' && buf[col] == '\n') /* "uberfl"ussiges \n
  544.                                                              * l"oschen */
  545.                 buf[col] = '\0';           /* (tritt nicht bei \n auf!) */
  546.               if (lines >= LinesPPage)
  547.                 NEWPAGE = TRUE;
  548.               pos = 0;
  549.             }
  550.           if (NEWPAGE)
  551.             {
  552.  
  553. #if READTEST
  554.               fprintf (stderr, "\n");
  555. #endif
  556.  
  557.               lines = 0;
  558.               Pages[NPages++] = buf + col;
  559.               if (NPages == MaxPages - 1)
  560.                 {
  561.                   sprintf (MsgString, "%s: Sorry - page number exceeded; use "
  562.                            "-p with a higher value!", PrgName);
  563.                   PRINTMSG;
  564.                   exit (5);
  565.                 }
  566.             }
  567.         }                                  /* while buf[col] */
  568.       buf += col;
  569.     }                                      /* while !feof */
  570.  
  571.   Pages[NPages] = NULL;                    /* Seitenliste mit NULL abschlie"sen */
  572.  
  573.   sprintf (MsgString,
  574.            "\r%d lines on %d page%s read\n",
  575.            alllines, NPages, NPages < 2 ? "" : "s");
  576.  
  577.   if (LogProcess)
  578.     fputs (MsgString, LogF);
  579.  
  580.   if (Verbose)
  581.     {
  582.       fputs (MsgString, stderr);
  583.       if (DSided && NPages % 2)
  584.         fprintf (stderr,
  585.         "\n* WARNING *: Odd number of pages; before printing with option d1\n"
  586.                  "after printing with option d0 you should put an extra sheet of paper\n"
  587.                  "on top of your output pile!\n\n");
  588.     }
  589.  
  590.   if (Stream != stdin)
  591.     fclose (Stream);
  592.  
  593.   if (LogProcess)
  594.     fprintf (LogF, "restbufsize = %d\n\n", restbufsize);
  595.  
  596.   return 0;
  597. }
  598.  
  599. /***************************************************************************/
  600.  
  601. #define NEXTODD(x)  ((x)%2 ? (x)+1 : (x))/* gerade sind ungerade ... */
  602. #define NEXTEVEN(x) ((x)%2 ? (x) : (x)+1)/* und umgekehrt (0,1,2...) */
  603. #define PREVODD(x)  ((x)%2 ? (x)-1 : (x))
  604. #define PREVEVEN(x) ((x)%2 ? (x) : (x)-1)
  605.  
  606. /***************************************************************************/
  607.  
  608. static int writeout ()
  609. {
  610.   int      page;
  611.   int      startpage;
  612.  
  613.   /*
  614.    * VORSICHT: ich gebe zu, da"s die Namensgebung bescheuert gew"ahlt ist, habe
  615.    * aber keinen Bock, daran etwas zu "andern...: startpage gibt an, mit welcher
  616.    * Seite die Ausgabe starten soll, StartPage weist allerdings auf die erste
  617.    * physikalische Seite (EndPage auf die letzte phys. Seite). W"ahrend also
  618.    * Startpage immer < EndPage sein sollte, ist startpage bei der
  619.    * R"uckw"artsausgabe immer >= Endpage-1.
  620.    */
  621.  
  622.   if (PrintInitStr)
  623.     /* Drucker-Initialisierungsstring schreiben */
  624.     fputs (PRTINITSTRING, stdout);
  625.  
  626.   /* ist eine Datei mit Initialisierungsstring angegeben? */
  627.   if (InitFile)
  628.     {
  629.       FILE    *f;
  630.       char     buf[512];
  631.  
  632.       /* Initialisierungsdatei "offnen und lesen */
  633.       if ((f = fopen (InitFile, "r")) != NULL)
  634.         {
  635.           while (!feof (f))
  636.             {
  637.               if (fgets (buf, 512, f) == NULL)
  638.                 break;
  639.               fputs (buf, stdout);
  640.             }                              /* while */
  641.         }
  642.       fclose (f);
  643.     }
  644.  
  645.   if (EndPage == -1 || EndPage >= NPages)
  646.     EndPage = NPages - 1;
  647.  
  648.   if (DSided)                              /* doppelseitiger Druck */
  649.     /* erste auszugebende Seite bestimmen (startpage) */
  650.     {
  651.       if (Evenforward)
  652.         startpage = StartPage ? NEXTEVEN (StartPage) : 1;
  653.       else if (Evenbackward)
  654.         /* NPages zeigt hinter letzte Seite (*0) */
  655.         startpage = EndPage ? PREVEVEN (EndPage) :
  656.             (NPages % 2 ? NPages - 2 : NPages - 1);
  657.       else if (Oddforward)
  658.         startpage = StartPage ? NEXTODD (StartPage) : 0;
  659.       else                                 /* Oddbackward */
  660.         startpage = EndPage ? PREVODD (EndPage) :
  661.             (NPages % 2 ? NPages - 1 : NPages - 2);
  662.  
  663.       if (Evenforward || Oddforward)
  664.         for (page = startpage; page <= EndPage; page += 2)
  665.           writepage (page);
  666.       else
  667.         for (page = startpage; page >= StartPage; page -= 2)
  668.           writepage (page);
  669.  
  670.       if (LogProcess)
  671.         fprintf (LogF, "\noutput page %d to page %d\n",
  672.                  startpage + 1, Evenforward || Oddforward ? EndPage + 1 : StartPage + 1);
  673.     }
  674.   else
  675.     /* kein doppelseitiger Druck */
  676.     {
  677.       if (ForwardOutput)
  678.         for (page = StartPage; page <= EndPage; page++)
  679.           writepage (page);
  680.       else
  681.         /* alle Seiten r"uckw"arts ausgeben */
  682.         for (page = EndPage; page >= StartPage; --page)
  683.           writepage (page);
  684.  
  685.       if (LogProcess)
  686.         fprintf (LogF, "\noutput page %d to page %d\n", EndPage + 1, StartPage + 1);
  687.  
  688.       if (Verbose)
  689.         fputc ('\n', stderr);
  690.     }
  691.  
  692.   /* falls \f nach letzter gedruckter Seite erw"unscht */
  693.   if (!NoLastFF)
  694.     fputc ('\f', stdout);
  695.  
  696.   return 0;
  697. }
  698.  
  699. /***************************************************************************/
  700.  
  701. static int writepage (int page)
  702. {
  703.   char     ch = '\0';
  704.   char    *buf;
  705.   static int calls = 1;
  706.  
  707.   /*
  708.    * ist eine Seite bereits mit \f beended worden (Zeichen in Datei), ist kein
  709.    * weiterer Seitenvorschub notwendig
  710.    */
  711.   static Bool DontWriteExtraFF = FALSE;
  712.  
  713.   /*
  714.    * vor erster auszugebender Seite ist ebenfalls kein Seitenvorschub n"otig,
  715.    * nicht mal erw"unscht.
  716.    */
  717.   static Bool FirstPage = TRUE;
  718.  
  719.   if (Pages[page])                         /* gibt's die Seite? */
  720.     {
  721.  
  722.       if (LogProcess)
  723.         fputc ('[', LogF);
  724.  
  725.       if (Verbose)
  726.         fputc ('[', stderr);
  727.  
  728.       if (WithFF && !FirstPage && !DontWriteExtraFF)
  729.         fputc ('\f', stdout);
  730.       FirstPage = FALSE;                   /* ab jetzt kannste ruhig */
  731.  
  732.       sprintf (MsgString, "%d", page + 1);
  733.       if (Verbose)
  734.         fputs (MsgString, stderr);
  735.  
  736.       if (LogProcess)
  737.         fputs (MsgString, LogF);
  738.  
  739.       if (Heading)
  740.         fprintf (stdout, HeadString, page+1);
  741.  
  742.       if (Pages[page + 1])
  743.         ch = *Pages[page + 1], *Pages[page + 1] = '\0'; /* n"achste Seite soll ja
  744.                                                          * nicht ausge- geben
  745.                                                          * werden */
  746.       for (buf = Pages[page]; *buf; buf += writeline (buf))
  747.         fputc ('\n', stdout);
  748.  
  749.       /* \f falls nicht sowieso schon \f geschrieben */
  750.       if (Pages[page + 1] && *(Pages[page + 1] - 1) == '\f')
  751.         DontWriteExtraFF = TRUE;
  752.       else
  753.         DontWriteExtraFF = FALSE;
  754.  
  755.       if (LogProcess)
  756.         fputc (']', LogF);
  757.  
  758.       if (Verbose)
  759.         fputc (']', stderr);
  760.  
  761.       if (!(calls++ % 13))
  762.         {
  763.           if (LogProcess)
  764.             fputc ('\n', LogF);
  765.           if (Verbose)
  766.             fputc ('\n', stderr);
  767.         }
  768.  
  769.       if (ch)
  770.         *Pages[page + 1] = ch;
  771.     }
  772.  
  773.   return 0;
  774. }
  775.  
  776. /**************************************************************************
  777.  * Schreibe Zeile; liefert Anzahl geschriebener Zeichen zur"uck
  778.  */
  779. static int writeline (char *buf)
  780. {
  781.   char     line[ColsPLine + 2];          /* nicht ANSI C konform! */
  782.   int      i;
  783.   int      col = 0;                      /* Eingabe-Index */
  784.   int      pos = 0;                      /* Ausgabe-Index */
  785.   Bool     EOL = FALSE;
  786.  
  787.   /* Zeichenweise Zeile ausgeben */
  788.   if (buf)
  789.     {
  790.       for (; *buf && !EOL && pos < ColsPLine; buf++)
  791.         {
  792.           switch (*buf)
  793.               {
  794.                 case '\t':
  795.                   for (i = calcTab (pos); i >= 0; --i, pos++)
  796.                     line[pos] = ' ';
  797.                   col++;
  798.                   break;
  799.                 case '\n':                 /* beachten, aber nicht "ubernehmen */
  800.                   EOL = TRUE;
  801.                   break;
  802.                 default:
  803.                   if (iscntrl (*buf))
  804.                     {
  805.                       line[pos++] = UndoCtrlChar ? '.' : *buf;
  806.                       col++;
  807.                     }
  808.                   else
  809.                 case '\f':
  810.                 case '\b':
  811.                     line[pos++] = *buf;    /* besser als Chaos drucken */
  812.                   col++;
  813.               }
  814.         }
  815.       line[pos] = '\0';
  816.       fputs (line, stdout);
  817.  
  818. #if WRITETEST
  819.       if (LogProcess)
  820.         fputs (line, LogF);
  821.       fputs ("\n", LogF);
  822. #endif
  823.     }
  824.   return EOL ? col + 1 : col;
  825. }
  826.  
  827. /**************************************************************************
  828.  * Falls Seiten"uberschrift "ubergeben wurde, scannen und Anzahl der
  829.  * "Uberschriftzeilen zur"uckgeben.
  830.  * ^ wird als \n betrachtet, $DATE durch aktuelles Datum ersetzt und
  831.  * $FILE durch den mit -o "ubergebenen Dateinamen (oder `stdout'),
  832.  * $LINEdc gibt eine Linie aus d Zeichen c an;
  833.  * die zu ersetzenden Strings sind in HeadVars angegeben.
  834.  * Heading enth"alt die Originalangabe, die nach HeaderString kopiert
  835.  * bzw. umgewandelt wird.
  836.  */
  837. static int scanPageHeader()
  838. {
  839.   int nchars = 511;                 /* remaining room for headline */
  840.   int hlines = 0;                   /* line counter of headline */
  841.   int i, j;
  842.   char *cp = Heading;               /* -> original user string */
  843.   char *hsp = HeadString;           /* -> resulting headline */
  844.   time_t t;
  845.  
  846.   /* Zeichen f"ur Zeichen Heading scannen */
  847.   for ( ; *cp && nchars; cp++, hsp++, --nchars)
  848.     {
  849.       switch (*cp)
  850.         {
  851.         case '^':                   /* NEWLINE */
  852.           *hsp = '\n';
  853.           hlines++;
  854.           break;
  855.         case '$':
  856.           /* m"ogliche Formatanweisungen scannen */
  857.           for (i = 0; i < sizeof(HeadVars)/sizeof(char *); i++)
  858.             if (strstr(cp+1, HeadVars[i]) == cp+1)
  859.               break;
  860.           switch (i)
  861.             {
  862.             case HEAD_FILE:
  863.               strcpy( hsp, InFile ? InFile : "stdin");
  864.               hsp = strrchr(hsp, '\0')-1;
  865.               cp += strlen(HeadVars[HEAD_FILE]);
  866.               nchars -= strlen(InFile?InFile:"stdin")-1;
  867.               break;
  868.             case HEAD_DATE:
  869.               time( &t);
  870.               hsp += (i = strftime(hsp, nchars, "%a %x", localtime(&t)) )-1;
  871.               cp += strlen(HeadVars[HEAD_DATE]);
  872.               nchars -= i-1;
  873.               break;
  874.             case HEAD_LINE:
  875.               cp += strlen(HeadVars[HEAD_LINE])+1;
  876.               j = (j=atoi(cp)) ? j : ColsPLine;
  877.               while (isdigit(*cp))    /* hinter Zahl gehen */
  878.                 cp++;
  879.               for (i=0; i<j; i++)
  880.                 *hsp++=*cp, --nchars;
  881.               --hsp, nchars++;
  882.               break;
  883.             case HEAD_PAGE:
  884.               PageNumbering = TRUE;
  885.               cp += strlen(HeadVars[HEAD_PAGE]);
  886.               strcpy( hsp, "%d");
  887.               hsp++, --nchars;
  888.               break;
  889.             default :
  890.               *hsp = '$';
  891.               break;
  892.             }
  893.           break;
  894.         default :
  895.           *hsp = *cp;
  896.           break;
  897.         } /* switch */
  898.     } /* while */
  899.  
  900.     *hsp = '\0';                     /* HeadString abschliessen */
  901.  
  902.     return hlines+1;
  903. }
  904.  
  905. /**************************************************************************
  906.  * Tabulator in Spalte actcol in Anzahl von Leerzeichen umrechnen;
  907.  * bis zum n"achsten Tabstop und maximal bis zum Zeilenende.
  908.  * col wird ab 0 gez"ahlt.
  909.  * Es wird die Anzahl-1 der zu setzenden Leerzeichen zur"uckgegeben.
  910.  */
  911. static int calcTab (int col)
  912. {
  913.   int      nspaces = TabLength - col % TabLength - 1;
  914.  
  915.   return col + nspaces >= ColsPLine ? ColsPLine - col - 1 : nspaces;
  916. }
  917.  
  918. /**************************************************************************
  919.  * Die kleine nette Spielerei mit dem laufenden Rad
  920.  */
  921. void     show_process_indicator (void)
  922. {
  923.   static int status = 0;
  924.   char     part[] = "|/-\\";
  925.  
  926.   status %= sizeof (part);
  927.   fprintf (stderr, "\b%c", part[status++]);
  928. }
  929.  
  930. /**************************************************************************/
  931.  
  932. static int filelength (char *filename)
  933. {
  934.   struct stat infstat;
  935.  
  936.   stat (filename, &infstat);
  937.   return infstat.st_size;
  938. }
  939.  
  940. /**************************************************************************/
  941.  
  942. static void usage ()
  943. {
  944.  
  945.   /*
  946.    * Hilfetext nach stdout, nicht nach stderr, ausgeben, so da"s man sich den
  947.    * Text auch seitenweise beispielsweise mit more anzeigen l"a"st.
  948.    */
  949.   fprintf (stdout, "%s\n"
  950.         "Reverts ASCII text for printing (last page first, first page last)\n"
  951.       " with option for double sided printing (on HP Deskjet for example)\n\n"
  952.            "USAGE: %s [fname] [-?] [-h] [-v] [-F] [-x{-|+}] [-n]\n"
  953.            "\t [-H page_heading] [-o outfile] [-d{0|1|2|3}] [-l lines]\n"
  954.            "\t [-c cols] [-s start_page] [-e end_page] [-i init_file]\n"
  955.            "\t [-V] [-u] [-t tab_length] [-b buffer_size] [-p pages]\n"
  956.            "\t-?\tthis help\n"
  957.            "\t-h\textended help\n"
  958.            "\t-c\tcolumns per line (%d)\n"
  959.            "\t-l\tlines per page (%d)\n"
  960.            "\t-o\toutput file name (stdout)\n"
  961.            "\t-v\tverbose\n"
  962.            "\tfname\tinput file\n",
  963.            version_string, PrgName,
  964.            STD_LINES_PER_PAGE, STD_COLS_PER_LINE);
  965.   if (ExtHelp)
  966.     fprintf (stdout,
  967.              "\t-b\tsize of stdin read in buffer (%ld)\n"
  968.              "\t-d\toutput for double sided printing\n"
  969.              "\t  0\t print even sides forward\n"
  970.              "\t  1\t print odd sides backward\n"
  971.              "\t  2\t print even sides backward\n"
  972.              "\t  3\t print odd sides forward\n"
  973.              "\t-e\tlast (end) page (end of document)\n"
  974.              "\t-F\tforward output (OFF)\n"
  975.              "\t-g\twrite log file\n"
  976.              "\t-H\theader on pages (max. 512 chars)\n"
  977.              "\t  `^' NEWLINE, `$DATE' date of output,\n"
  978.              "\t  `$FILE' filename of -o option, `$PAGE' page numbering\n"
  979.              "\t  `$LINEdc' d chars of c (for example: $LINE10-)\n"
  980.              "\t-i\tprint text from init_file before output\n"
  981.              "\t-n\tno \\f after last printed page (%s)\n"
  982.              "\t-p\tmax number of pages (%d)\n"
  983.              "\t-s\tfirst (start) page (1)\n"
  984.              "\t-t\ttab length (%d)\n"
  985.              "\t-u\tundo Ctrl characters (convert to `.')\n"
  986.              "\t-V\tprint version\n"
  987.              "\t-x\textra formfeed after page end (%s)\n"
  988.              "\t  -\t OFF\n"
  989.              "\t  +\t ON\n",
  990.              BUFFERSIZE,
  991.              NoLastFF ? "YES" : "NO",
  992.              MAXPAGES,
  993.              STD_TABLENGTH,
  994.              WithFF ? "ON" : "OFF");
  995. }
  996.  
  997. /**************************************************************************/
  998.  
  999.