home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff282.lzh / PrintHandler / Print-Handler.c < prev    next >
C/C++ Source or Header  |  1989-11-21  |  15KB  |  656 lines

  1. /*    Print-Handler.c v1.1, 30 September 1989
  2.  *
  3.  *    This  printer  device handler implements the single sheet support
  4.  *    and data-spooling facility the usual PRT:  device does not have.
  5.  *
  6.  *    Each time a page has been successfully transferred to the printer
  7.  *    and  single paper sheets are selected in Preferences, a requestor
  8.  *    pops  up  on  the Workbench screen telling the user to insert the
  9.  *    next  sheet  of  paper.   If a process addresses this handler via
  10.  *    Open()  and  Write()s  data  to it, the incoming text is buffered
  11.  *    until the data stream is Close()d.  After that the buffer sent to
  12.  *    the  printer  device and finally gets discarded.  Note that there
  13.  *    will  be  a buffer for each calling process (up to twenty callers
  14.  *    are  allowed),  so  you may get low on memory.  This handler will
  15.  *    notice low memory situations and return a write error in critical
  16.  *    situations.   This handler does no first-in-first-out or last-in-
  17.  *    first-out data stack handling, the first data to come out is from
  18.  *    the first buffer to be closed
  19.  *
  20.  *    Don't  be  confused if the requestor pops up while the printer is
  21.  *    still  working.  Since most printers have a write buffer of their
  22.  *    own true synchronous printer I/O is almost impossible.
  23.  *
  24.  *    Installation is as follows:
  25.  *
  26.  *    * Add the following lines to your DEVS:Mountlist
  27.  *
  28.  *      PRT:    Handler        = L:Print-Handler
  29.  *          Stacksize    = 3000
  30.  *          Priority    = 5
  31.  *          GlobVec        = 1
  32.  *      #
  33.  *
  34.  *    * Add the following lines to your S:Startup-Sequence
  35.  *
  36.  *      Assign PRT: Remove
  37.  *      Mount PRT:
  38.  *
  39.  *    * Copy Print-Handler to your L:
  40.  *
  41.  *    Skeleton handler code by Phillip Lindsay (C) 1986 Commodore
  42.  *    You may freely distribute this source and use it for Amiga Development,
  43.  *    as long as the Copyright notice is left intact.
  44.  *
  45.  *    Print-Handler (C) Copyright 1989 by Olaf Barthel & ED Hannover
  46.  *
  47.  *    Contact:    Olaf Barthel, Electronic Design Hannover
  48.  *            Brabeckstrasse 35
  49.  *            D-3000 Hannover 71
  50.  *
  51.  *            Federal Republic of Germany
  52.  */
  53.  
  54. #include <intuition/intuitionbase.h>
  55. #include <libraries/filehandler.h>
  56. #include <libraries/dosextens.h>
  57. #include <devices/prtbase.h>
  58. #include <devices/printer.h>
  59. #include <exec/memory.h>
  60.  
  61.     /* This version of BADDR() has no problems with castings. */
  62.  
  63. #undef  BADDR
  64. #define BADDR(x) ((APTR)((long)x << 2))
  65.  
  66.     /* Define the packet types which are not available
  67.      * in the dos 1.1/1.2 include files.
  68.      */
  69.  
  70. #define ACTION_FIND_INPUT    1005
  71. #define ACTION_FIND_OUTPUT    1006
  72. #define ACTION_END        1007
  73.  
  74.     /* Some BCPL boolean definitions. */
  75.  
  76. #define DOS_FALSE     0
  77. #define DOS_TRUE    -1
  78.  
  79.     /* Forward declarations. */
  80.  
  81. extern struct Library        *OpenLibrary();
  82. extern struct Process        *FindTask();
  83. extern struct MsgPort        *CreatePort();
  84. extern struct MsgPort        *FindPort();
  85. extern struct Message        *GetMsg();
  86. extern struct IOStdReq        *CreateStdIO();
  87. extern void            *AllocMem();
  88.  
  89.     /* This is a data segment, a part of a larger printer
  90.      * buffer used for data-spooling.
  91.      */
  92.  
  93. struct DataSeg
  94. {
  95.     struct DataSeg    *NextSeg;    /* Next segment. */
  96.  
  97.     APTR        Buffer;        /* Data array. */
  98.     long        Length;        /* Length of array. */
  99. };
  100.  
  101.     /* Some global data. */
  102.  
  103. struct IntuitionBase    *IntuitionBase;
  104. struct Preferences    *Preferences;
  105. struct DataSeg        *PrintSlot[20];
  106. struct IOStdReq        *PrinterDevice;
  107. struct MsgPort        *PrinterPort;
  108.  
  109.     /* Position counters to take care of the current page length. */
  110.  
  111. long LinesDone = 0;
  112. long ColumnsDone = 0;
  113.  
  114.     /* CreateSeg(Buffer,Length):
  115.      *
  116.      *    Creates a new segment entry for a linked list of
  117.      *    buffers.
  118.      */
  119.  
  120. struct DataSeg *
  121. CreateSeg(Buffer,Length)
  122. register APTR Buffer;
  123. register long Length;
  124. {
  125.     register struct DataSeg *NewSeg;
  126.  
  127.         /* Allocate memory for the list element. */
  128.  
  129.     if(NewSeg = (struct DataSeg *)AllocMem(sizeof(struct DataSeg),MEMF_PUBLIC | MEMF_CLEAR))
  130.     {
  131.             /* Allocate memory for the data buffer. */
  132.  
  133.         if(NewSeg -> Buffer = (APTR)AllocMem(Length,MEMF_PUBLIC | MEMF_CLEAR))
  134.         {
  135.                 /* Copy the data and set the buffer size. */
  136.  
  137.             CopyMem(Buffer,NewSeg -> Buffer,Length);
  138.             NewSeg -> Length = Length;
  139.  
  140.                 /* Return the new segment. */
  141.  
  142.             return(NewSeg);
  143.         }
  144.  
  145.             /* Free the segment data. */
  146.  
  147.         FreeMem(NewSeg,sizeof(struct DataSeg));
  148.     }
  149.  
  150.         /* We failed. */
  151.  
  152.     return(NULL);
  153. }
  154.  
  155.     /* DeleteSeg(OldSeg):
  156.      *
  157.      *    Deletes both the contents and the memory occupied
  158.      *    by a data segment and returns a pointer to the
  159.      *    next segment.
  160.      */
  161.  
  162. struct DataSeg *
  163. DeleteSeg(OldSeg)
  164. register struct DataSeg *OldSeg;
  165. {
  166.     register struct DataSeg *NextSeg = NULL;
  167.  
  168.         /* Valid pointer given? */
  169.  
  170.     if(OldSeg)
  171.     {
  172.             /* Remember this. */
  173.  
  174.         NextSeg = OldSeg -> NextSeg;
  175.  
  176.             /* Free the contents of the buffer. */
  177.  
  178.         if(OldSeg -> Buffer && OldSeg -> Length)
  179.         {
  180.             FreeMem(OldSeg -> Buffer,OldSeg -> Length);
  181.  
  182.                 /* Zero this out. */
  183.  
  184.             OldSeg -> Buffer = NULL;
  185.             OldSeg -> Length = 0;
  186.         }
  187.  
  188.             /* Free the segment data. */
  189.  
  190.         FreeMem(OldSeg,sizeof(struct DataSeg));
  191.     }
  192.  
  193.         /* Return pointer to next segment or null. */
  194.  
  195.     return(NextSeg);
  196. }
  197.  
  198.     /* ReturnPacket(Packet,res1,res2):
  199.      *
  200.      *    This one returns an AmigaDOS packet we have just
  201.      *    received.
  202.      */
  203.  
  204. void
  205. ReturnPacket(Packet,res1,res2)
  206. register struct DosPacket *Packet;
  207. register ULONG res1,res2;
  208. {
  209.     register struct Message    *Message;
  210.     register struct MsgPort    *ReplyPort;
  211.     register struct Process    *ThisProg;
  212.  
  213.     Packet -> dp_Res1        = res1;
  214.     Packet -> dp_Res2        = res2; 
  215.     ReplyPort            = Packet -> dp_Port;
  216.     Message                = Packet -> dp_Link;
  217.     ThisProg            = (struct Process *)FindTask(NULL);
  218.     Packet -> dp_Port        = &ThisProg -> pr_MsgPort;
  219.  
  220.     Message -> mn_Node . ln_Name    = (char *)Packet;
  221.     Message -> mn_Node . ln_Succ    = NULL;
  222.     Message -> mn_Node . ln_Pred    = NULL;
  223.  
  224.     PutMsg(ReplyPort,Message); 
  225. }
  226.  
  227.     /* TaskWait():
  228.      *
  229.      *    This one waits for an AmigaDOS packet to arrive
  230.      *    and returns a pointer to it.
  231.      */
  232.  
  233. struct DosPacket *
  234. TaskWait()
  235. {
  236.     register struct Process    *ThisProg;
  237.     register struct MsgPort    *MyPort;
  238.     register struct Message    *MyMessage;
  239.  
  240.     ThisProg = (struct Process *)FindTask(NULL);
  241.     MyPort = &ThisProg -> pr_MsgPort;
  242.  
  243.     WaitPort(MyPort);
  244.     MyMessage = (struct Message *)GetMsg(MyPort);
  245.  
  246.     return((struct DosPacket *)MyMessage -> mn_Node . ln_Name);
  247.  
  248.     /* PrintIt(Buffer,Length):
  249.      *
  250.      *    This function handles the printing. We could as well
  251.      *    replace the data <-> printer transfer by a short call
  252.      *    to Write(); probably the easiest way to implement
  253.      *    output redirection.
  254.      */
  255.  
  256. long
  257. PrintIt(Buffer,Length)
  258. register APTR Buffer;
  259. register long Length;
  260. {
  261.         /* Are buffer and size both valid? */
  262.  
  263.     if(Buffer && Length)
  264.     {
  265.             /* Send the text to the printer. */
  266.  
  267.         PrinterDevice -> io_Command    = CMD_WRITE;
  268.         PrinterDevice -> io_Data    = Buffer;
  269.         PrinterDevice -> io_Length    = Length;
  270.  
  271.         return(DoIO(PrinterDevice));
  272.     }
  273.  
  274.     return(-1);
  275. }
  276.  
  277.     /* DoRequest():
  278.      *
  279.      *    Displays the requestor asking to insert the next
  280.      *    sheet of paper and returns the result.
  281.      */
  282.  
  283. BOOL
  284. DoRequest()
  285. {
  286.     static struct TextAttr DefaultFont =
  287.     {
  288.         (UBYTE *)"topaz.font",9,FS_NORMAL,FPF_ROMFONT
  289.     };
  290.  
  291.     static struct IntuiText RequestTxt[] =
  292.     {
  293.         {0,1,JAM1,18, 4,(struct TextAttr *)&DefaultFont,(UBYTE *)"Finished with current page,",    &RequestTxt[1]},
  294.         {0,1,JAM1,18,14,(struct TextAttr *)&DefaultFont,(UBYTE *)"please insert next sheet.",    NULL},
  295.         {0,1,JAM1, 5, 3,(struct TextAttr *)&DefaultFont,(UBYTE *)"Ready",            NULL},
  296.         {0,1,JAM1, 5, 3,(struct TextAttr *)&DefaultFont,(UBYTE *)"Abort",            NULL}
  297.     };
  298.  
  299.         /* Open the Workbench first, we don't want to have a dead
  300.          * Workbench screen hanging around if a task preferred to
  301.          * close it.
  302.          */
  303.  
  304.     OpenWorkBench();
  305.  
  306.     return(AutoRequest(NULL,&RequestTxt[0],&RequestTxt[2],&RequestTxt[3],NULL,NULL,326,59));
  307. }
  308.  
  309.     /* PrintData(Buffer,Length):
  310.      *
  311.      *    This is the main interface processing the incoming
  312.      *    data.
  313.      */
  314.  
  315. BOOL
  316. PrintData(Buffer,Length)
  317. register char *Buffer;
  318. register long Length;
  319. {
  320.     register long i,j,InBuff = 0,PaperWidth = Preferences -> PrintRightMargin - Preferences -> PrintLeftMargin;
  321.     char LineStack[1024];
  322.  
  323.         /* Clear out the contents of the line buffer. */
  324.  
  325.     for(i = 0 ; i < 1024 ; i++)
  326.         LineStack[i] = 0;
  327.  
  328.     if(Buffer)
  329.     {
  330.         for(i = 0 ; i < Length ; i++)
  331.         {
  332.                 /* Put the data into the line buffer. */
  333.  
  334.             LineStack[InBuff++] = Buffer[i];
  335.             ColumnsDone++;
  336.  
  337.                 /* Right margin reached/newline encountered? */
  338.  
  339.             if(ColumnsDone == PaperWidth || Buffer[i] == '\n')
  340.             {
  341.                     /* Overriding the paper width
  342.                      * does not necessarily include a
  343.                      * newline.
  344.                      */
  345.  
  346.                 if(ColumnsDone == PaperWidth || ColumnsDone == 1024)
  347.                 {
  348.                     LineStack[InBuff++] = '\n';
  349.                     ColumnsDone++;
  350.                 }
  351.  
  352.                     /* Send the line to the printer. */
  353.  
  354.                 PrintIt(LineStack,InBuff);
  355.  
  356.                     /* Did we reach the bottom line? */
  357.  
  358.                 if((++LinesDone) == Preferences -> PaperLength)
  359.                 {
  360.                     LinesDone = 0;
  361.  
  362.                         /* Inform the user about it. */
  363.  
  364.                     if(Preferences -> PaperType == SINGLE)
  365.                         if(!DoRequest())
  366.                             return(FALSE);
  367.                 }
  368.  
  369.                     /* Clear the rest. */
  370.  
  371.                 ColumnsDone = InBuff = 0;
  372.  
  373.                 for(j = 0 ; j < 1024 ; j++)
  374.                     LineStack[j] = 0;
  375.             }
  376.         }
  377.  
  378.             /* Is there still something in the line buffer? */
  379.  
  380.         if(InBuff)
  381.             PrintIt(LineStack,InBuff);
  382.     }
  383.  
  384.         /* We succeeded and nobody cancelled us. */
  385.  
  386.     return(TRUE);
  387. }
  388.  
  389.     /* _main():
  390.      *
  391.      *    Handler main routine, bypasses all typical Aztec
  392.      *    startup code.
  393.      */
  394.  
  395. _main()
  396. {
  397.     struct Process        *ThisProg = (struct Process *)FindTask(NULL);
  398.     struct DosPacket    *MyPacket;
  399.     struct DeviceNode    *MyNode;    /* Our device node passed in parmpkt Arg3. */
  400.     long            OpenCount = 0;    /* Handler open flag. */
  401.     BOOL            Running = TRUE;    /* Handler main loop flag. */
  402.  
  403.     register long NextSlot = 0;
  404.     register long i;
  405.  
  406.         /* The list of available buffers. */
  407.  
  408.     UBYTE Available[20];
  409.  
  410.         /* Since we were started as a non-BCPL module we get sent the
  411.          * parameter packet (i.e. parameter packet not in D1).
  412.          */
  413.  
  414.     MyPacket = TaskWait();    /* Wait for parameter packet. */
  415.  
  416.         /* Mark all buffers as empty. */
  417.  
  418.     for(i = 0 ; i < 20 ; i++)
  419.     {
  420.         Available[i] = TRUE;
  421.         PrintSlot[i] = NULL;
  422.     }
  423.  
  424.         /* Get a pointer to our device node. */
  425.  
  426.     MyNode = (struct DeviceNode *)BADDR(MyPacket -> dp_Arg3);
  427.  
  428.         /* Do the main handler initialization. */
  429.  
  430.     if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",33)))
  431.     {
  432.         ReturnPacket(MyPacket,DOS_FALSE,MyPacket -> dp_Res2);
  433.         goto FallOff;
  434.     }
  435.  
  436.     if(!(PrinterPort = (struct MsgPort *)CreatePort(NULL,0)))
  437.     {
  438.         ReturnPacket(MyPacket,DOS_FALSE,MyPacket -> dp_Res2);
  439.         goto FallOff;
  440.     }
  441.  
  442.     if(!(PrinterDevice = (struct IOStdReq *)CreateStdIO(PrinterPort)))
  443.     {
  444.         ReturnPacket(MyPacket,DOS_FALSE,MyPacket -> dp_Res2);
  445.         goto FallOff;
  446.     }
  447.  
  448.     if(OpenDevice("printer.device",0,PrinterDevice,0))
  449.     {
  450.         ReturnPacket(MyPacket,DOS_FALSE,MyPacket -> dp_Res2);
  451.         goto FallOff;
  452.     }
  453.  
  454.         /* Initialize the Preferences pointer to
  455.          * reflect current printer.device settings.
  456.          */
  457.  
  458.     Preferences = &((struct PrinterData *)PrinterDevice -> io_Device) -> pd_Preferences;
  459.  
  460.         /* If initialization was possible then we install our
  461.          * taskid.   If we don't...for every reference to our
  462.          * handler  a  new  process will be created.  This is
  463.          * fine  for  things like CON:  (console handler) but
  464.          * if  you  plan  to be the only dude on block  (like
  465.          * the file-system handler or SER:)   you should fill
  466.          * the    task    field   with   your   taskid  (i.e.
  467.          * &(pr_MsgPort))    Note:  remember that shared code
  468.          * has  to  be reentrant.  (like CON:  handler), keep
  469.          * your  variables on the stack [autos], and allocate
  470.          * memory  for  larger  data  structures  and  "FLAG"
  471.          * global   data   structures   that   need  only  be
  472.          * intialized once.
  473.          */
  474.  
  475.     MyNode -> dn_Task = &ThisProg -> pr_MsgPort;
  476.  
  477.     ReturnPacket(MyPacket,DOS_TRUE,MyPacket -> dp_Res2);
  478.  
  479.     while(Running)    /* Start of the real work. */
  480.     {
  481.         MyPacket = TaskWait();    /* Wait for a packet. */
  482.  
  483.         switch(MyPacket -> dp_Type)
  484.         {
  485.                 /* Somebody Open()ed us. */
  486.  
  487.             case ACTION_FIND_INPUT:
  488.             case ACTION_FIND_OUTPUT:
  489.             {
  490.                 struct FileHandle *FileHandle = (struct FileHandle *)BADDR(MyPacket -> dp_Arg1);
  491.  
  492.                     /* Assume failure. */
  493.  
  494.                 FileHandle -> fh_Port = DOS_FALSE;
  495.  
  496.                 for(i = 0 ; i < 20 ; i++)
  497.                 {
  498.                         /* Any buffer available? */
  499.  
  500.                     if(Available[i])
  501.                     {
  502.                             /* We didn't fail. */
  503.  
  504.                         FileHandle -> fh_Port = DOS_TRUE;
  505.                         FileHandle -> fh_Arg1 = i;
  506.  
  507.                         Available[i] = FALSE;
  508.  
  509.                             /* Increment usercount. */
  510.  
  511.                         OpenCount++;
  512.  
  513.                         break;
  514.                     }
  515.                 }
  516.  
  517.                     /* Return the compliment. */    
  518.  
  519.                 ReturnPacket(MyPacket,FileHandle -> fh_Port,MyPacket -> dp_Res2);
  520.                 break;
  521.             }
  522.  
  523.                 /* Someone Close()d the file. */
  524.  
  525.             case ACTION_END:
  526.             {
  527.                 long TheSlotIs = MyPacket -> dp_Arg1;
  528.                 BOOL GoOn = TRUE;
  529.  
  530.                     /* We want to fall out of the loop if not OPEN. */
  531.  
  532.                 if((--OpenCount) <= 0)
  533.                     Running = FALSE;
  534.  
  535.                 ReturnPacket(MyPacket,DOS_TRUE,MyPacket -> dp_Res2);
  536.  
  537.                     /* Print the current buffer. */
  538.  
  539.                 while(PrintSlot[TheSlotIs])
  540.                 {
  541.                     if(GoOn)
  542.                         if(!PrintData(PrintSlot[TheSlotIs] -> Buffer,PrintSlot[TheSlotIs] -> Length))
  543.                             GoOn = FALSE;
  544.  
  545.                     PrintSlot[TheSlotIs] = DeleteSeg(PrintSlot[TheSlotIs]);
  546.                 }
  547.  
  548.                 Available[TheSlotIs] = TRUE;
  549.  
  550.                     /* Reset the line counters. */
  551.  
  552.                 LinesDone = ColumnsDone = 0;
  553.  
  554.                 break;
  555.             }
  556.  
  557.                 /* Someone tries to Read() us. */
  558.  
  559.             case ACTION_READ:
  560.             {
  561.                     /* We *always* read nothing. */
  562.  
  563.                 ReturnPacket(MyPacket,NULL,MyPacket -> dp_Res2);
  564.                 break;
  565.             }
  566.  
  567.                 /* Someone tries to Write() to us. */
  568.  
  569.             case ACTION_WRITE:
  570.             {
  571.                 long TheSlotIs = MyPacket -> dp_Arg1;
  572.                 char *Buffer = (char *)MyPacket -> dp_Arg2;
  573.                 struct DataSeg *NextSlot;
  574.  
  575.                 MyPacket -> dp_Res1 = MyPacket -> dp_Arg3;
  576.  
  577.                     /* Buffer not initialized yet? */
  578.  
  579.                 if(!PrintSlot[TheSlotIs])
  580.                 {
  581.                     if(!(PrintSlot[TheSlotIs] = CreateSeg(Buffer,MyPacket -> dp_Res1)))
  582.                         MyPacket -> dp_Res1 = -1;
  583.                 }
  584.                 else
  585.                 {
  586.                         /* Add a new buffer to the current list. */
  587.  
  588.                     NextSlot = PrintSlot[TheSlotIs];
  589.  
  590.                     while(NextSlot -> NextSeg)
  591.                         NextSlot = NextSlot -> NextSeg;
  592.  
  593.                     if(!(NextSlot -> NextSeg = CreateSeg(Buffer,MyPacket -> dp_Res1)))
  594.                         MyPacket -> dp_Res1 = -1;
  595.                 }
  596.  
  597.                     /* We *always* write everything. */
  598.  
  599.                 ReturnPacket(MyPacket,MyPacket -> dp_Arg3,MyPacket -> dp_Res2);
  600.                 break;
  601.             }
  602.  
  603.                 /* Someone wants us the leave the town. */
  604.  
  605.             case ACTION_DIE:
  606.             {
  607.                     /* Empty all buffers. */
  608.  
  609.                 for(i = 0 ; i < 20 ; i++)
  610.                     while(PrintSlot[i])
  611.                         PrintSlot[i] = DeleteSeg(PrintSlot[i]);
  612.  
  613.                     /* Result will be ignored anyway. */
  614.  
  615.                 ReturnPacket(MyPacket,DOS_FALSE,ERROR_ACTION_NOT_KNOWN);
  616.  
  617.                 goto FallOff;
  618.             }
  619.  
  620.                 /* For any other purpose: ignore the message. */
  621.  
  622.             default:
  623.             {
  624.                     /* Say what? */
  625.  
  626.                 ReturnPacket(MyPacket,DOS_FALSE,ERROR_ACTION_NOT_KNOWN);
  627.  
  628.                 break;
  629.             }
  630.         }
  631.     }
  632.  
  633.         /* Fall off the edge of the world. */
  634.  
  635. FallOff:MyNode -> dn_Task = FALSE; /* Zero the TaskID field of device node. */
  636.  
  637.         /* Free all our data. */
  638.  
  639.     if(PrinterDevice)
  640.     {
  641.         if(PrinterDevice -> io_Device)
  642.             CloseDevice(PrinterDevice);
  643.  
  644.         DeleteStdIO(PrinterDevice);
  645.     }
  646.  
  647.     if(PrinterPort)
  648.         DeletePort(PrinterPort);
  649.  
  650.     if(IntuitionBase)
  651.         CloseLibrary(IntuitionBase);
  652.  
  653.         /* This is truly the end. */
  654. }
  655.