home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d168 / dillonstuff.lha / doc / dres / ipc.doc next >
Text File  |  1988-11-22  |  25KB  |  563 lines

  1.  
  2.             IPC.DOC  (DRES.LIBRARY)
  3.  
  4.                 Matthew Dillon
  5.               25 September 1988
  6.  
  7.  
  8.     Special note.  I've tried my best, but this document is somewhat
  9.     lacking in the exact mechanics and implementation of IPC.  Please
  10.     refer to the source module to resolve such questions.
  11.  
  12.     The IPC section of the DRES.LIBRARY pertains to the sending of commands
  13. and retrieval of results between arbitrary processes without conflict.
  14. Processes which use IPC come in three flavors:
  15.  
  16.     (1) A process which is able to accept and process IPC commands
  17.     (2) A process which might send IPC commands to other processes
  18.     (3) A process which does both.
  19.  
  20.     The IPC mechanism implemented by this library is inherently asychronous
  21. in nature (messages and ports), but also provides a synchronous call
  22. specially tailored for cases (2) and (3).  Doing IPC commands synchronously
  23. has certain advantages, the greatest of which is that the implementation by
  24. application software is greatly simplified.  The synchronous call gets
  25. around possible deadlocks in case (3) by interrupting itself and calling a
  26. handler if incomming messages occur while it is waiting for the sent
  27. message to complete.
  28.  
  29.     The greatest feature of this IPC mechanism is a great flexibility in
  30. who allocates and deallocates the message buffers.  Both the source and
  31. destination processes have a wide range of options concerning message
  32. buffers.  Unless otherwise specified, an allocated copy of the message
  33. buffer is automatically made and used which allows the send or reply message
  34. buffer used by the process to be immediately discarded.  Additionaly, a
  35. destination IPCPort can be flagged to force incomming messages to be
  36. allocated no matter what the sender requests.
  37.  
  38.     NOTE:   The term 'allocation of messages' inherently means that if the
  39. message is allocated, the user of the message can munge it however he likes
  40. (exception: IF_GLOBAL messages that are propogated through all IPC ports).
  41. Allocated messages are automatically FreeMem()d by the IPC subsystem or
  42. by the receiver.   Also, WHEN DUPLICATION OF MESSAGES OCCUR, THE ALLOCATED
  43. MEMORY IS OF THE SAME TYPE AS THE ORIGINAL BUFFER.
  44.  
  45.     NOTE:   Do not get confused by the many different ways messages buffers
  46. can be allocated.  A message buffer will never be allocated-duplicate more
  47. than once.  For example, if the source specifies that it is passing an
  48. allocated buffer, the further duplication is done by the IPC subsystem.
  49.  
  50.     -The sender can allocate the message buffer himself and pass that,
  51.      pass a temporary buffer which the IPC subsystem duplicates, or pass
  52.      a static buffer to the destination without the overhead of allocation
  53.      and duplication.
  54.  
  55.     -The receiver of a particular message can determine whether that message
  56.      was allocated and FreeMem() it himself, can allow the IPC subsystem
  57.      to free allocated messages, and can even force all incomming messages
  58.      on his IPC port to be allocated no matter what the sender specifies.
  59.      Forcing the IPC subsystem to allocate otherwise static messages is
  60.      a very useful ability to have.  For instance, the parser employed by
  61.      the receiver of a message might destroy or modify the message buffer
  62.      during parser, while the original sender of the message might have
  63.      used a static string or otherwise expected the buffer not to be
  64.      screwed up.  By the receiver being able to specify that he WANTS the
  65.      buffer to be an allocated duplicate because he intends to modify it
  66.      without permission is a good thing.  Even though the sender passes
  67.      a static buffer to the IPC subsystem, that subsystem will allocate
  68.      and duplicate it for the receiver.
  69.  
  70.     -In his reply to the sender, the receiver has the same sort of control
  71.      as sender in terms of how the reply message buffer is allocated.  After
  72.      getting the reply back, the original sender can determine whether the
  73.      reply buffer was allocated and FreeMem() it himself, or allow the IPC
  74.      subsystem to free it when he is through with the reply.
  75.  
  76.     -Finally, the receiver of the original message can even free his own
  77.      reply buffer when the original sender is through with it!
  78.  
  79.     So in general, one can use either the IPC subsystem's automatic buffer
  80. copying feature (the default), use no buffer copying at all, or use one's
  81. own scheme.
  82.  
  83.     MOST OF THESE ALLOCATION FEATURES ARE IMPLEMENTED THROUGH FLAGS.  READ
  84. THE DOCS BELOW ON OPENIPC() and SENDIPC().
  85.  
  86.  
  87.                APPLICATION NAME DOMAINS
  88.  
  89.     An application for cases (1) or (3) must create a receiver port for
  90. IPC messages with the OpenIPC() call.  OpenIPC() takes a name and flags
  91. argument.  The Flags argument is discussed in OpenIPC() below.  You do
  92. not need a port to send messages, only to receive them.
  93.  
  94.     The name of the port determines the type of messages it will receive.
  95. The format is:    <application_name>.<domain> , for example: "dmouse.CMD"
  96. Currently, only the .CMD domain has a defined standard.  It does not matter
  97. whether multiple copies of the application are running, they can share the
  98. same port name.  You could have, say, two invocations of your favorite
  99. editor each with five active projects.    Support for this is discussed in
  100. the ReplyIPC() call below.
  101.  
  102.     While this port exists, other tasks may send IPC messages to it.  You
  103. must extract the messages (GetMsg()), process it, and then ReplyIPC() the
  104. message to return it to the original sender.
  105.  
  106.     The CloseIPC() call will shutdown an IPC port that you had previously
  107. openning and return any pending messages by trying to pass them to the
  108. next application of the same name (if none, an error is eventually returned
  109. to the sender).
  110.  
  111.                 .CMD ports
  112.  
  113.     Messages sent over .CMD ports are formatted as null-terminated
  114. ascii-text.  The send buffer (msg->TBuf) is formatted as follows:
  115.  
  116.     project\0command\0        (\0 = NUL = ascii 0)
  117.  
  118.     The \0 MUST be included as part of the buffer and buffer length.  The
  119. project name allows the application to identify which of its possibly
  120. multiple projects is to be affected by the command.  a NULL project name
  121. (\0command\0) is allowed and refers to the 'active' project in the
  122. application.  The project name is ignored by applications which do not
  123. support the notion of projects. The format of the command depends on the
  124. application, but must be in ascii and terminate with a \0 (ascii 0).
  125.  
  126.     The logical extension for the specification of multiple project names
  127. is to allow comma delimited names and wildcards (* and ?).  For example,
  128. "*\0command\0" would refer to all projects, "a.c,b.c\0command\0" would
  129. refer to two specific projects.  This might not be supported by all
  130. applications.
  131.  
  132.     The format of the reply buffer depends on the success of the operation.
  133. If the command was successful, the reply buffer will either be NULL or
  134. contain some ascii/text response ending in \0.    If the command was not
  135. successful, IF_ERROR will be set in msg->RFlags and the reply buffer will
  136. be either NULL or contain some ascii/text error message ending in \0.
  137. The msg->Error integer error code will be non-zero if IF_ERROR was set,
  138. possibly non-zero if IF_ERROR was not set to indicate a warning (IF_ERROR
  139. always means Total-Failure).   If msg->Error is not set by the application
  140. which returns the error condition, it will be set by the IPC subsystem.
  141.  
  142.  
  143.  
  144. ParseCmd                            ParseCmd
  145.  
  146.     argc = ParseCmd(buf, &argv, varget, varfree, &error, NULL);
  147.     int argc;
  148.     char **argv;        (note, address passed)
  149.     char *((*varget)());
  150.     void (*varfree)();
  151.     long error;         (note, address passed)
  152.  
  153.     (note:  Last NULL argument (0L) is required for future compatibility).
  154.  
  155.     THIS COMMAND CAN EASILY BE USED WITH NON-IPC RELATED ROUTINES!
  156.  
  157.     Do DME-type parsing on a string buffer.  The buffer is terminated with
  158.     a \0 but may contain a \n at the end (which is ignored) to be compatible
  159.     with fgets() as well as gets().  NOTE that if the string is terminated
  160.     with a \<LF><NUL> i.e. "\\n\0", the backslash will override the standard
  161.     ignorance of the \n and parse it into the result.
  162.  
  163.     DME-type parsing delimites fields by spaces except those enclosed in
  164.     matching parenthesis.  Multiple levels of parenthesis is tracked and
  165.     the OUTERMOST SET REMOVED.    Other special characters include shift-6
  166.     (^), backslash (\), and dollar ($):
  167.  
  168.      form        examples        function
  169.  
  170.     ^<char>     ^c ^d ^L        embed a control character, case is ignored
  171.                     you may not embed ^@ (0)
  172.  
  173.     \<char>     \\ \( \^        override the meaning of the next character,
  174.                     allows embedding special characters.
  175.  
  176.     $(varname)                  inline string variable, var name can be
  177.                     anything except ')'.
  178.  
  179.     $varname            inline string variable, var name
  180.                     restricted to alphanumeric and '_'.
  181.  
  182.     Example:    "echo (this (is a) test) $file/x a mess ^g \(hi! \)ug\\"
  183.     Result: argv[0] =    "echo"
  184.         argv[1] =    "this (is a) test"
  185.         argv[2] =    "dnet:foo/x"        (assumes $file == 'dnet:foo')
  186.         argv[3] =    "a"
  187.         argv[4] =    "mess"
  188.         argv[5] =    "<CONTROL-G>"       (i.e. ascii code 7)
  189.         argv[6] =    "(hi!"
  190.         argv[7] =    ")ug\"
  191.         argv[8] =    NULL
  192.  
  193.     Note: offset -1 of each argument contains a status byte.  Currently,
  194.     bit 0 is defined to indicate whether the argument was paren'd or not.
  195.     This cannot otherwise be told as the highest level of parenthesis are
  196.     stripped.    (argv[0][-1] & 1) == 0, (argv[1][-1] & 1) == 1.
  197.  
  198.                 --------
  199.  
  200.     buf     -\0 or \n\0 terminated buffer holding the command to parse.
  201.          The buffer is not modified and may be discarded after the
  202.          call returns.
  203.  
  204.     &argv    -address of a variable which will be initialized to point
  205.          to an allocated array of pointers to strings exactly the
  206.          way the main() argv works.
  207.  
  208.     varget    -Pointer to a subroutine which when given a \0 terminated
  209.          variable name will return a \0 terminated string or NULL
  210.          if that variable does not exist.
  211.  
  212.          varget(varname)
  213.          char *varname;
  214.  
  215.          This argument may be NULL indicating string variables are
  216.          not supported.  The routine may return non-allocated
  217.          strings (i.e. static storage or string constants) in which
  218.          case there is no need for a varfree() function.
  219.  
  220.     varfree    -Pointer to a subroutine which when given the string
  221.          pointer returned by varget() will free it.  This call
  222.          is made immediately after varget() is called and returns
  223.          nothing.  You may pass NULL for this argument if no free
  224.          function exists (varget was NULL or returned static
  225.          strings).
  226.  
  227.     &error    -You must supply a pointer to a longword which will be
  228.          returned holding the error, if any, and the index where
  229.          the error occured, if any.  The index is stored in the
  230.          lower 16 bits and the error # stored in the upper 16 bits:
  231.  
  232.          1 << 16  = NO MEMORY
  233.          2 << 16  = STRING VARIABLE NOT FOUND (varget() returned
  234.                 NULL), the lower 16 bits contain the index into
  235.                 buf.
  236.  
  237.     NULL    -This argument is reserved for future expansion and must
  238.          be NULL (0L).
  239.  
  240.     NOTE:   The returned arg list may be munged however you wish, including
  241.         the pointers themselves, just as long as you pass the original
  242.         argv pointer (i.e. the entries may be munged) to FreeParseCmd().
  243.  
  244.         0 is always returned on error, and error will be set properly.
  245.         Otherwise, argc is returned and error will hold 0.
  246.  
  247.         The original buffer is not modified in any way.
  248.  
  249.     NOTE:   Since the outer set of parenthesis () are stripped, offset -1
  250.         of each arguments provides information as to whether the
  251.         argument was quoted or not.  If bit 0 is set, the argument
  252.         was quoted,  I.E.:        (a) b c
  253.         (av[0][-1] & 1) = 1     av[0] = "a"
  254.         (av[1][-1] & 1) = 0     av[1] = "b"
  255.         (av[2][-1] & 1) = 0     av[2] = "c"
  256.  
  257.  
  258. FreeParseCmd                            FreeParseCmd
  259.  
  260.     (void) FreeParseCmd(argv)
  261.  
  262.     This routine frees the storage referenced by the argv... the argv array
  263.     and the original strings are all freed.  It is ok if Individual entries
  264.     in the argv array are munged.
  265.  
  266.  
  267. OpenIPC                             OpenIPC
  268.  
  269.     port = OpenIPC(name, flags)
  270.     PORT *port;
  271.     char *name;
  272.     long flags;
  273.  
  274.     Create an IPC port of the specified name suitable for receiving IPC
  275.     commands on.  If the port could not be created due to lack of memory,
  276.     NULL is returned, else an EXEC port is returned.  The port is setup
  277.     as PA_SIGNAL and takes up a signal bit in your signal set.    If the
  278.     application does not wish a signal-port, it may FreeSignal() the
  279.     signal and change the flags to something other than PA_SIGNAL.
  280.  
  281.     Due to the fact that the signal was allocated for the calling task,
  282.     the port is normally usable only from the calling task.  One can
  283.     change the ownership and port characterstics but always remember that
  284.     CloseIPC() can only be called from the owning task.
  285.  
  286.     IF_ALLOC    If IF_ALLOC is specified, *ALL* incomming message buffers
  287.         will be forced to be allocated even if the sender of the
  288.         message specifies IF_NOCOPY.  I.E. you, as the receiver of
  289.         IPC messages on this port can specify this flag to guarentee
  290.         the msg->TBuf is allocated and thus modify the contents
  291.         of the buffer itself without confusing the original sender
  292.         (who may have specified a static string)
  293.  
  294.         Note:    However, if this message is to be passed on via
  295.         IF_GLOBAL or IF_NOTFND, you probably do not want to munge
  296.         the buffer.
  297.  
  298.     IF_GLOBAL    If IF_GLOBAL is specified, this port will accept global
  299.         messages.  Global messages are always of the .CMD form
  300.         though the sender could easily put stuff beyond the second
  301.         \0 in the message buffer destined for specialized routines.
  302.  
  303.         If not specified, IF_GLOBAL messages will NEVER propogate
  304.         through this port.
  305.  
  306.  
  307. CloseIPC                            CloseIPC
  308.  
  309.     (void) CloseIPC(port)
  310.     PORT *port;
  311.  
  312.     Delete an IPC port previously obtained by OpenIPC().  The owning task
  313.     must make this call since it will FreeSignal() the allocated signal.
  314.     Note that if the port flags are set to something other than PA_SIGNAL,
  315.     no attempt will be made to free the possibly non-existant signal bit.
  316.  
  317.     This call will pass any pending messages on to the next application
  318.     of the same name, and if no other exists, will return the messages
  319.     with an error.  Pending messages with IF_GLOBAL set continue their
  320.     journey through the IPC ports flagged to accept IF_GLOBAL messages.
  321.  
  322.  
  323. SendIPC                             SendIPC
  324.  
  325.     msg = SendIPC(appname, buf, len, flags)
  326.     IPCMSG *msg;
  327.     char *appname;
  328.     APTR buf;
  329.     long len, flags;
  330.  
  331.     This routine starts an asynchronous IPC request.  It allocates and
  332.     initializes an IPCMSG and reply port and sends the message to the
  333.     destination application (appname).  The IPCMSG is returned and it's
  334.     msg->RFlags holds the IF_ALLOCMSG internal flag to cause the IPCMSG
  335.     and replyport to be deallocated when you are through with the reply.
  336.     NOTE!  The returned IPCMSG has already been sent to the destination
  337.     and is not owned by you.  The idea is to then CheckMsg() and/or
  338.     WaitMsg() on it.  The ANode filed of the IPCMSG, however, *IS* owned
  339.     by you and you may use it however you wish.
  340.  
  341.     The buffer and buffer length you pass SendIPC() is used to allocate
  342.     a copy of the buffer (which is deallocated automatically when the
  343.     destination ReplyIPC()s).  This means that you can discard/reuse your
  344.     buffer as soon as the call returns.  However, if you specify the
  345.     IF_NOCOPY flag SendIPC() will use your buffer pointer and you must
  346.     be sure not to modify its contents until the message is returned to you.
  347.  
  348.     If you specify IF_ALLOC it is assumed your buffer pointer and length
  349.     were AllocMem()d and the system will FreeMem() them when the destination
  350.     is through processing your message and calls ReplyIPC().
  351.  
  352.     ** See SendIPC2() for info on how to handle the message when it comes
  353.     back.
  354.  
  355.     * If the 'name' is NULL, the first application in the application
  356.     list is called.  This is usually used in conjunction with IF_GLOBAL
  357.     to indicate that the message should propogate to all IPC ports in the
  358.     system.
  359.  
  360. SendIPC2                            SendIPC2
  361.  
  362.     msg = SendIPC2(name, msg)
  363.  
  364.     This routine is called by SendIPC().  This assumes you already have an
  365.     IPC message structure that you wish to use.  The msg argument is
  366.     returned.  The reply fields, including msg->Error and msg->Confirm,
  367.     are zerod.
  368.  
  369.     The IPCMSG must be initialized as follows:
  370.  
  371.     msg->Msg.mn_ReplyPort    -must hold reply port for the message
  372.     msg->Msg.mn_Length        -must hold length of IPCMSG structure ONLY
  373.                  if you had specified the IF_ALLOCMSG in
  374.                  msg->TFlags
  375.     msg->Msg.TBuf        -holds the buffer pointer to the message
  376.                  or NULL
  377.     msg->Msg.TLen        -holds the length of the message
  378.     msg->Msg.TFlags        -holds flags associated with the message:
  379.         IF_ALLOC        * automatic deallocation on reply.  This flag
  380.                   is set automatically if you do not specify
  381.                   IF_NOCOPY.  If NEITHER IF_NOCOPY or IF_ALLOC
  382.                   are specified, the IPC subsystem will
  383.                   allocate a duplicate of your buffer, place
  384.                   it in TBuf, and set this flag for you.
  385.         IF_GLOBAL        * force propogate to all applications in
  386.                   the system before returning to the sender.
  387.         IF_NOCOPY        * force IPC system to use the buffer you
  388.                   placed in TBuf.  Otherwise, the IPC system
  389.                   will allocate and copy your buffer into
  390.                   its own and place that in TBuf.
  391.  
  392.         IF_ALLOCMSG     * IPC system will deallocate the IPCMSG
  393.                   itself when FreeIPC() is called.  This
  394.                   flag is set automatically when the IPC
  395.                   system has allocate the IPCMSG in the
  396.                   first place.
  397.  
  398.     WARNING!    Upon return, msg->TBuf will not necessarily point to your
  399.         buffer, even if you specified IF_NOCOPY or IF_ALLOC.
  400.  
  401.            Handling the message when it comes back
  402.  
  403.     After you make the call, you must wait for the message to complete
  404.     via WaitMsg and/or CheckMsg().  Note that WaitMsg() will remove the
  405.     message from its reply port.  If you do not use WaitMsg(), you must
  406.     remember to remove the message before continuing.
  407.  
  408.     Then, process the reply (msg->RBuf, msg->RLen, and msg->RFlags).
  409.     The msg->RBuf IS ALLOWED TO BE NULL.  msg->RFlags will indicate
  410.     any errors:
  411.  
  412.     IF_ERROR|IF_NOTFND|IF_NOAPP:    no application of that name exists
  413.  
  414.     IF_ERROR|IF_NOTFND       :    application exists, but the requested
  415.                     project does not exist.  OR the
  416.                     command does not exist for this
  417.                     application.
  418.  
  419.     IF_ERROR            :    The application exists and can handle
  420.                     the project & command, but an error
  421.                     occured during interpretation of the
  422.                     command.
  423.  
  424.     IF_ALLOC            :    The reply buffer was allocated.  You
  425.                     have the option of FreeMem()ing it
  426.                     yourself or allowing the system to
  427.                     do so.    If you do it, you must set
  428.                     RBuf = NULL.
  429.  
  430.     NOTE:   msg->Error does not need to be set by the receiver.  If
  431.         msg->Error is 0 and IF_ERROR is set, msg->Error will be set to
  432.         20 by the IPC subsystem.
  433.  
  434.     NOTE:   In the case of IF_ERROR, the reply buffer might still be
  435.         non-NULL and contain an error message.
  436.  
  437.     When through dealing with the reply, you MUST call FreeIPC(msg).  This
  438.     routine will deallocate the reply buffer if msg->RFlags had IF_ALLOC
  439.     set by the destination and also free the message it self if msg->TFlags
  440.     contained IF_ALLOCMSG set by SendIPC() or the user before a SendIPC2().
  441.  
  442.     Additionaly, if msg->Confirm was set by the destination, this function
  443.     vector will be called with the message pointer as an argument to allow
  444.     the destination to deallocate its reply.  NOTE!!! If the message has
  445.     IF_GLOBAL set in msg->TFlags, msg->Confirm should NOT be touched by
  446.     receiving applications.
  447.  
  448.  
  449. DoIPC2                                DoIPC2
  450.  
  451.     (void)  DoIPC2(name, msg, collfunc, collport)
  452.     long flags
  453.     char *name;
  454.     IPCMSG *msg;
  455.     void (*collfunc)();
  456.     PORT *collport;
  457.  
  458.     This is the hall mark of the IPC library and allows one to send
  459.     SEMI-SYNCHRONOUS messages while still retaining the ability to
  460.     process incomming messages on your own IPC port (case #3).  The IPC
  461.     message msg must be setup as in SendIPC2().  The collision function
  462.     and collision port may be NULL if you wish.
  463.  
  464.     This command sends the message and then waits for the reply.  If while
  465.     waiting for the reply a message comes in on the collision port (usually
  466.     your IPC port), the collision vector will be called with the collision
  467.     port as an argument, allowing you to process incomming messages while
  468.     waiting for one you sent.  By properly designing your software, you
  469.     can allow multiply stacked DoIPC2() calls.
  470.  
  471.     When the message finally comes back, this call returns.  The application
  472.     must still deal with its reply buffer and call IPCFree() when through.
  473.  
  474.     NOTE: The collision function is called as a subroutine of this routine
  475.     and thus if it is a general command-handling routine remember that
  476.     your command interpreter might have to be synchronously reentrant.
  477.     DoIPC2() is certainly reentrant.  A4 and A5 are not screwed up by
  478.     DoIPC2() so you do not have to reload your data segment base register
  479.     from the collision vector routine if using the small data model.
  480.     (Compatible with both Aztec and Lattice C).
  481.  
  482.  
  483. ReplyIPC                            ReplyIPC
  484.  
  485.     (void) ReplyIPC(msg, buf, len, flags)
  486.     IPCMSG *msg;
  487.     APTR buf;        (may be NULL & len would then be 0)
  488.     long len;
  489.     long flags;
  490.  
  491.     You must reply to all IPC messages you receive on your IPC port by
  492.     calling ReplyIPC().  ReplyIPC() works almost exactly like IPCSend(),
  493.     but deals with the RBuf, RFlags, and RLen fields of the IPC structure.
  494.     Normally, the IPC system will allocate a duplicate of your reply buffer
  495.     and stick that in msg->RBuf which allows you to throw away your buffer
  496.     after calling ReplyIPC().  Various flags override this feature:
  497.  
  498.     IF_ALLOC        -    The buffer is automatically deallocated when the
  499.             source calls FreeIPC().  If this flag exists the
  500.             IPC system will not allocate a duplicate buffer.
  501.  
  502.     IF_NOCOPY        -    The IPC system will not allocate a duplicate buffer.
  503.  
  504.     Normally, (1) neither of these flags is specified, or (2) IF_ALLOC
  505.     is specified.  Specifying IF_ALLOC alone means that the IPC system
  506.     assumes you have AllocMem()d the buffer yourself and want it
  507.     automatically freed when the source (original sender) calls
  508.     FreeIPC().
  509.  
  510.     Specifying both IF_ALLOC and IF_NOCOPY is equivalent to specifying
  511.     just IF_ALLOC.    Specifying just IF_NOCOPY means that the IPC system
  512.     will use your buffer pointer and will NOT attempt to deallocate it.
  513.     In this case, you are responsible for keeping the buffer intact
  514.     until the source calls FreeIPC().  This is possible through the
  515.     use of the msg->Confirm vector.   ** you cannot use msg->Confirm
  516.     for IF_GLOBAL packets!    Because there is only one vector entry and
  517.     possibly multiple users of the message.
  518.  
  519.     NOTE:  The buffer pointer placed in msg->RBuf will either be yours
  520.     or the allocated duplicate.  msg->RBuf will be set to NULL when (if)
  521.     the system deallocates it.
  522.  
  523.     If you specify IF_NOTFND, your buffer and length are completely
  524.     ignored and the message is passed on to the next application program
  525.     with the same name.  IF THE ORIGINATOR SPECIFIED IF_GLOBAL WHEN HE
  526.     SENT THE MESSAGE, AND YOU DID NOT SPECIFY IF_NOTFND, the message
  527.     will be passed on to the next application anyway and your reply will be
  528.     passed on along with it.  In the same way, if you were not the first
  529.     application to get this message (it was passed to you), the
  530.     msg->RBuf/RLen/RFlags fields may already have something in them. If
  531.     your reply buffer pointer is not the same address as the one already in
  532.     msg->RBuf and msg->RFlags held IF_ALLOC, the old buffer in msg->RBuf
  533.     will be deallocated before processing continues with your ReplyIPC().
  534.  
  535.     ** Warning, msg->RBuf will be replaced in the same manner msg->TBuf
  536.        is by an allocated duplicate unless you specify otherwise.
  537.  
  538.  
  539. FreeIPC                             FreeIPC
  540.  
  541.     (void) FreeIPC(msg)
  542.     IPCMSG *msg;
  543.  
  544.     This routine must be called when the original sender of an IPC message
  545.     has sent the message, waited for the reply, removed the message from
  546.     the reply port, and processed the reply buffer.  This serves to free
  547.     the message and any associated buffers as specified below:
  548.  
  549.     If msg->RFlags has IF_ALLOC set the reply buffer is immediately
  550.     deallocated and msg->RBuf set to NULL.
  551.  
  552.     If msg->Confirm is non-NULL the vector will be called with the IPCMSG
  553.     as an argument.
  554.  
  555.     If msg->TFlags holds IF_ALLOCMSG, the msg will be FreeMem()d with the
  556.     length msg->Msg.mn_Length.    IF_ALLOCMSG is not usually set by the
  557.     user... it is set by SendIPC() which allocates a message/replyport
  558.     and wants both to be deallocated on completion.
  559.  
  560.  
  561.  
  562.  
  563.