home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
332.lha
/
IPC_nov89
/
Sources
/
FormatServer.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-01-09
|
23KB
|
751 lines
/************************************************************
* *
* Print Formatting Server (demo) for IPC *
* *
* -- Shared Library Version -- *
* *
* Pete Goodeve 89:3:29 *
* *
* [This module has only been compiled under Lattice; *
* it will need some modification for Manx/Aztec; *
* ... I'd be very grateful if someone would do the *
* conversion...] *
* *
* *
* This is a server to handle 'printf' conversion of *
* int, float, string, and char valued message items. *
* This saves space in other IPC modules that have a *
* need for text output. A single message results in *
* a single output string, but there can be any number *
* of items in the message, converted in sequence to *
* ASCII -- either under the control of format specifier *
* items, or in default format if these are omitted. *
* *
* The output string will by default be displayed in *
* the server's window (thus other modules need not have *
* their own). Optional message items will redirect *
* the output either to a supplied file handle (FILH) or *
* to be returned as an item in the reply message (RETS). *
* *
* Only one message ID (aside from QUIT) is recognized *
* by this server: CNVA ("CoNVert to Ascii"). See below *
* for item IDs. The program will terminate when it *
* receives a QUIT message (items ignored), or when *
* the number of clients drops from non-zero to zero *
* (in other words it will not exit if there are initially *
* no clients). It will also quit if it is sent a *
* cntrl-C break. *
* *
* *
* This is currently a completely "synchronous" server; *
* it processes each message completely before returning *
* it, and doesn't attempt to handle more than one at a *
* time. It doesn't send out any messages of its own *
* either (i.e. it isn't a "manager"), but for the *
* purpose of illustration some dummy manager type code *
* has been included in the main loop, so you can see *
* the sort of extensions that would be needed for that. *
* The procreply() procedure -- here a dummy -- would *
* have to dispatch each reply message that arrived to *
* a suitable close-out procedure, then dispose of the *
* message and any associated memory. *
* *
************************************************************/
#ifdef DEBUG /* set this to show messages being received and so on */
#include "stdio.h"
#endif
#ifdef LATTICE
#if LATTICE_40 | LATTICE_50
#include "IPC_proto.h"
/* ...else (not recent Lattice) will need library linkage stubs (IPC.o) */
#endif
#endif
#include "IPC.h"
#include "exec/memory.h"
#include "libraries/dos.h"
/*
* Define the ID codes recognized by the print format server
*
* (MAKE_ID is defined in IPC.h)
*/
/* Message IDs: */
#define CNVA MAKE_ID('C','N','V','A')
/* CoNVert to Ascii */
#define QUIT MAKE_ID('Q','U','I','T')
/* Item IDs: */
#define RETS MAKE_ID('R','E','T','S')
/* RETurn String */
#define FILH MAKE_ID('F','I','L','H')
/* FILe Handle */
#define PATS MAKE_ID('P','A','T','S')
/* PATtern String */
/* NOTE: the total formatted length for a PATS item
must not be longer than 255 characters, to avoid crashing! */
#define PAT1 MAKE_ID('P','A','T','1')
/* PATtern -- 1 item */
/* NOTE: the total formatted length for a single PAT1 item
must not be longer than 65 characters, to avoid crashing! */
#define LINE MAKE_ID('L','I','N','E')
/* indicates a complete line of text -- omitting newline */
#define TEXT MAKE_ID('T','E','X','T')
/* Text block -- may include newlines */
#define STRG MAKE_ID('S','T','R','G')
/* general non-specific ASCII STRinG */
/* The above three categories are treated identically by Pserver
-- they may have distinct meanings to other servers */
#define CHAR MAKE_ID('C','H','A','R')
/* A single character in L.S byte of ii_Ptr */
#define INTG MAKE_ID('I','N','T','G')
/* A 32-bit INTeGer in ii_Ptr */
#define REAL MAKE_ID('R','E','A','L')
/* A 32-bit floating point value in ii_Ptr (care in conversion!) */
/*******************
rather than the above if you prefer, with Lattice 4.0 you can simply
use 4-character constants as in the following (compile with the -cm option):
#define CNVA 'CNVA'
#define QUIT 'QUIT'
#define RETS 'RETS'
#define FILH 'FILH'
#define PATS 'PATS'
#define PAT1 'PAT1'
#define LINE 'LINE'
#define TEXT 'TEXT'
#define STRG 'STRG'
#define CHAR 'CHAR'
#define INTG 'INTG'
#define REAL 'REAL'
*********************/
APTR IPCBase = NULL;
struct IPCPort *import=NULL; /* this is the port we serve */
struct IPCMessage *imsg=NULL; /* we only handle one message at a time,
so a global pointer is useful */
struct MsgPort *report=NULL; /* reply port: not actually used here
-- skeleton code is shown */
/* should really include proto.h now for system procedures (next time...) */
char * AllocMem(int, ULONG);
void FreeMem(char *, int);
struct MsgPort * CreatePort(char *, int);
struct Message * Getmsg(struct MsgPort *);
void Cleanup();
void clearmarkers();
void outputstr(char *);
void procline();
void baditem(struct IPCItem *, ULONG);
int active = TRUE, /* when this goes FALSE, it's time to quit */
replies = 0; /* number of replies to get back (...if we got replies!) */
ULONG reportsig = 0, /* signal masks for ports */
importsig = 0;
ULONG outputhnd; /* file handle from message (if any) will be held here */
struct IPCItem *retitem; /* item to contain return string (if any) */
int total_length, /* length computed for output string in pre-scan */
alloc_length; /* length of allocated block (if any) -- acts as flag */
char *linebuf, *bufptr; /* pointers (fixed and moveable) to output string */
int mesg_bad; /* flag to prevent further processing if a bad item found */
/* (note that extraneous items are mostly just ignored) */
/***************************
*
* Main program entry point:
*
* -- Note that to save overhead we use '_main'; you should also compile
* (under Lattice) using the -v switch, to suppress stack checking and
* the associated baggage.
*
* We avoid using any C level I/O also -- just AmigaDOS calls, so we
* dont need <stdio.h>.
*/
void _main()
{
int clients, oldclients = 0;
/* keeps track of number of current clients */
ULONG sigset; /* set to signals that woke up Wait() */
#ifdef TRACKCLIENTS /* define this to display current number of clients */
char clrepstr[16];
#endif
/* Before anything else, we need the IPC Library: */
IPCBase = (APTR)OpenLibrary("ppipc.library",0);
if (!IPCBase) {
outputstr(
"Couldn't find ppipc.library!\nHave you installed it in LIBS: ?\n");
_exit(20);
}
report = CreatePort(NULL,0); /* I repeat... this is just a dummy here */
if (!report) {
outputstr("no space!!");
Cleanup();
return;
}
import = ServeIPCPort("Print_Format");
if (!import) {
outputstr("Print Server already exists ... exiting");
Cleanup();
return;
}
/* Get the signal bits for each port for later convenience: */
/* (Note that, because we did not include IPCPorts.h, IPCPorts
are identical to MsgPorts as far as the user is concerned;
if we DID need IPCPorts.h, these statements would have to
be changed appropriately.) */
reportsig = 1<<report->mp_SigBit;
importsig = 1<<import->mp_SigBit;
#ifdef DEBUG
setnbf(stdout); /* so we can see output! (unbuffered) */
#endif
/*
* The main loop:
* -- first we process any outstanding messages, looping until
* no more are found (procreply() always fails -- there are no
* replies in this program!).
* -- then we check the number of current clients: if they have
* all gone, we will exit.
*/
do {
while ( procimsg() || procreply()) ; /* loop to satisfy messages */
/*
* look at the number of clients (optional code included to display
* this). The number returned by CheckIPCPort includes the server,
* so for convenience we subtract 1.
* Note that we set IPP_NOTIFY, so the process gets woken up each
* time the number of clients changes.
*/
if ((clients = CheckIPCPort(import, IPP_NOTIFY) - 1)
!= oldclients) {
#ifdef TRACKCLIENTS /* for demonstration purposes... */
sprintf(clrepstr,
(clients == 1? "1 client\n" : "%d clients\n"), clients);
outputstr(clrepstr);
#endif
if (!clients) {
active = FALSE; /* quit if everyone gone */
ShutIPCPort(import); /* note: multiple calls don't hurt! */
continue; /* so we clear out any messages that sneak in */
}
oldclients = clients;
}
/*
* Now wait for further messages, unless 'active' is FALSE
* ('replies' is always FALSE in this program).
*/
if (active | replies) {
/* Note that Wait() must be used rather than WaitPort()
if we want to wake up on IPP_NOTIFY as well as messages */
sigset = Wait(importsig | reportsig | SIGBREAKF_CTRL_C);
if (sigset & SIGBREAKF_CTRL_C) {
active = FALSE;
ShutIPCPort(import); /* note: multiple calls don't hurt! */
continue; /* so we clear out any messages that sneak in */
}
}
} while (active | replies);
/*** end of main loop ***/
outputstr("Pserver terminating...\n");
Cleanup();
}
/*** end of _main ***/
void Cleanup()
{
if (import) LeaveIPCPort(import);
if (report) DeletePort(report);
CloseLibrary(IPCBase);
}
int itemn; /* global item counter */
struct IPCItem *curitem; /* global item pointer */
/*
* Process incoming messages
* -- returns FALSE if there are none, otherwise TRUE.
* It recognizes the message ID and invokes the appropriate
* handling procedures.
*/
procimsg()
{
if (!(imsg = (struct IPCMessage *) GetMsg(import))) return FALSE;
#ifdef DEBUG
printf("item count = %d\n", imsg->ipc_ItemCount);
#endif
switch (imsg->ipc_Id) {
case CNVA:
/*
* First do a scan of the message to determine how
* big a string space will be needed
* (and check for bad items):
*/
curitem = imsg->ipc_Items; /* initialize the item pointer */
for (itemn=imsg->ipc_ItemCount;
itemn && scanitem(); curitem++, itemn-- ) /* loop */;
if (mesg_bad) break;
/*
* Allocate the space needed:
*/
if (!allocline()) break;
/*
* Now actually process the items in the message:
*/
curitem = imsg->ipc_Items; /* reset again */
for (itemn=imsg->ipc_ItemCount;
itemn && procitem(); curitem++, itemn-- ) /* loop */;
/*
* Finally terminate the output string and handle
* it as directed:
*/
procline();
break; /* end of CNVA processing */
case QUIT:
active = FALSE;
ShutIPCPort(import);
outputstr("Pserver got QUIT message...");
break;
default:
#ifdef DEBUG
outputstr("got bad message");
#endif
imsg->ipc_Flags |= IPC_NOTKNOWN;
break;
}
if (mesg_bad)
imsg->ipc_Flags |= IPC_NOTKNOWN | IPC_FAILED;
ReplyMsg(imsg);
clearmarkers(); /* reset things for the next message */
return TRUE;
}
/*
* Skeleton procedure for handling replies (of which there are none
* in this program...).
*/
procreply()
{
struct IPCMessage *rpmsg;
struct IPCItem *item;
if (!(rpmsg = (struct IPCMessage *)GetMsg(report))) return FALSE;
if (rpmsg->ipc_Flags & IPC_NOTKNOWN) {
#ifdef DEBUG
outputstr("\nServer didn't like this message...");
#endif
}
item = rpmsg->ipc_Items;
/* message deletion and so on would go here... */
replies--; /* This variable is incremented for each original message
generated by this program; the program will not exit
as long as it is non-zero */
return TRUE;
}
/*
* Reset all global variables ready for next incoming message
*/
void clearmarkers()
{
if (alloc_length) FreeMem(linebuf, alloc_length);
linebuf = NULL;
alloc_length = 0;
total_length = 0;
retitem = NULL;
outputhnd = NULL;
mesg_bad = FALSE;
}
/*
* Scan the current item to determine length of string it will generate.
* It also recognizes disposition control items (RETS, FILH) and sets up
* the required pointers.
* Subprocedures called may also check validity of item to be formatted
* (extraneous items not following a format specifier are simply ignored.)
*/
scanitem()
{
char dummy[66]; /* this should be enough (!) */
#ifdef DEBUG
debugitem(curitem,"scanitem:");
#endif
switch (curitem->ii_Id) {
case RETS:
retitem = curitem;
break;
case FILH:
outputhnd = (ULONG)curitem->ii_Ptr;
break;
case PATS:
total_length += scanpatstring();
return FALSE; /* stop here */
case PAT1:
total_length += patternitem(dummy);
break;
case LINE:
case TEXT:
case STRG:
total_length += strlen(curitem->ii_Ptr);
break;
case CHAR:
total_length++;
break;
case INTG:
total_length += cnvtitem(dummy, "%ld", curitem);
break;
case REAL:
total_length += cnvtitem(dummy, "%g", curitem);
break;
default:
#ifdef DEBUG
outputstr("got unknown item");
#endif
/* ignore extraneous stuff */
break;
}
return (!mesg_bad); /* stops here if message has failed */
}
/*
* Allocate space for complete output string.
* -- if the message has supplied a NON-NULL ii_Ptr in a RETS item,
* this will be used as the buffer, as long as the associated ii_Size
* is large enough (if it is not, the message will fail). Otherwise
* space is allocated for the buffer. If a NULL RETS item has been
* supplied, the buffer pointer will be returned there (otherwise the
* buffer will be freed again after output).
* It will of course also fail if there is no space for a buffer.
* Any failure returns FALSE, otherwise TRUE.
*/
allocline()
{
int adj_length = total_length + 1; /* allow for terminator */
if (retitem) { /* check for a RETS item first */
if (retitem->ii_Ptr) { /* buffer supplied? */
if (retitem->ii_Size > total_length) { /* big enough ? */
linebuf = (char *)retitem->ii_Ptr; /* yes -- use it */
}
else { /* too small */
baditem(retitem, IPC_FAILED);
return FALSE;
}
}
else { /* NULL pointer, so create some space */
linebuf = AllocMem(adj_length, MEMF_PUBLIC);
if (!linebuf) {
baditem(retitem, IPC_FAILED);
return FALSE;
}
else {
retitem->ii_Ptr = (void *)linebuf;
retitem->ii_Size = adj_length;
retitem->ii_Flags = IPC_TRANSFER | IPC_MODIFIED;
/* flags are set to indicate client must dispose
of this block */
}
}
}
else { /* no RETS item, so use local buffer */
linebuf = AllocMem(adj_length, MEMF_PUBLIC);
if (!linebuf) {
imsg->ipc_Flags |= IPC_NOTKNOWN | IPC_FAILED;
return FALSE;
}
alloc_length = adj_length; /* used to free the message afterward */
}
bufptr = linebuf; /* initialize the output pointer */
return TRUE;
}
/*
* Write the ASCII string for the current item to the output buffer.
* A suitable default format is used for items with no preceding
* format specifier.
*/
procitem()
{
#ifdef DEBUG
debugitem(curitem,"procitem:");
#endif
switch (curitem->ii_Id) {
case RETS:
case FILH:
break;
case PATS:
bufptr +=cnvtpatstring(bufptr);
return FALSE; /* no further items allowed */
case PAT1:
bufptr += patternitem(bufptr);
break;
case LINE:
case TEXT:
case STRG:
strcpy(bufptr, curitem->ii_Ptr);
bufptr += strlen(curitem->ii_Ptr);
break;
case CHAR:
*bufptr++ = (char) (curitem->ii_Ptr);
/* Lattice gives a warning here but does it OK */
break;
case INTG:
bufptr += cnvtitem(bufptr, "%ld", curitem);
break;
case REAL:
bufptr += cnvtitem(bufptr, "%g", curitem);
break;
default:
/* ignore */
break;
}
return TRUE;
}
/*
* Determine length (and validity) of multi-item format specifier
*/
scanpatstring()
{
char *dummy;
int len;
dummy = AllocMem(256, 0L); /* temporary local storage */
if (!dummy)
return 256; /* let allocline fail instead ! */
len = cnvtpatstring(dummy);
FreeMem(dummy, 256);
return len;
}
/*
* Use the format in a PAT1 item to convert the following item.
* (Simply calls cnvtitem with suitable arguments.)
*/
patternitem(destptr)
char *destptr;
{
char *fmtp;
if (--itemn) {
fmtp = (char *)curitem->ii_Ptr;
return cnvtitem(destptr, fmtp, ++curitem);
}
else {
baditem(curitem, IPC_FAILED);
mesg_bad = TRUE;
return 0;
}
}
/*
* Convert a single value (in the current item) according to the
* format specified. Output is to destptr; the size of the resulting
* string is returned.
*/
cnvtitem(destptr, fmtstr, item)
char *destptr, *fmtstr;
struct IPCItem *item;
{
double doubleval;
int len;
#ifdef DEBUG
debugitem(item,"cnvtitem:");
#endif
switch (item->ii_Id) { /* handle according to type */
case LINE:
case TEXT:
case STRG:
len = sprintf(destptr, fmtstr, item->ii_Ptr);
break;
case CHAR:
len = sprintf(destptr, fmtstr, item->ii_Ptr);
break;
case INTG:
len = sprintf(destptr, fmtstr, item->ii_Ptr);
break;
case REAL:
doubleval = *(float *) &item->ii_Ptr;
len = sprintf(destptr, fmtstr, doubleval);
break;
default:
baditem(item, IPC_FAILED);
mesg_bad = TRUE;
}
return len;
}
/*
* Convert items to ASCII according to format pattern in the current item.
* All the remaining items in the message must be values to satisfy the
* pattern. (Note that REAL items aren't allowed, because they can't be
* passed to sprintf as 32-bit values). Up to 10 items can be handled.
* The total length of the formatted string is returned.
*/
cnvtpatstring(destptr)
char *destptr; /* destination string buffer */
{
char *fmtp;
int i;
ULONG p[10]; /* Storage for 10 values */
fmtp = (char *)curitem->ii_Ptr; /* hold pointer to pattern string */
for (i=0; i<10 && --itemn; i++ ) { /* process all the remaining items */
++curitem;
#ifdef DEBUG
debugitem(curitem,"patstring:");
#endif
switch (curitem->ii_Id) { /* everything the same except REAL */
case LINE:
case TEXT:
case STRG:
case CHAR:
case INTG:
p[i] = (ULONG)curitem->ii_Ptr; /* local copy */
break;
case REAL: /* can't pass a double this way!! */
default:
baditem(curitem, IPC_FAILED);
mesg_bad = TRUE; /* can't continue */
return 0;
}
}
if (itemn) { /* more than 10 items found */
baditem(++curitem, IPC_FAILED);
mesg_bad = TRUE;
return 0;
}
while (i<10) p[i++] = (ULONG) "\0"; /* not very adequate protection */
/* Fortunately C allows us to pass unused arguments: */
return sprintf(destptr, fmtp,
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
}
/*
* Process the assembled output string
* -- if the output handle has been supplied, the string will be sent there;
* if neither handle nor return slot has been supplied, the string will
* be output to the server's window.
* (If a return slot is present in the message, the string will be returned
* there in any case.)
*/
void procline()
{
*bufptr = '\0'; /* Terminate the string first */
if (outputhnd) Write(outputhnd, linebuf, total_length);
else if (!retitem)
Write(Output(), linebuf, total_length);
}
/*
* Set error flags in an item and the message
* (Note that this doesn't abort processing -- if this is needed,
* the mesg_bad flag should also be set)
*/
void baditem(item, extraflags)
struct IPCItem *item;
ULONG extraflags;
{
imsg->ipc_Flags |= IPC_CHECKITEM;
item->ii_Flags |= IPC_NOTKNOWN | extraflags;
#ifdef DEBUG
debugitem(item, "BAD:");
#endif
}
/* procedure to display string in window (avoids C I/O overhead) */
void outputstr(str) char *str;
{
Write(Output(), str, strlen(str));
}
/************************************************************?
/* the following is only included if you want debugging output: */
#ifdef DEBUG
debugitem(item, str)
struct IPCItem *item;
char *str;
{
ULONG icode[2];
icode[0] = item->ii_Id;
icode[1] = 0;
printf("%s item %d code %s [%x] ptr %x flagged %x\n",
str, item - imsg->ipc_Items,
icode, *icode,
item->ii_Ptr, item->ii_Flags);
}
#endif