/*---------------------------------------------------------* | Author: Maurizio Loreti, aka MLO or I3NOO. | | Address: University of Padova - Department of Physics | | Via F. Marzolo, 8 - 35131 PADOVA - Italy | | Phone: (39)(49) 844-313 FAX: (39)(49) 844-245 | | E-Mail: LORETI at IPDINFN (BITNET); or VAXFPD::LORETI | | (DECnet) - VAXFPD is node 38.257 i.e. 39169; or | | LORETI@PADOVA.INFN.IT (INTERNET). | | Home: Via G. Donizetti 6 - 35010 CADONEGHE (PD) - Italy | *---------------------------------------------------------*/ #include /* Standard library */ #include #include #include #include #include /* Amiga specific */ #include #include #include #include #include #include #include #include #if defined(ASL) | defined(BOTH) #include #endif #if defined(REQ) | defined(BOTH) #include #endif #include "mlo.h" /* Program specific */ #include "pf2.h" #include "ext.h" static Boolean CheckBreak(void); /* Local functions */ static void CheckDefaults(void); static void ClearPageBuffer(void); static void ClearThisPage(void); static void Detab(char *buffer, int length); static void DoubleLine(char *left, char *right); static void EjectPage(void); static void Header(int type); static void InterLine(void); static void OutLine(void); static void ResetPrinter(void); static void SendBuffer(char *buffer); static void SendToPrinter(char *fmt, ...); static void Syntax(void); /*-----------------------------------------* | Routines (sorted in alphabetical order) | *-----------------------------------------*/ static Boolean CheckBreak(void) { /** | Check for CTRL-C **/ if (SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { return True; } return False; } static void CheckDefaults(void) { /** | As the name says, this procedure checks for incompatible options | selected: e.g. the only Landscape fonts are Courier, so to give | the command PF2 -TL ... is an error. I suppose that the internal | fonts only are present; otherwise this procedure should be modified. | Checks: | -> Letter-Gothic: 12 or 24 cpi; portrait only. | -> Times: proportional; portrait only. | -> Courier: 10 or 20 cpi; plus 16.67 cpi for Courier Roman (not | allowed for Courier Italic). | -> Landscape/Italic not allowed. | -> The number of extra spaces inserted before every line must be | not negative. **/ switch (Font) { case GOTHIC: Orientation = PORTRAIT; if (Pitch == P10CPI) { Pitch = P12CPI; } else { Pitch = P24CPI; } break; case TIMES: Orientation = PORTRAIT; Pitch = PROPORTIONAL; break; case COURIER: if (Pitch == P16_67CPI && Style == ITALIC) { Pitch = P20CPI; } break; } if (Orientation == LANDSCAPE) { Style = ROMAN; } if (nBlanks < 0) { nBlanks = 0; } else { if (nBlanks) { Buffer = inBuffer + nBlanks; memset(inBuffer, BLANK, nBlanks); } } } void Cleanup( int code ){ /** | Releases all system resources (closes opened files | and frees heap memory - if any). "code" is the | status to be returned to the operating system. **/ windowOff(); if (fp != NULL) fclose(fp); if (pPB != NULL) { if (pPB->line[0] != NULL) free(pPB->line[0]); free(pPB); } if (PrinterOpened) CloseDevice((struct IORequest *) IOrequest); if (IOrequest != NULL) DeleteExtIO((struct IORequest *) IOrequest); if (printPort != NULL) DeletePort((struct MsgPort *) printPort); if (!FromCLI) { int i; fprintf(stdout, "\nStrike to continue ... "); while ( (i = getchar()) != '\n' && i != EOF) { } } #if defined(ASL) | defined(BOTH) if (pFR != NULL) FreeAslRequest(pFR); if (AslBase != NULL) CloseLibrary(AslBase); #endif #if defined(REQ) | defined(BOTH) if (ReqBase != NULL) { PurgeFiles(&fr); CloseLibrary((struct Library *) ReqBase); } #endif if (Topaz8 != NULL) CloseFont(Topaz8); if (GfxBase != NULL) CloseLibrary((struct Library *) GfxBase); if (IntuitionBase != NULL) CloseLibrary((struct Library *) IntuitionBase); exit(code); } static void ClearPageBuffer(void) { /** | This routine resets the content of the page buffer | used in the 2-pages-on-a-sheet mode to all-blank | lines, and the pointer to the next line to be filled | to the first line in the buffer. **/ memset(pPB->line[0], BLANK, BUFFER_SIZE); ThisLine = 0; } static void ClearThisPage(void) { /** | Called every end of file: in the normal mode, ejects a page; | in the 2-pages mode, switches from the left to the right page | (if there is at least a line on this page), and leaving the | remainder of the lines on the current page all blank. **/ switch (PageMode) { case SINGLE_PAGE: EjectPage(); break; case LEFT_PAGE: if (ThisLine) { Header(UP); PageMode = RIGHT_PAGE; ThisLine = 0; } break; case RIGHT_PAGE: if (ThisLine) { FlushBuffers(); PageMode = LEFT_PAGE; ClearPageBuffer(); } break; } } static void Detab( char *buffer, int length ){ /** | Translates TAB stops to blanks: TAB stops are assumed at columns | (1 + N * nTabs), with N = 1, 2, ... ; the default value of nTabs | is 8, and can be changed using the command switches. In the same | time non-printable characters are deleted from the input string: | for non-printable I mean characters from 00 to 037 and 0177, assuming | that characters over 0177 have special meaning different for every | computer and that nothing can be said about them. | The buffer can hold "length" characters only. **/ char temp[LINE_LENGTH]; /* Internal buffer */ char *pC1, *pC2; /* Temporary pointers */ char *pEnd; /* Pointer to the buffer end */ strcpy(temp, buffer); pEnd = (buffer + length - 1); for (pC1 = temp, pC2 = buffer; *pC1 && pC2 < pEnd; pC1++) { if (*pC1 == TAB) { do { *pC2++ = BLANK; } while (((pC2 - buffer) % nTabs) && (pC2 < pEnd)); } else { if (isspace(*pC1) || !iscntrl(*pC1)) { *pC2++ = *pC1; } } } *pC2 = NIHIL; } void DoOutput( char *FileName ){ /** | Sends to the printer the given file. **/ if ((fp = fopen(FileName, "r")) == NULL) { printf("Can't open input file %s ...", FileName); return; } printf("Printing file %s ... ", FileName); while (fgets(Buffer, bufferLength, fp) != NULL) { if (CheckBreak()) { printf("*** PF2: BREAK ***\n"); EjectPage(); ExitProgram(); } Detab(Buffer, bufferLength); OutLine(); } printf("done.\n"); fclose(fp); fp = NULL; ClearThisPage(); } static void DoubleLine( char *left, char *right ){ /** | Prints a line in the 2-pages mode, the two parameters being | pointers to the left-page line and the right-page line; a | blank line is printed if the corresponding pointer is NULL. **/ if (left == NULL) { SendToPrinter("%c%*c%c%*c", V_LINE, TOTAL_LENGTH, BLANK, V_LINE, SEP_LENGTH, BLANK); } else { SendToPrinter("%c%*c%.*s%*c%c%*c", V_LINE, SIDE_LENGTH, BLANK, OUTPUT_LENGTH, left, SIDE_LENGTH, BLANK, V_LINE, SEP_LENGTH, BLANK); } if (right == NULL) { SendToPrinter("%c%*c%c\r", V_LINE, TOTAL_LENGTH, BLANK, V_LINE); } else { SendToPrinter("%c%*c%.*s%*c%c\r", V_LINE, SIDE_LENGTH, BLANK, OUTPUT_LENGTH, right, SIDE_LENGTH, BLANK, V_LINE); } } static void EjectPage(void) { SendToPrinter("%c", FORM_FEED); } void ExitProgram(void) { ResetPrinter(); printf("Good Bye!\n"); Cleanup(SYS_NORMAL_CODE); } void FlushBuffers(void) { /** | In the 2-pages mode, prints the current page as | it is (non filled lines will be left blank). **/ int i; switch (PageMode) { case LEFT_PAGE: if (!ThisLine) return; for (i=0; iline[i], NULL); InterLine(); } for (i=ThisLine; iline[i], NULL); InterLine(); } Header(DOWN); break; } } static void Header( int type ){ /** | Prints the top or the down header in the 2-pages mode: | a solid line separated from the text by a blank line. **/ memset(Buffer, H_LINE, TOTAL_LENGTH); if (type == UP) { SendToPrinter("%c%.*s%c%*c%c%.*s%c\r", NW, TOTAL_LENGTH, Buffer, NE, SEP_LENGTH, BLANK, NW, TOTAL_LENGTH, Buffer, NE); InterLine(); } DoubleLine(NULL, NULL); InterLine(); if (type == DOWN) { SendToPrinter("%c%.*s%c%*c%c%.*s%c\r", SW, TOTAL_LENGTH, Buffer, SE, SEP_LENGTH, BLANK, SW, TOTAL_LENGTH, Buffer, SE); EjectPage(); } } void InitPrinter(void) { char *PitchID[] = {"10", "12", "16.67", "20", "24"}; UBYTE status[2] = {0, 0}; int dummy; /** | Connect properly to printer device **/ printPort = (PrintIO *) CreatePort(PORT_NAME, 0); IOrequest = (PrintIO *) CreateExtIO((struct MsgPort *) printPort, sizeof(PrintIO)); if (!(PrinterOpened = (OpenDevice("printer.device", 0, (struct IORequest *) IOrequest, 0) == 0))) { Cleanup(SYS_ABORT_CODE); } /** | Check if the printer is there... Actually, if the printer is connected | to the serial port, I don't know what to do: in this case I continue, | knowing that a System requester will come after a while. If the printer | is connected to the parallel port, printer selected (bit 0), paper out | (bit 1) and printer offline (bit 2) are checked. **/ FOREVER { IOrequest->ios.io_Command = PRD_QUERY; IOrequest->ios.io_Data = (APTR) status; IOrequest->ios.io_Flags = 0; if (DoIO((struct IORequest *) IOrequest)) { Cleanup(SYS_ABORT_CODE); } if (IOrequest->ios.io_Actual == 2) break; if ((status[0] & 0x7) == 0x4) break; puts("Please, check if your printer is ready!"); printf("When done, enter ... "); while (((dummy = getc(stdin)) != NEWLINE) && (dummy != EOF) ) {} puts(""); } /** | Printer initialisation: see the HP DeskJet 500 | user's manual for explanation... **/ SendToPrinter("%c&l%do%dD%c(10U%c(s0u", ESC, Orientation, Lpi, ESC, ESC); if (Pitch == PROPORTIONAL) { SendToPrinter("1p"); } else { SendToPrinter("0p%sh", PitchID[Pitch]); } SendToPrinter("%dv%ds0b%dt%dQ", Height, Style, Font, Quality); } static void InterLine(void) { /** | Writing at 8 lines per inch and 6 points high characters, | the double page border is not continuous. This procedure | skips half line, draws the border, then skips another half | line down to the correct placement for next printing. **/ SendToPrinter("%c=", ESC); DoubleLine(NULL, NULL); SendToPrinter("%c=", ESC); } static void OutLine(void) { /** | Outputs a line to the printer. | In the normal mode, the line is sent to the port after storing a | carriage return and a line feed after the text, and leaving to the | printer to deal with form feeds and long lines. | In the 2-pages mode, the line is truncated to a fixed length: if | we are in the left page, the line is stored in the internal buffer; | otherwise, it is printed together with the corresponding left line. **/ int length, n; static char *EndOfLine = "\r\n"; length = strlen(inBuffer) - 1; switch (PageMode) { case SINGLE_PAGE: memcpy(inBuffer+length, EndOfLine, 3); SendBuffer(inBuffer); break; case LEFT_PAGE: if (length > OUTPUT_LENGTH) { length = OUTPUT_LENGTH; } memcpy(pPB->line[ThisLine], Buffer, length); if (++ThisLine >= PAGE_LENGTH) { Header(UP); PageMode = RIGHT_PAGE; ThisLine = 0; } break; case RIGHT_PAGE: if ((n = OUTPUT_LENGTH - length) > 0) { memset(Buffer+length, BLANK, n); } DoubleLine(pPB->line[ThisLine], Buffer); InterLine(); if (++ThisLine >= PAGE_LENGTH) { Header(DOWN); PageMode = LEFT_PAGE; ClearPageBuffer(); } break; } } static void ResetPrinter(void) { /** | Resets the printer to the default. **/ SendToPrinter("%cE", ESC); } static void SendBuffer( char *buffer ){ IOrequest->ios.io_Command = PRD_RAWWRITE; IOrequest->ios.io_Data = (APTR) buffer; IOrequest->ios.io_Length = strlen(buffer); IOrequest->ios.io_Flags = 0; if (DoIO((struct IORequest *) IOrequest)) { Cleanup(SYS_ABORT_CODE); } } static void SendToPrinter( char *fmt, ... ){ static char slate[LINE_LENGTH]; va_list vl; va_start(vl, fmt); vsprintf(slate, fmt, vl); va_end(vl); SendBuffer(slate); } void SetSpecialMode(void) { /** | Sets the internal constants for the "two pages on a sheet" | special mode. **/ int i; if ((pPB = malloc(sizeof(PageBuffer))) == NULL) { fprintf(stderr, "Can't allocate the Page Buffer ..."); Cleanup(SYS_ABORT_CODE); } if ((pPB->line[0] = malloc(BUFFER_SIZE)) == NULL) { fprintf(stderr, "Can't allocate the Line Buffer ..."); Cleanup(SYS_ABORT_CODE); } for (i=1; iline[i] = pPB->line[0] + (i * OUTPUT_LENGTH); } Orientation = LANDSCAPE; Font = COURIER; Style = ROMAN; Pitch = P16_67CPI; Height = 6; Lpi = 8; nBlanks = 0; ClearPageBuffer(); } char **Setup( int *pArgc, char **argv ){ /** | We look into the command line for switches; the function value is | argv when pointing to the first non-switch argument (i.e. the first | file name) - if any. If the 2-page mode was requested, the procedure | allocates from the heap memory the internal buffer for the left-page | lines, and a service buffer filled with the pointers to the first | character of every line in the left page; this calling malloc(), | to make this program more portable. **/ int i; char c; /** | Error if called from the Workbench, or if called | from the CLI but without any argument. **/ if (*pArgc < 2) Syntax(); while (--(*pArgc)) { if ((*++argv)[0] == '-') { /** | A switch ... **/ for (i=1; (c = (*argv)[i]); i++) { switch (c) { case 'l': case 'L': Orientation = LANDSCAPE; break; case 'i': case 'I': Style = ITALIC; break; case 'b': case 'B': nBlanks = atoi(*argv + ++i); goto NextSwitch; case 'a': case 'A': nTabs = atoi(*argv + ++i); goto NextSwitch; case 'g': case 'G': Font = GOTHIC; break; case 't': case 'T': Font = TIMES; break; case 's': case 'S': Pitch = P16_67CPI; break; case 'x': case 'X': Pitch = P20CPI; break; case '6': Height = 6; break; case '8': Lpi = 8; break; case 'd': case 'D': Quality = DRAFT_Q; break; case '2': PageMode = LEFT_PAGE; break; default: Syntax(); } } } else { /** | The first file name; perform some | intialisations, then return to the caller. **/ if (PageMode != SINGLE_PAGE) { SetSpecialMode(); } else { CheckDefaults(); } return argv; } NextSwitch: ; } /** | Here if no file name given; initialise | the printer, then exit without reset. **/ CheckDefaults(); InitPrinter(); puts("Printer initialised ... Good bye!"); Cleanup(SYS_NORMAL_CODE); } static void Syntax(void) { /** | A syntax error has been detected in the command | line; a short help is output to the screen. **/ puts("Usage: PF2 [switches] [file [file [file ... ] ] ]"); puts("Switches: -l : Landscape (default is Portrait);"); puts(" -i : Italic (default is Roman);"); puts(" -bN : insert N Blanks before every output line;"); puts(" -aN : tAb stops every N characters (default: 8);"); puts(" -g : Letter-Gothic font (default is Courier);"); puts(" -t : Times font (default is Courier);"); puts(" -s : Small pitch (Courier: 16.67 cpi; " "Letter-Gothic: 24 cpi);"); puts(" -x : eXtra-small pitch (20 cpi - for Courier only);"); puts(" -8 : 8 lines per inch (default: 6 lpi);"); puts(" -6 : 6 points high font (default: 12 points);"); puts(" -d : Draft quality (default is Letter quality);"); puts(" -2 : special mode (2 pages on every sheet of paper).\n"); puts("The s/x/l switches are ignored when incompatible with selected font."); puts("The default pitch is 10 cpi for Courier and 12 cpi for Letter-Gothic"); puts("(Times is a proportional font); all switches different from -d and"); puts("-a will be ignored, if -2 is selected. The switches on the command"); puts("line can be grouped: e.g. -c -8 -s is the same as -c8s; only -bN and"); puts("-aN, if present, must be specified alone or the last in a group. The"); puts("printer is reset to its default, at the completion - UNLESS if no"); puts("file names are given. This program can be interrupted with CTRL-C.\n"); Cleanup(SYS_NORMAL_CODE); }