home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 164.lha / IPC / DemoSources / Pserver.c < prev    next >
C/C++ Source or Header  |  1988-04-28  |  23KB  |  727 lines

  1. /************************************************************
  2.  *                                                          *
  3.  *         Print Formatting Server (demo) for IPC           *
  4.  *                                                          *
  5.  *               Pete Goodeve 88:7:25                       *
  6.  *                                                          *
  7.  *  [This module has only been compiled under Lattice;      *
  8.  *   it will need some modification for Manx/Aztec]         *
  9.  *                                                          *
  10.  *                                                          *
  11.  *  This is a server to handle 'printf' conversion of       *
  12.  *  int, float, string, and char valued message items.      *
  13.  *  This saves space in other IPC modules that have a       *
  14.  *  need for text output.  A single message results in      *
  15.  *  a single output string, but there can be any number     *
  16.  *  of items in the message, converted in sequence to       *
  17.  *  ASCII -- either under the control of format specifier   *
  18.  *  items, or in default format if these are omitted.       *
  19.  *                                                          *
  20.  *  The output string will by default be displayed in       *
  21.  *  the server's window (thus other modules need not have   *
  22.  *  their own).  Optional message items will redirect       *
  23.  *  the output either to a supplied file handle (FILH) or   *
  24.  *  to be returned as an item in the reply message (RETS).  *
  25.  *                                                          *
  26.  *  Only one message ID (aside from QUIT) is recognized     *
  27.  *  by this server: CNVA ("CoNVert to Ascii").  See below   *
  28.  *  for item IDs.  The program will terminate when it       *
  29.  *  receives a QUIT message (items ignored), or when        *
  30.  *  the number of clients drops from non-zero to zero       *
  31.  *  (in other words it will not exit if there are initially *
  32.  *  no clients).  It will also quit if it is sent a         *
  33.  *  cntrl-C break.                                          *
  34.  *                                                          *
  35.  *                                                          *
  36.  *  This is currently a completely "synchronous" server;    *
  37.  *  it processes each message completely before returning   *
  38.  *  it, and doesn't attempt to handle more than one at a    *
  39.  *  time.  It doesn't send out any messages of its own      *
  40.  *  either (i.e. it isn't a "manager"), but for the         *
  41.  *  purpose of illustration some dummy manager type code    *
  42.  *  has been included in the main loop, so you can see      *
  43.  *  the sort of extensions that would be needed for that.   *
  44.  *  The procreply() procedure -- here a dummy -- would      *
  45.  *  have to dispatch each reply message that arrived to     *
  46.  *  a suitable close-out procedure, then dispose of the     *
  47.  *  message and any associated memory.                      *
  48.  *                                                          *
  49.  ************************************************************/
  50.  
  51. #ifdef DEBUG /* set this to show messages being received and so on */
  52. #include "stdio.h"
  53. #endif
  54.  
  55. #include "IPC.h"
  56.  
  57. #include "exec/memory.h"
  58. #include "libraries/dos.h"
  59.  
  60. /*
  61.  *  Define the ID codes recognized by the print format server
  62.  *
  63.  *  (MAKE_ID is defined in IPC.h)
  64.  */
  65.  
  66. /* Message IDs: */
  67. #define CNVA  MAKE_ID('C','N','V','A')
  68.     /* CoNVert to Ascii */
  69. #define QUIT  MAKE_ID('Q','U','I','T')
  70.  
  71. /* Item IDs: */
  72. #define RETS  MAKE_ID('R','E','T','S')
  73.     /* RETurn String */
  74.  
  75. #define FILH  MAKE_ID('F','I','L','H')
  76.     /* FILe Handle */
  77.  
  78.  
  79. #define PATS  MAKE_ID('P','A','T','S')
  80.     /* PATtern String */
  81.     /* NOTE: the total formatted length for a PATS item
  82.        must not be longer than 255 characters, to avoid crashing! */
  83.  
  84. #define PAT1  MAKE_ID('P','A','T','1')
  85.     /* PATtern -- 1 item */
  86.     /* NOTE: the total formatted length for a single PAT1 item
  87.        must not be longer than 65 characters, to avoid crashing! */
  88.  
  89. #define LINE  MAKE_ID('L','I','N','E')
  90.     /* indicates a complete line of text -- omitting newline */
  91.  
  92. #define TEXT  MAKE_ID('T','E','X','T')
  93.     /* Text block -- may include newlines */
  94.  
  95. #define STRG  MAKE_ID('S','T','R','G')
  96.     /* general non-specific ASCII STRinG */
  97.  
  98.     /* The above three categories are treated identically by Pserver
  99.        -- they may have distinct meanings to other servers */
  100.  
  101. #define CHAR  MAKE_ID('C','H','A','R')
  102.     /* A single character in L.S byte of ii_Ptr */
  103.  
  104. #define INTG  MAKE_ID('I','N','T','G')
  105.     /* A 32-bit INTeGer in ii_Ptr */
  106.  
  107. #define REAL  MAKE_ID('R','E','A','L')
  108.     /* A 32-bit floating point value in ii_Ptr (care in conversion!) */
  109.  
  110.  
  111. /*******************
  112. rather than the above if you prefer, with Lattice 4.0 you can simply
  113. use 4-character constants as in the following (compile with the -cm option):
  114.  
  115. #define CNVA 'CNVA'
  116. #define QUIT 'QUIT'
  117.  
  118. #define RETS 'RETS'
  119. #define FILH 'FILH'
  120.  
  121. #define PATS 'PATS'
  122. #define PAT1 'PAT1'
  123. #define LINE 'LINE'
  124. #define TEXT 'TEXT'
  125. #define STRG 'STRG'
  126. #define CHAR 'CHAR'
  127. #define INTG 'INTG'
  128. #define REAL 'REAL'
  129. *********************/
  130.  
  131.  
  132.  
  133. struct IPCPort *import=NULL; /* this is the port we serve */
  134.  
  135. struct IPCMessage *imsg=NULL; /* we only handle one message at a time,
  136.                                  so a global pointer is useful */
  137.  
  138. struct MsgPort *report=NULL; /* reply port: not actually  used here
  139.                                 -- skeleton code is shown */
  140.  
  141. /* should really include proto.h now for system procedures (next time...) */
  142. char * AllocMem(int, ULONG);
  143. void FreeMem(char *, int);
  144. struct MsgPort * CreatePort(char *, int);
  145. struct Message * Getmsg(struct MsgPort *);
  146.  
  147. void Cleanup();
  148. void clearmarkers();
  149. void outputstr(char *);
  150. void procline();
  151. void baditem(struct IPCItem *, ULONG);
  152.  
  153.  
  154. int active = TRUE, /* when this goes FALSE, it's time to quit */
  155.     replies = 0; /* number of replies to get back (...if we got replies!) */
  156.  
  157. ULONG reportsig = 0,  /* signal masks for ports */
  158.       importsig = 0;
  159.  
  160. ULONG outputhnd; /* file handle from message (if any) will be held here */
  161. struct IPCItem *retitem; /* item to contain return string (if any) */
  162.  
  163. int total_length, /* length computed for output string in pre-scan */
  164.     alloc_length; /* length of allocated block (if any)  -- acts as flag */
  165.  
  166. char *linebuf, *bufptr; /* pointers (fixed and moveable) to output string */
  167.  
  168. int mesg_bad; /* flag to prevent further processing if a bad item found */
  169.               /* (note that extraneous items are mostly just ignored) */
  170.  
  171.  
  172. /***************************
  173.  *
  174.  *  Main program entry point:
  175.  *
  176.  *  -- Note that to save overhead we use '_main'; you should also compile
  177.  *  (under Lattice) using the -v switch, to suppress stack checking and
  178.  *  the associated baggage.
  179.  *
  180.  *  We avoid using any C level I/O also -- just AmigaDOS calls, so we
  181.  *  dont need <stdio.h>.
  182.  */
  183.  
  184. void _main()
  185. {
  186.     int clients, oldclients = 0;
  187.                    /* keeps track of number of current clients */
  188.     ULONG sigset;  /* set to signals that woke up Wait() */
  189.  
  190. #ifdef TRACKCLIENTS /* define this to display current number of clients */
  191.     char clrepstr[16];
  192. #endif
  193.  
  194.     report = CreatePort(NULL,0); /* I repeat... this is just a dummy here */
  195.     if (!report) {
  196.         outputstr("no space!!");
  197.         Cleanup();
  198.         return;
  199.     }
  200.     import = ServeIPCPort("Print_Format");
  201.     if (!import) {
  202.         outputstr("Print Server already exists ... exiting");
  203.         Cleanup();
  204.         return;
  205.     }
  206.     /* Get the signal bits for each port for later convenience: */
  207.     /* (Note that, because we did not include IPCPorts.h, IPCPorts
  208.         are identical to MsgPorts as far as the user is concerned;
  209.         if we DID need IPCPorts.h, these statements would have to
  210.         be changed appropriately.) */
  211.     reportsig = 1<<report->mp_SigBit;
  212.     importsig = 1<<import->mp_SigBit;
  213.  
  214. #ifdef DEBUG
  215.     setnbf(stdout); /* so we can see output! (unbuffered) */
  216. #endif
  217.  
  218.  
  219.     /*
  220.      *  The main loop:
  221.      *  -- first we process any outstanding messages, looping until
  222.      *  no more are found (procreply() always fails -- there are no
  223.      *  replies in this program!).
  224.      *  -- then we check the number of current clients: if they have
  225.      *  all gone, we will exit.
  226.      */
  227.     do {
  228.         while ( procimsg() || procreply()) ;  /* loop to satisfy messages */
  229.  
  230.         /*
  231.          *  look at the number of clients (optional code included to display
  232.          *  this).  The number returned by CheckIPCPort includes the server,
  233.          *  so for convenience we subtract 1.
  234.          *  Note that we set IPP_NOTIFY, so the process gets woken up each
  235.          *  time the number of clients changes.
  236.          */
  237.         if ((clients = CheckIPCPort(import, IPP_NOTIFY) - 1)
  238.                    != oldclients) {
  239. #ifdef TRACKCLIENTS /* for demonstration purposes... */
  240.             sprintf(clrepstr,
  241.                     (clients == 1? "1 client\n" : "%d clients\n"), clients);
  242.             outputstr(clrepstr);
  243. #endif
  244.             if (!clients) {
  245.                 active = FALSE; /* quit if everyone gone */
  246.                 ShutIPCPort(import); /* note: multiple calls don't hurt! */
  247.                 continue; /* so we clear out any messages that sneak in */
  248.             }
  249.             oldclients = clients;
  250.         }
  251.  
  252.         /*
  253.          *  Now wait for further messages, unless 'active' is FALSE
  254.          *  ('replies' is always FALSE in this program).
  255.          */
  256.         if (active | replies) {
  257.             /* Note that Wait() must be used rather than WaitPort()
  258.                if we want to wake up on IPP_NOTIFY as well as messages */
  259.             sigset = Wait(importsig | reportsig | SIGBREAKF_CTRL_C);
  260.             if (sigset & SIGBREAKF_CTRL_C) {
  261.                 active = FALSE;
  262.                 ShutIPCPort(import); /* note: multiple calls don't hurt! */
  263.                 continue; /* so we clear out any messages that sneak in */
  264.             }
  265.         }
  266.     } while (active | replies);
  267.     /*** end of main loop ***/
  268.  
  269.     outputstr("Pserver terminating...\n");
  270.  
  271.     Cleanup();
  272. }
  273. /*** end of _main ***/
  274.  
  275.  
  276. void Cleanup()
  277. {
  278.     if (import) LeaveIPCPort(import);
  279.     if (report) DeletePort(report);
  280. }
  281.  
  282.  
  283. int itemn;  /* global item counter */
  284. struct IPCItem *curitem; /* global item pointer */
  285.  
  286.  
  287. /*
  288.  *  Process incoming messages
  289.  *  -- returns FALSE if there are none, otherwise TRUE.
  290.  *  It recognizes the message ID and invokes the appropriate
  291.  *  handling procedures.
  292.  */
  293. procimsg()
  294. {
  295.     if (!(imsg = (struct IPCMessage *) GetMsg(import))) return FALSE;
  296. #ifdef DEBUG
  297.     printf("item count = %d\n", imsg->ipc_ItemCount);
  298. #endif
  299.     switch (imsg->ipc_Id) {
  300.        case CNVA:
  301.                /*
  302.                 *   First do a scan of the message to determine how
  303.                 *   big a string space will be needed
  304.                 *   (and check for bad items):
  305.                 */
  306.                curitem = imsg->ipc_Items; /* initialize the item pointer */
  307.                for (itemn=imsg->ipc_ItemCount;
  308.                     itemn && scanitem(); curitem++, itemn-- ) /* loop */;
  309.  
  310.                if (mesg_bad) break;
  311.  
  312.                /*
  313.                 *   Allocate the space needed:
  314.                 */
  315.                if (!allocline()) break;
  316.  
  317.                /*
  318.                 *   Now actually process the items in the message:
  319.                 */
  320.                curitem = imsg->ipc_Items; /* reset again */
  321.                for (itemn=imsg->ipc_ItemCount;
  322.                     itemn && procitem(); curitem++, itemn-- ) /* loop */;
  323.  
  324.                /*
  325.                 *   Finally terminate the output string and handle
  326.                 *   it as directed:
  327.                 */
  328.                procline();
  329.                break; /* end of CNVA processing */
  330.  
  331.        case QUIT:
  332.                active = FALSE;
  333.                ShutIPCPort(import);
  334.                outputstr("Pserver got QUIT message...");
  335.                break;
  336.  
  337.        default:
  338. #ifdef DEBUG
  339.                outputstr("got bad message");
  340. #endif
  341.                imsg->ipc_Flags |= IPC_NOTKNOWN;
  342.                break;
  343.     }
  344.  
  345.     if (mesg_bad)
  346.         imsg->ipc_Flags |= IPC_NOTKNOWN | IPC_FAILED;
  347.     ReplyMsg(imsg);
  348.     clearmarkers(); /* reset things for the next message */
  349.     return TRUE;
  350. }
  351.  
  352.  
  353. /*
  354.  *  Skeleton procedure for handling replies (of which there are none
  355.  *  in this program...).
  356.  */
  357. procreply()
  358. {
  359.     struct IPCMessage *rpmsg;
  360.     struct IPCItem *item;
  361.  
  362.     if (!(rpmsg = (struct IPCMessage *)GetMsg(report))) return FALSE;
  363.     if (rpmsg->ipc_Flags & IPC_NOTKNOWN) {
  364. #ifdef DEBUG
  365.         outputstr("\nServer didn't like this message...");
  366. #endif
  367.     }
  368.     item = rpmsg->ipc_Items;
  369.     /* message deletion and so on would go here... */
  370.     replies--; /* This variable is incremented for each original message
  371.                   generated by this program; the program will not exit
  372.                   as long as it is non-zero */
  373.     return TRUE;
  374. }
  375.  
  376.  
  377. /*
  378.  *  Reset all global variables ready for next incoming message
  379.  */
  380. void clearmarkers()
  381. {
  382.     if (alloc_length) FreeMem(linebuf, alloc_length);
  383.     linebuf = NULL;
  384.     alloc_length = 0;
  385.     total_length = 0;
  386.     retitem = NULL;
  387.     outputhnd = NULL;
  388.     mesg_bad = FALSE;
  389. }
  390.  
  391.  
  392. /*
  393.  *  Scan the current item to determine length of string it will generate.
  394.  *  It also recognizes disposition control items (RETS, FILH) and sets up
  395.  *  the required pointers.
  396.  *  Subprocedures called may also check validity of item to be formatted
  397.  *  (extraneous items not following a format specifier are simply ignored.)
  398.  */
  399. scanitem()
  400. {
  401.     char dummy[66]; /* this should be enough (!) */
  402.  
  403. #ifdef DEBUG
  404.     debugitem(curitem,"scanitem:");
  405. #endif
  406.  
  407.             switch (curitem->ii_Id) {
  408.         case RETS:
  409.                 retitem = curitem;
  410.                 break;
  411.         case FILH:
  412.                 outputhnd = (ULONG)curitem->ii_Ptr;
  413.                 break;
  414.         case PATS:
  415.                 total_length += scanpatstring();
  416.                 return FALSE;   /* stop here */
  417.         case PAT1:
  418.                 total_length += patternitem(dummy);
  419.                 break;
  420.         case LINE:
  421.         case TEXT:
  422.         case STRG:
  423.                 total_length += strlen(curitem->ii_Ptr);
  424.                 break;
  425.         case CHAR:
  426.                 total_length++;
  427.                 break;
  428.         case INTG:
  429.                 total_length += cnvtitem(dummy, "%ld", curitem);
  430.                 break;
  431.         case REAL:
  432.                 total_length += cnvtitem(dummy, "%g", curitem);
  433.                 break;
  434.  
  435.         default:
  436. #ifdef DEBUG
  437.                 outputstr("got unknown item");
  438. #endif
  439.                 /* ignore extraneous stuff */
  440.             }
  441.         return (!mesg_bad); /* stops here if message has failed */
  442. }
  443.  
  444.  
  445. /*
  446.  *  Allocate space for complete output string.
  447.  *  -- if the message has supplied a NON-NULL ii_Ptr in a RETS item,
  448.  *  this will be used as the buffer, as long as the associated ii_Size
  449.  *  is large enough (if it is not, the message will fail).  Otherwise
  450.  *  space is allocated for the buffer.  If a NULL RETS item has been
  451.  *  supplied, the buffer pointer will be returned there (otherwise the
  452.  *  buffer will be freed again after output).
  453.  *  It will of course also fail if there is no space for a buffer.
  454.  *  Any failure returns FALSE, otherwise TRUE.
  455.  */
  456. allocline()
  457. {
  458.     int adj_length = total_length + 1; /* allow for terminator */
  459.     if (retitem) { /* check for a RETS item first */
  460.         if (retitem->ii_Ptr) { /* buffer supplied? */
  461.             if (retitem->ii_Size > total_length) { /* big enough ? */
  462.                 linebuf = (char *)retitem->ii_Ptr; /* yes -- use it */
  463.             }
  464.             else { /* too small */
  465.                 baditem(retitem, IPC_FAILED);
  466.                 return FALSE;
  467.             }
  468.         }
  469.         else { /* NULL pointer, so create some space */
  470.             linebuf = AllocMem(adj_length, MEMF_PUBLIC);
  471.             if (!linebuf) {
  472.                 baditem(retitem, IPC_FAILED);
  473.                 return FALSE;
  474.             }
  475.             else {
  476.                 retitem->ii_Ptr = (void *)linebuf;
  477.                 retitem->ii_Size = adj_length;
  478.                 retitem->ii_Flags = IPC_TRANSFER | IPC_MODIFIED;
  479.                     /* flags are set to indicate client must dispose
  480.                        of this block */
  481.             }
  482.         }
  483.     }
  484.     else { /* no RETS item, so use local buffer */
  485.         linebuf = AllocMem(adj_length, MEMF_PUBLIC);
  486.         if (!linebuf) {
  487.             imsg->ipc_Flags |= IPC_NOTKNOWN | IPC_FAILED;
  488.             return FALSE;
  489.         }
  490.         alloc_length = adj_length; /* used to free the message afterward */
  491.     }
  492.     bufptr = linebuf; /* initialize the output pointer */
  493.     return TRUE;
  494. }
  495.  
  496.  
  497. /*
  498.  *  Write the ASCII string for the current item to the output buffer.
  499.  *  A suitable default format is used for items with no preceding
  500.  *  format specifier.
  501.  */
  502. procitem()
  503. {
  504.  
  505. #ifdef DEBUG
  506.     debugitem(curitem,"procitem:");
  507. #endif
  508.             switch (curitem->ii_Id) {
  509.         case RETS:
  510.         case FILH:
  511.                 break;
  512.         case PATS:
  513.                 bufptr +=cnvtpatstring(bufptr);
  514.                 return FALSE;  /* no further items allowed */
  515.         case PAT1:
  516.                 bufptr += patternitem(bufptr);
  517.                 break;
  518.         case LINE:
  519.         case TEXT:
  520.         case STRG:
  521.                 strcpy(bufptr, curitem->ii_Ptr);
  522.                 bufptr += strlen(curitem->ii_Ptr);
  523.                 break;
  524.         case CHAR:
  525.                 *bufptr++ = (char) (curitem->ii_Ptr);
  526.                     /* Lattice gives a warning here but does it OK */
  527.                 break;
  528.         case INTG:
  529.                 bufptr += cnvtitem(bufptr, "%ld", curitem);
  530.                 break;
  531.         case REAL:
  532.                 bufptr += cnvtitem(bufptr, "%g", curitem);
  533.                 break;
  534.  
  535.         default:
  536.                 /* ignore */
  537.             }
  538.         return TRUE;
  539. }
  540.  
  541.  
  542. /*
  543.  *  Determine length (and validity) of multi-item format specifier
  544.  */
  545. scanpatstring()
  546. {
  547.     char *dummy;
  548.     int len;
  549.     dummy = AllocMem(256, 0L); /* temporary local storage */
  550.     if (!dummy)
  551.         return 256;  /* let allocline fail instead ! */
  552.     len = cnvtpatstring(dummy);
  553.     FreeMem(dummy, 256);
  554.     return len;
  555. }
  556.  
  557.  
  558. /*
  559.  *  Use the format in a PAT1 item to convert the following item.
  560.  *  (Simply calls cnvtitem with suitable arguments.)
  561.  */
  562. patternitem(destptr)
  563. char *destptr;
  564. {
  565.     char *fmtp;
  566.     if (--itemn) {
  567.         fmtp = (char *)curitem->ii_Ptr;
  568.         return cnvtitem(destptr, fmtp, ++curitem);
  569.     }
  570.     else {
  571.         baditem(curitem, IPC_FAILED);
  572.         mesg_bad = TRUE;
  573.         return 0;
  574.     }
  575. }
  576.  
  577.  
  578. /*
  579.  *  Convert a single value (in the current item) according to the
  580.  *  format specified.  Output is to destptr; the size of the resulting
  581.  *  string is returned.
  582.  */
  583. cnvtitem(destptr, fmtstr, item)
  584. char *destptr, *fmtstr;
  585. struct IPCItem *item;
  586. {
  587.     double doubleval;
  588.     int len;
  589.  
  590. #ifdef DEBUG
  591.     debugitem(item,"cnvtitem:");
  592. #endif
  593.  
  594.             switch (item->ii_Id) { /* handle according to type */
  595.         case LINE:
  596.         case TEXT:
  597.         case STRG:
  598.                 len = sprintf(destptr, fmtstr, item->ii_Ptr);
  599.                 break;
  600.         case CHAR:
  601.                 len = sprintf(destptr, fmtstr, item->ii_Ptr);
  602.                 break;
  603.         case INTG:
  604.                 len = sprintf(destptr, fmtstr, item->ii_Ptr);
  605.                 break;
  606.         case REAL:
  607.                 doubleval = *(float *) &item->ii_Ptr;
  608.                 len = sprintf(destptr, fmtstr, doubleval);
  609.                 break;
  610.  
  611.         default:
  612.                 baditem(item, IPC_FAILED);
  613.                 mesg_bad = TRUE;
  614.             }
  615.         return len;
  616. }
  617.  
  618.  
  619. /*
  620.  *  Convert items to ASCII according to format pattern in the current item.
  621.  *  All the remaining items in the message must be values to satisfy the
  622.  *  pattern.  (Note that REAL items aren't allowed, because they can't be
  623.  *  passed to sprintf as 32-bit values).  Up to 10 items can be handled.
  624.  *  The total length of the formatted string is returned.
  625.  */
  626. cnvtpatstring(destptr)
  627. char *destptr; /* destination string buffer */
  628. {
  629.     char *fmtp;
  630.     int i;
  631.      ULONG p[10]; /* Storage for 10 values */
  632.  
  633.     fmtp = (char *)curitem->ii_Ptr; /* hold pointer to pattern string */
  634.     for (i=0; i<10 && --itemn; i++ ) { /* process all the remaining items */
  635.         ++curitem;
  636. #ifdef DEBUG
  637.     debugitem(curitem,"patstring:");
  638. #endif
  639.         switch (curitem->ii_Id) { /* everything the same except REAL */
  640.         case LINE:
  641.         case TEXT:
  642.         case STRG:
  643.         case CHAR:
  644.         case INTG:
  645.                 p[i] = (ULONG)curitem->ii_Ptr; /* local copy */
  646.                 break;
  647.         case REAL: /* can't pass a double this way!! */
  648.         default:
  649.                 baditem(curitem, IPC_FAILED);
  650.                 mesg_bad = TRUE; /* can't continue */
  651.                 return 0;
  652.         }
  653.     }
  654.     if (itemn) { /* more than 10 items found */
  655.         baditem(++curitem, IPC_FAILED);
  656.         mesg_bad = TRUE;
  657.         return 0;
  658.     }
  659.     while (i<10) p[i++] = (ULONG) "\0"; /* not very adequate protection */
  660.  
  661.     /* Fortunately C allows us to pass unused arguments: */
  662.     return sprintf(destptr, fmtp,
  663.            p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
  664. }
  665.  
  666.  
  667. /*
  668.  *  Process the assembled output string
  669.  *  -- if the output handle has been supplied, the string will be sent there;
  670.  *  if neither handle nor return slot has been supplied, the string will
  671.  *  be output to the server's window.
  672.  *  (If a return slot is present in the message, the string will be returned
  673.  *  there in any case.)
  674.  */
  675. void procline()
  676. {
  677.     *bufptr = '\0'; /* Terminate the string first */
  678.     if (outputhnd) Write(outputhnd, linebuf, total_length);
  679.     else if (!retitem)
  680.          Write(Output(), linebuf, total_length);
  681. }
  682.  
  683.  
  684. /*
  685.  * Set error flags in an item and the message
  686.  * (Note that this doesn't abort processing -- if this is needed,
  687.  * the mesg_bad flag should also be set)
  688.  */
  689. void baditem(item, extraflags)
  690.     struct IPCItem *item;
  691.     ULONG extraflags;
  692. {
  693.     imsg->ipc_Flags |= IPC_CHECKITEM;
  694.     item->ii_Flags |= IPC_NOTKNOWN | extraflags;
  695. #ifdef DEBUG
  696.     debugitem(item, "BAD:");
  697. #endif
  698. }
  699.  
  700.  
  701. /* procedure to display string in window (avoids C I/O overhead) */
  702.  
  703. void outputstr(str) char *str;
  704. {
  705.     Write(Output(), str, strlen(str));
  706. }
  707.  
  708. /************************************************************?
  709.  
  710. /* the following is only included if you want debugging output: */
  711. #ifdef DEBUG
  712. debugitem(item, str)
  713.     struct IPCItem *item;
  714.     char *str;
  715. {
  716.     ULONG icode[2];
  717.     icode[0] = item->ii_Id;
  718.     icode[1] = 0;
  719.     printf("%s item %d code %s [%x] ptr %x flagged %x\n",
  720.            str, item - imsg->ipc_Items,
  721.            icode, *icode,
  722.            item->ii_Ptr, item->ii_Flags);
  723. }
  724. #endif
  725.  
  726.  
  727.