home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / GUSI / GUSIPAP.cp < prev    next >
Encoding:
Text File  |  1993-12-30  |  12.2 KB  |  678 lines  |  [TEXT/MPS ]

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIPAP.cp        -    Printer Access Protocol Sockets
  4. Author    :    Matthias Neeracher
  5.  
  6.     Based on code from 
  7.         Sak Wathanasin <sw@nan.co.uk>
  8.         David A. Holzgang, _Programming the LaserWriter_, Addison-Wesley 1991
  9.         Apple's Q&A stack
  10.     
  11. Started    :    10Feb93                                Language    :    MPW C/C++
  12. Modified    :    01Mar93    MN    Be more clever about handling shutdowns
  13.                 03Apr93    MN    close() has to do shutdown as well.
  14.                 01Sep93    MN    Throw out nonbreaking spaces
  15.                 30Dec93    MN    Fiddle with select
  16. Last        :    30Dec93
  17. *********************************************************************/
  18.  
  19. #include "GUSI_P.h"
  20.  
  21. #include <Resources.h>
  22. #include <AppleTalk.h>
  23. #include <Errors.h>
  24. #include <Folders.h>
  25. #include <PLStringFuncs.h>
  26. #include <TextUtils.h>
  27. #include <Timer.h>
  28.  
  29. #include <sys/types.h>
  30.  
  31. const long NiceDoggie    =    20;        // Rest Hellhound for 20msec
  32. const long MaxPAP            =    4096;        //    Maximum transaction size
  33.  
  34. class PAPSocket;                             // That's what this file's all about
  35.  
  36. class PAPID {
  37.     void        GetPAPCode(short vRefNum, long dirID, StringPtr name);
  38.     
  39.     Handle    papCode;
  40.     Handle    papName;    
  41. public:
  42.     PAPID();
  43.     
  44.     ~PAPID();
  45.     
  46.     Ptr    Code()            {    return *papCode;    }
  47.     Ptr    Name()            {    return *papName;    }
  48.     operator void *()        {    return papCode;    }
  49. };
  50.  
  51. struct PAPPB {
  52.     TMTask            timer;
  53.     short                length;
  54.     short                eof;
  55.     short                state;
  56.     PAPSocket *        sock;
  57. };
  58.  
  59. struct PAPStatusRec { 
  60.     long      systemStuff;
  61.    char      statusstr[256];
  62. };
  63.         
  64.  
  65. class PAPSocket : public Socket    {        
  66.     friend class PAPSocketDomain;    
  67.     friend pascal void PAPReadTimer();
  68.     friend pascal void PAPWriteTimer();
  69.     friend pascal void PAPReadHellHound(PAPPB *);
  70.     friend pascal void PAPWriteHellHound(PAPPB *);
  71.  
  72.     enum {
  73.         opening,
  74.         open,
  75.         closed
  76.     }                        status;
  77.     Boolean                nonblocking;
  78.     Boolean                readPending;
  79.     Boolean                writePending;
  80.     Boolean                readShutDown;
  81.     Boolean                writeShutDown;
  82.     short                    papRefNum;
  83.     RingBuffer *        rb;
  84.     RingBuffer *        wb;
  85.     long                    ourA5;
  86.     PAPID                    papID;
  87.     PAPStatusRec        papStatus;
  88.     PAPPB                    wpb;
  89.     PAPPB                    rpb;
  90.     
  91.                     PAPSocket();
  92.                     
  93.     virtual         ~PAPSocket();
  94.     
  95.     int            Powerup();
  96. public:
  97.     virtual int    fcntl(unsigned int cmd, int arg);
  98.     virtual int    recvfrom(void * buffer, int buflen, int flags, void * from, int *);
  99.     virtual int sendto(void * buffer, int buflen, int flags, void * to, int);
  100.     virtual int select(Boolean * canRead, Boolean * canWrite, Boolean * exception);
  101.     virtual int    ioctl(unsigned int request, void *argp);
  102.     virtual int shutdown(int how);
  103. };    
  104.  
  105. class PAPSocketDomain : public DeviceSocketDomain {
  106. public:
  107.     PAPSocketDomain()    :    DeviceSocketDomain(AF_PAP)    {    }
  108.     
  109.     virtual     Socket * open(const char * filename, int oflag);
  110. };
  111.  
  112. PAPSocketDomain    PAPSockets;
  113.  
  114. /***************************** PAP glue *****************************/
  115.  
  116. pascal short PAPOpen(
  117.     short *             refNum, 
  118.     char *             printerName,
  119.     short             flowQuantum, 
  120.     PAPStatusRec * statusBuf, 
  121.     short *            compState,
  122.     Ptr                papCode
  123. )    =    {0x205F, 0x4EA8, 0x0000};
  124.  
  125. pascal short PAPRead(
  126.     short                refNum, 
  127.     char *            buffer,
  128.     short *            length, 
  129.     short *            eof,
  130.     short *            compState,
  131.     Ptr                papCode
  132. )    =    {0x205F, 0x4EA8, 0x0004};
  133.  
  134. pascal short PAPWrite(
  135.     short                 refNum,
  136.      char *            buffer,
  137.      short                length,
  138.      short                eof,
  139.      short *            compState,
  140.     Ptr                papCode
  141. )    =    {0x205F, 0x4EA8, 0x0008};
  142.  
  143. pascal short PAPStatus(
  144.     char    *            printerName,
  145.      PAPStatusRec *    statusBuff,
  146.      AddrBlock *        netAddr,
  147.     Ptr                papCode
  148. )    =    {0x205F, 0x4EA8, 0x000C};
  149.  
  150. pascal short PAPClose(
  151.      short                refNum,
  152.     Ptr                papCode
  153. )    =    {0x205F, 0x4EA8, 0x0010};
  154.  
  155. pascal short PAPUnload(
  156.     Ptr                papCode
  157. )    =    {0x205F, 0x4EA8, 0x0014};    
  158.  
  159. /********************* Link stuffing procedures *********************/
  160.  
  161. #pragma segment GUSIResident
  162.  
  163. PAPPB * GetPAPInfo() = 0x2009;                    // MOVE.L A1,D0
  164.  
  165. pascal void PAPReadTimer()
  166. {
  167.     PAPPB *    pb        =    GetPAPInfo();
  168.     long        oldA5    =    SetA5(pb->sock->ourA5);
  169.     
  170.     PAPReadHellHound(pb);
  171.     
  172.     SetA5(oldA5);
  173. }
  174.  
  175. pascal void PAPWriteTimer()
  176. {
  177.     PAPPB *    pb        =    GetPAPInfo();
  178.     long        oldA5    =    SetA5(pb->sock->ourA5);
  179.     
  180.     PAPWriteHellHound(pb);
  181.     
  182.     SetA5(oldA5);
  183. }
  184.  
  185. pascal void PAPReadHellHound(PAPPB * pb)
  186. {
  187.     if (pb->state > 0) {
  188.         PrimeTime(QElemPtr(pb), NiceDoggie);        // See you again 
  189.         
  190.         return;
  191.     }
  192.     
  193.     if (!pb->sock->rb)                                    // We're closing
  194.         return;
  195.     
  196.     PAPSocket &        sock    =    *pb->sock;    
  197.     RingBuffer &     buf     =    *sock.rb;
  198.     Boolean &        pend    =    sock.readPending;
  199.         
  200.     if (buf.Locked())
  201.         buf.Later(Deferred(PAPReadHellHound), pb);
  202.     else    {
  203.         buf.Later(nil, nil);
  204.         if (pend) {
  205.             pend    =    false;
  206.             
  207.             if (pb->state)    {
  208.                 pb->sock->readShutDown    =    true;
  209.                 
  210.                 return;
  211.             }
  212.             
  213.             buf.Validate(pb->length);
  214.             
  215.             if (pb->eof){
  216.                 pb->sock->readShutDown    =    true;
  217.                 
  218.                 return;
  219.             }
  220.         }
  221.         
  222.         if (!buf.Free()) 
  223.             buf.Later(Deferred(PAPReadHellHound), pb);
  224.         else {
  225.             char *    buffer;
  226.             long        max    =    MaxPAP;
  227.             
  228.             buffer            =    buf.Producer(max);
  229.             pb->length        =    short(max);
  230.             pend                =    true;
  231.             
  232.             PAPRead(
  233.                 sock.papRefNum,
  234.                 buffer, 
  235.                 &pb->length,
  236.                 &pb->eof,
  237.                 &pb->state,
  238.                 sock.papID.Code());
  239.                 
  240.             PrimeTime(QElemPtr(pb), NiceDoggie);        // See you again 
  241.         }
  242.     }
  243.  
  244. }
  245.  
  246. pascal void PAPWriteHellHound(PAPPB * pb)
  247. {
  248.     if (pb->state > 0) {
  249.         PrimeTime(QElemPtr(pb), NiceDoggie);        // See you again 
  250.         
  251.         return;
  252.     }
  253.     
  254.     if (!pb->sock->wb)                                    // We're closing
  255.         return;
  256.         
  257.     PAPSocket &        sock    =    *pb->sock;    
  258.     RingBuffer &    buf     =    *sock.wb;
  259.     Boolean &        pend    =    sock.writePending;
  260.         
  261.     if (buf.Locked())
  262.         buf.Later(Deferred(PAPWriteHellHound), pb);
  263.     else    {
  264.         buf.Later(nil, nil);
  265.         
  266.         if (pend) {
  267.             if (pb->state)    {
  268.                 pb->sock->writeShutDown    =    true;
  269.                 pb->eof                        =    1;
  270.                 
  271.                 return;
  272.             }
  273.  
  274.             buf.Invalidate(pb->length);
  275.             pend    =    false;
  276.         }
  277.         
  278.         if (!buf.Valid()) {
  279.             if (pb->sock->writeShutDown && !pb->eof)
  280.                 PAPWrite(
  281.                     sock.papRefNum,
  282.                      0,
  283.                      0,
  284.                      pb->eof = 1,
  285.                      &pb->state,
  286.                     sock.papID.Code());
  287.             else
  288.                 buf.Later(Deferred(PAPWriteHellHound), pb);
  289.         } else {
  290.             char *     buffer;
  291.             long        max    =    MaxPAP;
  292.             
  293.             buffer            =    buf.Consumer(max);
  294.             pb->length        =    short(max);
  295.             pend                =    true;
  296.             
  297.             PAPWrite(
  298.                 sock.papRefNum,
  299.                  buffer,
  300.                  pb->length,
  301.                  0,
  302.                  &pb->state,
  303.                 sock.papID.Code());
  304.                 
  305.             PrimeTime(QElemPtr(pb), NiceDoggie);        // See you again
  306.         }
  307.     }
  308. }
  309.  
  310. #pragma segment GUSIPAP
  311.  
  312. /************************** PAPID members **************************/
  313.  
  314. void PAPID::GetPAPCode(short vRefNum, long dirID, StringPtr name)
  315. {
  316.     short        res;
  317.     
  318.     res = HOpenResFile(vRefNum, dirID, name, fsRdPerm);
  319.     
  320.     if (res == -1)
  321.         return;
  322.         
  323.     papCode = Get1Resource('PDEF', 10);
  324.     
  325.     if (papCode) {
  326.         DetachResource(papCode);
  327.         MoveHHi(papCode);
  328.         HLock(papCode);
  329.     } else 
  330.         goto done;
  331.  
  332.     papName = Get1Resource('PAPA', -8192);
  333.     
  334.     if (papName) {
  335.         DetachResource(papName);
  336.         MoveHHi(papName);
  337.         HLock(papName);
  338.     } else {
  339.         DisposeHandle(papCode);
  340.         
  341.         papCode = nil;
  342.     }
  343.  
  344. done:    
  345.     CloseResFile(res);
  346. }
  347.  
  348. PAPID::PAPID()
  349. {
  350.     OSErr                err;
  351.     short                saveRes;
  352.     short                prVol;
  353.     long                prDir;
  354.     StringHandle    printer;
  355.         
  356.     papCode    =    nil;
  357.     papName    =    nil;
  358.  
  359.     if (err = FindFolder(
  360.                     kOnSystemDisk, 
  361.                     kExtensionFolderType, 
  362.                     kDontCreateFolder,
  363.                     &prVol,
  364.                     &prDir)
  365.     )
  366.         return;
  367.         
  368.     saveRes = CurResFile();
  369.     UseResFile(0);
  370.             
  371.     if (printer = StringHandle(Get1Resource('STR ', -8192))) {
  372.         HLock(Handle(printer));
  373.         
  374.         GetPAPCode(prVol, prDir, *printer);
  375.         
  376.         ReleaseResource(Handle(printer));
  377.     } 
  378.  
  379.     if (!papCode)
  380.         GetPAPCode(prVol, prDir, "\pLaserWriter");
  381.     
  382.     UseResFile(saveRes);
  383. }
  384.  
  385. PAPID::~PAPID()
  386. {
  387.     if (papName)    
  388.         DisposeHandle(papName);
  389.     if (papCode)
  390.         DisposeHandle(papCode);
  391. }
  392.  
  393. /************************ PAPSocket members ************************/
  394.  
  395. PAPSocket::PAPSocket()
  396. {
  397.     status            =    PAPSocket::opening;
  398.     nonblocking        =    false;
  399.     rb                    =    new RingBuffer(4096);
  400.     wb                    =    new RingBuffer(4096);
  401.     readPending        =    false;
  402.     writePending    =    false;
  403.     readShutDown    =    false;
  404.     writeShutDown    =    false;
  405.     ourA5                =    SetCurrentA5();
  406.     
  407.     if (!papID) {
  408.         GUSI_error(ENETDOWN);
  409.         
  410.         return;
  411.     } else if (!rb || !wb) {
  412.         GUSI_error(ENOMEM);
  413.         
  414.         return;
  415.     }
  416.     
  417.     if (PAPOpen(&papRefNum, papID.Name(), 8, &papStatus, &wpb.state, papID.Code()))
  418.         GUSI_error(ENETDOWN);
  419. }
  420.  
  421. PAPSocket::~PAPSocket()
  422. {
  423.     char dummy;
  424.     
  425.     shutdown(1);                                                // Got nothing morte to say
  426.     while (recvfrom(&dummy, 1, 0, nil, nil) > 0)        // Wait for printer to complete
  427.         ;
  428.         
  429.     PAPUnload(papID.Code());
  430.  
  431.     if (rb)
  432.         delete rb;
  433.     
  434.     if (wb)
  435.         delete wb;
  436. }
  437.  
  438. int PAPSocket::Powerup()
  439. {
  440.     switch (status) {
  441.     case PAPSocket::opening:
  442.         if (wpb.state > 0 && nonblocking)
  443.             return GUSI_error(EWOULDBLOCK);
  444.             
  445.         SPIN(wpb.state > 0, SP_MISC, 0);
  446.         
  447.         if (wpb.state) {
  448.             status = PAPSocket::closed;
  449.             
  450.             return GUSI_error(ENETDOWN);
  451.         } 
  452.         
  453.         status    =    PAPSocket::open;
  454.         
  455.         // Opening was successful, start hellhounds
  456.         
  457.         rpb.timer.tmAddr        =    PAPReadTimer;
  458.         rpb.timer.tmWakeUp    =    0;
  459.         rpb.timer.tmReserved    =    0;
  460.         rpb.sock                    =    this;
  461.         
  462.         wpb.timer.tmAddr        =    PAPWriteTimer;
  463.         wpb.timer.tmWakeUp    =    0;
  464.         wpb.timer.tmReserved    =    0;
  465.         wpb.sock                    =    this;
  466.         wpb.eof                    =    0;
  467.         
  468.         InsTime((QElem *) &rpb.timer);
  469.         InsTime((QElem *) &wpb.timer);
  470.         
  471.         PAPReadHellHound(&rpb);
  472.         PAPWriteHellHound(&wpb);
  473.     
  474.         return 0;
  475.     case PAPSocket::open:
  476.         return 0;
  477.     case PAPSocket::closed:
  478.         return GUSI_error(ENOTCONN);
  479.     }
  480. }
  481.  
  482. int PAPSocket::fcntl(unsigned int cmd, int arg)
  483. {
  484.     switch (cmd)    {
  485.     case F_GETFL:
  486.         if (nonblocking)
  487.             return FNDELAY;
  488.         else
  489.             return 0;
  490.     case F_SETFL:
  491.         if (arg & FNDELAY)
  492.             nonblocking = true;
  493.         else
  494.             nonblocking = false;
  495.             
  496.         return 0;
  497.     default:
  498.         return GUSI_error(EOPNOTSUPP);
  499.     }
  500. }
  501.  
  502. int PAPSocket::ioctl(unsigned int request, void *argp)
  503. {
  504.     switch (request)    {
  505.     case FIONBIO:
  506.         nonblocking    =    (Boolean) *(long *) argp;
  507.         
  508.         return 0;
  509.     case FIONREAD:
  510.         if (Powerup())
  511.             return -1;
  512.             
  513.         if (status != PAPSocket::open)
  514.             return GUSI_error(ENOTCONN);    
  515.     
  516.         *(unsigned long *) argp    = rb->Valid();
  517.         
  518.         return 0;
  519.     default:
  520.         return GUSI_error(EOPNOTSUPP);
  521.     }
  522. }
  523.  
  524. int PAPSocket::recvfrom(void * buffer, int buflen, int flags, void * from, int *)
  525. {
  526.     if (from)
  527.         return GUSI_error(EOPNOTSUPP);
  528.     if (flags)
  529.         return GUSI_error(EOPNOTSUPP);
  530.  
  531.     if (Powerup())
  532.         return -1;
  533.         
  534.     if (!rb->Valid())    
  535.         if (readShutDown)
  536.             return 0;
  537.         else if (nonblocking)
  538.             return GUSI_error(EWOULDBLOCK);
  539.         else
  540.             SPIN(!rb->Valid() && !readShutDown, SP_STREAM_READ, 0);
  541.     
  542.     long    len    =    buflen;
  543.     
  544.     rb->Consume(Ptr(buffer), len);
  545.     
  546.     return len;
  547. }
  548.  
  549. int PAPSocket::sendto(void * buffer, int buflen, int flags, void * to, int)
  550. {
  551.     if (to)
  552.         return GUSI_error(EOPNOTSUPP);
  553.     if (flags)
  554.         return GUSI_error(EOPNOTSUPP);
  555.     
  556.     if (Powerup())
  557.         return -1;
  558.         
  559.     if (writeShutDown)
  560.         return GUSI_error(ESHUTDOWN);
  561.     
  562.     if (!wb->Free())
  563.         if (nonblocking)
  564.             return GUSI_error(EWOULDBLOCK);
  565.         
  566.     long    len    =    buflen;
  567.     long    done    =    0;
  568.     
  569.     for (;;) {
  570.         wb->Produce(Ptr(buffer), len);
  571.         
  572.         done        +=    len;
  573.         
  574.         if (nonblocking)
  575.             break;
  576.         
  577.         buflen    -=    int(len);
  578.         
  579.         if (!buflen)
  580.             break;
  581.         
  582.         buffer     =    Ptr(buffer) + len;
  583.         len        =    buflen;
  584.         
  585.         SPIN(!wb->Free() && !writeShutDown, SP_STREAM_WRITE, 0);
  586.         
  587.         if (writeShutDown)
  588.             break;
  589.     }
  590.     
  591.     return done;
  592. }
  593.  
  594. int PAPSocket::shutdown(int how)
  595. {
  596.     if (how < 0 || how > 2)
  597.         return GUSI_error(EINVAL);
  598.     
  599.     if (how) {
  600.         writeShutDown    =    true;
  601.         
  602.         if (status == PAPSocket::open)
  603.             wb->Undefer();                            //    Wake up write hellhound
  604.     }
  605.     if (!(how & 1))
  606.         readShutDown    =    true;
  607.         
  608.     return 0;
  609. }
  610.  
  611. int PAPSocket::select(Boolean * canRead, Boolean * canWrite, Boolean *)
  612. {
  613.     int        goodies     =     0;
  614.     
  615.     if (canRead)
  616.         switch (status) {
  617.         case PAPSocket::open:
  618.             if (rb->Valid() || readShutDown) {
  619.                 *canRead = true;
  620.                 ++goodies;
  621.             }
  622.             break;
  623.         case PAPSocket::opening:
  624.             break;
  625.         case PAPSocket::closed:
  626.             *canRead = true;
  627.             ++goodies;
  628.             break;
  629.         }
  630.     
  631.     if (canWrite)
  632.         switch (status) {
  633.         case PAPSocket::opening:
  634.             if (!(wpb.state > 0)) {
  635.                 *canWrite = true;
  636.                 ++goodies;
  637.             }
  638.             break;
  639.         case PAPSocket::open:
  640.             if (wb->Free() || writeShutDown) {
  641.                 *canWrite = true;
  642.                 ++goodies;
  643.             }
  644.             break;
  645.         case PAPSocket::closed:
  646.             *canRead = true;
  647.             ++goodies;
  648.             break;
  649.         }
  650.         
  651.     return goodies;
  652. }
  653.  
  654. /********************* PAPSocketDomain member **********************/
  655.  
  656. extern "C" void GUSIwithPAPSockets()
  657. {
  658.     PAPSockets.DontStrip();
  659. }
  660.  
  661. Socket * PAPSocketDomain::open(const char * filename, int)
  662. {
  663.     Socket *    sock;
  664.     
  665.     if (equalstring((char *) filename, (char *) "printer", false, true)) {
  666.         errno = 0;
  667.         sock     = new PAPSocket();
  668.         
  669.         if (sock && errno) {
  670.             delete sock;
  671.             
  672.             return nil;
  673.         } else
  674.             return sock;
  675.     } else
  676.         return (Socket *) TryNextDevice;
  677. }
  678.