summaryrefslogtreecommitdiff
path: root/mig/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'mig/server.c')
-rw-r--r--mig/server.c1322
1 files changed, 1322 insertions, 0 deletions
diff --git a/mig/server.c b/mig/server.c
new file mode 100644
index 0000000..07e4964
--- /dev/null
+++ b/mig/server.c
@@ -0,0 +1,1322 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <assert.h>
+
+#include "write.h"
+#include "utils.h"
+#include "global.h"
+#include "error.h"
+#include "cpu.h"
+
+static void
+WriteIncludes(FILE *file)
+{
+ fprintf(file, "#define EXPORT_BOOLEAN\n");
+ fprintf(file, "#include <mach/boolean.h>\n");
+ fprintf(file, "#include <mach/kern_return.h>\n");
+ fprintf(file, "#include <mach/message.h>\n");
+ fprintf(file, "#include <mach/mig_errors.h>\n");
+ fprintf(file, "#include <mach/mig_support.h>\n");
+ if (IsKernelServer)
+ fprintf(file, "#include <ipc/ipc_port.h>\n");
+ fprintf(file, "\n");
+}
+
+static void
+WriteGlobalDecls(FILE *file)
+{
+ if (RCSId != strNULL)
+ WriteRCSDecl(file, strconcat(SubsystemName, "_server"), RCSId);
+
+ /* Used for locations in the request message, *not* reply message.
+ Reply message locations aren't dependent on IsKernelServer. */
+
+ if (IsKernelServer)
+ {
+ fprintf(file, "#define msgh_request_port\tmsgh_remote_port\n");
+ fprintf(file, "#define MACH_MSGH_BITS_REQUEST(bits)");
+ fprintf(file, "\tMACH_MSGH_BITS_REMOTE(bits)\n");
+ fprintf(file, "#define msgh_reply_port\t\tmsgh_local_port\n");
+ fprintf(file, "#define MACH_MSGH_BITS_REPLY(bits)");
+ fprintf(file, "\tMACH_MSGH_BITS_LOCAL(bits)\n");
+ }
+ else
+ {
+ fprintf(file, "#define msgh_request_port\tmsgh_local_port\n");
+ fprintf(file, "#define MACH_MSGH_BITS_REQUEST(bits)");
+ fprintf(file, "\tMACH_MSGH_BITS_LOCAL(bits)\n");
+ fprintf(file, "#define msgh_reply_port\t\tmsgh_remote_port\n");
+ fprintf(file, "#define MACH_MSGH_BITS_REPLY(bits)");
+ fprintf(file, "\tMACH_MSGH_BITS_REMOTE(bits)\n");
+ }
+ fprintf(file, "\n");
+}
+
+static void
+WriteProlog(FILE *file)
+{
+ fprintf(file, "/* Module %s */\n", SubsystemName);
+ fprintf(file, "\n");
+
+ WriteIncludes(file);
+ WriteBogusDefines(file);
+ WriteGlobalDecls(file);
+}
+
+
+static void
+WriteSymTabEntries(FILE *file, const statement_t *stats)
+{
+ register const statement_t *stat;
+ register u_int current = 0;
+
+ for (stat = stats; stat != stNULL; stat = stat->stNext)
+ if (stat->stKind == skRoutine) {
+ register num = stat->stRoutine->rtNumber;
+ const char *name = stat->stRoutine->rtName;
+
+ while (++current <= num)
+ fprintf(file,"\t\t\t{ \"\", 0, 0 },\n");
+ fprintf(file, "\t{ \"%s\", %d, _X%s },\n",
+ name,
+ SubsystemBase + current - 1,
+ name);
+ }
+ while (++current <= rtNumber)
+ fprintf(file,"\t{ \"\", 0, 0 },\n");
+}
+
+static void
+WriteArrayEntries(FILE *file, const statement_t *stats)
+{
+ register u_int current = 0;
+ register const statement_t *stat;
+
+ for (stat = stats; stat != stNULL; stat = stat->stNext)
+ if (stat->stKind == skRoutine)
+ {
+ register const routine_t *rt = stat->stRoutine;
+
+ while (current++ < rt->rtNumber)
+ fprintf(file, "\t\t0,\n");
+ fprintf(file, "\t\t_X%s,\n", rt->rtName);
+ }
+ while (current++ < rtNumber)
+ fprintf(file, "\t\t\t0,\n");
+}
+
+static void
+WriteEpilog(FILE *file, const statement_t *stats)
+{
+ fprintf(file, "\n");
+
+ /*
+ * First, the symbol table
+ */
+ fprintf(file, "static mig_routine_t %s_routines[] = {\n", ServerDemux);
+
+ WriteArrayEntries(file, stats);
+
+ fprintf(file, "};\n");
+ fprintf(file, "\n");
+
+ /*
+ * Then, the server routine
+ */
+ fprintf(file, "mig_external boolean_t %s\n", ServerDemux);
+ fprintf(file, "\t(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)\n");
+
+ fprintf(file, "{\n");
+ fprintf(file, "\tregister mach_msg_header_t *InP = InHeadP;\n");
+
+ fprintf(file, "\tregister mig_reply_header_t *OutP = (mig_reply_header_t *) OutHeadP;\n");
+
+ fprintf(file, "\n");
+
+ WriteStaticDecl(file, itRetCodeType,
+ itRetCodeType->itDeallocate, itRetCodeType->itLongForm,
+ !IsKernelServer, "RetCodeType");
+ fprintf(file, "\n");
+
+ fprintf(file, "\tregister mig_routine_t routine;\n");
+ fprintf(file, "\n");
+
+ fprintf(file, "\tOutP->Head.msgh_bits = ");
+ fprintf(file, "MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InP->msgh_bits), 0);\n");
+ fprintf(file, "\tOutP->Head.msgh_size = sizeof *OutP;\n");
+ fprintf(file, "\tOutP->Head.msgh_remote_port = InP->msgh_reply_port;\n");
+ fprintf(file, "\tOutP->Head.msgh_local_port = MACH_PORT_NULL;\n");
+ fprintf(file, "\tOutP->Head.msgh_seqno = 0;\n");
+ fprintf(file, "\tOutP->Head.msgh_id = InP->msgh_id + 100;\n");
+ fprintf(file, "\n");
+ WritePackMsgType(file, itRetCodeType,
+ itRetCodeType->itDeallocate, itRetCodeType->itLongForm,
+ !IsKernelServer, "OutP->RetCodeType", "RetCodeType");
+ fprintf(file, "\n");
+
+ fprintf(file, "\tif ((InP->msgh_id > %d) || (InP->msgh_id < %d) ||\n",
+ SubsystemBase + rtNumber - 1, SubsystemBase);
+ fprintf(file, "\t ((routine = %s_routines[InP->msgh_id - %d]) == 0)) {\n",
+ ServerDemux, SubsystemBase);
+ fprintf(file, "\t\tOutP->RetCode = MIG_BAD_ID;\n");
+ fprintf(file, "\t\treturn FALSE;\n");
+ fprintf(file, "\t}\n");
+
+ /* Call appropriate routine */
+ fprintf(file, "\t(*routine) (InP, &OutP->Head);\n");
+ fprintf(file, "\treturn TRUE;\n");
+ fprintf(file, "}\n");
+ fprintf(file, "\n");
+
+ /*
+ * Then, the <subsystem>_server_routine routine
+ */
+ fprintf(file, "mig_external mig_routine_t %s_routine\n", ServerDemux);
+ fprintf(file, "\t(const mach_msg_header_t *InHeadP)\n");
+
+ fprintf(file, "{\n");
+ fprintf(file, "\tregister int msgh_id;\n");
+ fprintf(file, "\n");
+ fprintf(file, "\tmsgh_id = InHeadP->msgh_id - %d;\n", SubsystemBase);
+ fprintf(file, "\n");
+ fprintf(file, "\tif ((msgh_id > %d) || (msgh_id < 0))\n",
+ rtNumber - 1);
+ fprintf(file, "\t\treturn 0;\n");
+ fprintf(file, "\n");
+ fprintf(file, "\treturn %s_routines[msgh_id];\n", ServerDemux);
+ fprintf(file, "}\n");
+ fprintf(file, "\n");
+
+ /* symtab */
+
+ if (GenSymTab) {
+ fprintf(file,"\nmig_symtab_t _%sSymTab[] = {\n",SubsystemName);
+ WriteSymTabEntries(file,stats);
+ fprintf(file,"};\n");
+ fprintf(file,"int _%sSymTabBase = %d;\n",SubsystemName,SubsystemBase);
+ fprintf(file,"int _%sSymTabEnd = %d;\n",SubsystemName,SubsystemBase+rtNumber);
+ }
+}
+
+/*
+ * Returns the return type of the server-side work function.
+ * Suitable for "extern %s serverfunc()".
+ */
+static const char *
+ServerSideType(const routine_t *rt)
+{
+ if (rt->rtServerReturn == argNULL)
+ return "void";
+ else
+ return rt->rtServerReturn->argType->itTransType;
+}
+
+static void
+WriteLocalVarDecl(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *it = arg->argType;
+
+ if (it->itInLine && it->itVarArray)
+ {
+ register const ipc_type_t *btype = it->itElement;
+
+ fprintf(file, "\t%s %s[%d]", btype->itTransType,
+ arg->argVarName, it->itNumber/btype->itNumber);
+ }
+ else
+ fprintf(file, "\t%s %s", it->itTransType, arg->argVarName);
+}
+
+static void
+WriteLocalPtrDecl(FILE *file, register const argument_t *arg)
+{
+ fprintf(file, "\t%s *%sP",
+ FetchServerType(arg->argType->itElement),
+ arg->argVarName);
+}
+
+static void
+WriteServerArgDecl(FILE *file, const argument_t *arg)
+{
+ fprintf(file, "%s %s%s",
+ arg->argType->itTransType,
+ arg->argByReferenceServer ? "*" : "",
+ arg->argVarName);
+}
+
+/*
+ * Writes the local variable declarations which are always
+ * present: InP, OutP, the server-side work function.
+ */
+static void
+WriteVarDecls(FILE *file, const routine_t *rt)
+{
+ int i;
+ boolean_t NeedMsghSize = FALSE;
+ boolean_t NeedMsghSizeDelta = FALSE;
+
+ fprintf(file, "\tregister Request *In0P = (Request *) InHeadP;\n");
+ for (i = 1; i <= rt->rtMaxRequestPos; i++)
+ fprintf(file, "\tregister Request *In%dP;\n", i);
+ fprintf(file, "\tregister Reply *OutP = (Reply *) OutHeadP;\n");
+
+ fprintf(file, "\tmig_external %s %s\n",
+ ServerSideType(rt), rt->rtServerName);
+ fprintf(file, "\t\t(");
+ WriteList(file, rt->rtArgs, WriteServerArgDecl, akbServerArg, ", ", "");
+ fprintf(file, ");\n");
+ fprintf(file, "\n");
+
+ if (!rt->rtSimpleFixedReply)
+ fprintf(file, "\tboolean_t msgh_simple;\n");
+ else if (!rt->rtSimpleCheckRequest)
+ {
+ fprintf(file, "#if\tTypeCheck\n");
+ fprintf(file, "\tboolean_t msgh_simple;\n");
+ fprintf(file, "#endif\t/* TypeCheck */\n");
+ fprintf(file, "\n");
+ }
+
+ /* if either request or reply is variable, we may need
+ msgh_size_delta and msgh_size */
+
+ if (rt->rtNumRequestVar > 0)
+ NeedMsghSize = TRUE;
+ if (rt->rtMaxRequestPos > 0)
+ NeedMsghSizeDelta = TRUE;
+
+ if (rt->rtNumReplyVar > 1)
+ NeedMsghSize = TRUE;
+ if (rt->rtMaxReplyPos > 0)
+ NeedMsghSizeDelta = TRUE;
+
+ if (NeedMsghSize)
+ fprintf(file, "\tunsigned int msgh_size;\n");
+ if (NeedMsghSizeDelta)
+ fprintf(file, "\tunsigned int msgh_size_delta;\n");
+
+ if (NeedMsghSize || NeedMsghSizeDelta)
+ fprintf(file, "\n");
+}
+
+static void
+WriteMsgError(FILE *file, const char *error_msg)
+{
+ fprintf(file, "\t\t{ OutP->RetCode = %s; return; }\n", error_msg);
+}
+
+static void
+WriteReplyInit(FILE *file, const routine_t *rt)
+{
+ boolean_t printed_nl = FALSE;
+
+ if (rt->rtSimpleFixedReply)
+ {
+ if (!rt->rtSimpleSendReply) /* complex reply message */
+ {
+ printed_nl = TRUE;
+ fprintf(file, "\n");
+ fprintf(file,
+ "\tOutP->Head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;\n");
+ }
+ }
+ else
+ {
+ printed_nl = TRUE;
+ fprintf(file, "\n");
+ fprintf(file, "\tmsgh_simple = %s;\n",
+ strbool(rt->rtSimpleSendReply));
+ }
+
+ if (rt->rtNumReplyVar == 0)
+ {
+ if (!printed_nl)
+ fprintf(file, "\n");
+ fprintf(file, "\tOutP->Head.msgh_size = %d;\n", rt->rtReplySize);
+ }
+}
+
+static void
+WriteReplyHead(FILE *file, const routine_t *rt)
+{
+ if ((!rt->rtSimpleFixedReply) ||
+ (rt->rtNumReplyVar > 1))
+ {
+ fprintf(file, "\n");
+ if (rt->rtMaxReplyPos > 0)
+ fprintf(file, "\tOutP = (Reply *) OutHeadP;\n");
+ }
+
+ if (!rt->rtSimpleFixedReply)
+ {
+ fprintf(file, "\tif (!msgh_simple)\n");
+ fprintf(file,
+ "\t\tOutP->Head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;\n");
+ }
+ if (rt->rtNumReplyVar > 1)
+ fprintf(file, "\tOutP->Head.msgh_size = msgh_size;\n");
+}
+
+static void
+WriteCheckHead(FILE *file, const routine_t *rt)
+{
+ fprintf(file, "#if\tTypeCheck\n");
+ if (rt->rtNumRequestVar > 0)
+ fprintf(file, "\tmsgh_size = In0P->Head.msgh_size;\n");
+ if (!rt->rtSimpleCheckRequest)
+ fprintf(file, "\tmsgh_simple = !(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX);\n");
+
+ if (rt->rtNumRequestVar > 0)
+ fprintf(file, "\tif ((msgh_size < %d)",
+ rt->rtRequestSize);
+ else
+ fprintf(file, "\tif ((In0P->Head.msgh_size != %d)",
+ rt->rtRequestSize);
+
+ if (rt->rtSimpleCheckRequest)
+ fprintf(file, " ||\n\t %s(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)",
+ rt->rtSimpleReceiveRequest ? "" : "!");
+ fprintf(file, ")\n");
+ WriteMsgError(file, "MIG_BAD_ARGUMENTS");
+ fprintf(file, "#endif\t/* TypeCheck */\n");
+ fprintf(file, "\n");
+}
+
+static void
+WriteTypeCheck(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *it = arg->argType;
+ register const routine_t *rt = arg->argRoutine;
+
+ fprintf(file, "#if\tTypeCheck\n");
+ if (akCheck(arg->argKind, akbRequestQC))
+ fprintf(file, "\tif (* (int *) &In%dP->%s != * (int *) &%sCheck)\n",
+ arg->argRequestPos, arg->argTTName, arg->argVarName);
+ else
+ {
+ fprintf(file, "\tif (");
+ if (!it->itIndefinite) {
+ fprintf(file, "(In%dP->%s%s.msgt_inline != %s) ||\n\t ",
+ arg->argRequestPos, arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "",
+ strbool(it->itInLine));
+ }
+ fprintf(file, "(In%dP->%s%s.msgt_longform != %s) ||\n",
+ arg->argRequestPos, arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "",
+ strbool(arg->argLongForm));
+ if (it->itOutName == MACH_MSG_TYPE_POLYMORPHIC)
+ {
+ if (!rt->rtSimpleCheckRequest)
+ fprintf(file, "\t (MACH_MSG_TYPE_PORT_ANY(In%dP->%s.msgt%s_name) && msgh_simple) ||\n",
+ arg->argRequestPos, arg->argTTName,
+ arg->argLongForm ? "l" : "");
+ }
+ else
+ fprintf(file, "\t (In%dP->%s.msgt%s_name != %s) ||\n",
+ arg->argRequestPos, arg->argTTName,
+ arg->argLongForm ? "l" : "",
+ it->itOutNameStr);
+ if (!it->itVarArray)
+ fprintf(file, "\t (In%dP->%s.msgt%s_number != %d) ||\n",
+ arg->argRequestPos, arg->argTTName,
+ arg->argLongForm ? "l" : "",
+ it->itNumber);
+ fprintf(file, "\t (In%dP->%s.msgt%s_size != %d))\n",
+ arg->argRequestPos, arg->argTTName,
+ arg->argLongForm ? "l" : "",
+ it->itSize);
+ }
+ WriteMsgError(file, "MIG_BAD_ARGUMENTS");
+ fprintf(file, "#endif\t/* TypeCheck */\n");
+ fprintf(file, "\n");
+}
+
+static void
+WriteCheckArgSize(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *ptype = arg->argType;
+ register const ipc_type_t *btype = ptype->itElement;
+ const argument_t *count = arg->argCount;
+ int multiplier = btype->itTypeSize / btype->itNumber;
+
+ if (ptype->itIndefinite) {
+ /*
+ * Check descriptor. If out-of-line, use standard size.
+ */
+ fprintf(file, "(In%dP->%s%s.msgt_inline) ? ",
+ arg->argRequestPos,
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ }
+
+ if (btype->itTypeSize % 4 != 0)
+ fprintf(file, "(");
+
+ if (multiplier > 1)
+ fprintf(file, "%d * ", multiplier);
+
+ fprintf(file, "In%dP->%s", arg->argRequestPos, count->argMsgField);
+
+ /* If the base type size of the data field isn`t a multiple of 4,
+ we have to round up. */
+ if (btype->itTypeSize % 4 != 0)
+ fprintf(file, " + 3) & ~3");
+
+ if (ptype->itIndefinite) {
+ fprintf(file, " : sizeof(%s *)", FetchServerType(btype));
+ }
+}
+
+static void
+WriteCheckMsgSize(FILE *file, register const argument_t *arg)
+{
+ register const routine_t *rt = arg->argRoutine;
+
+ /* If there aren't any more In args after this, then
+ we can use the msgh_size_delta value directly in
+ the TypeCheck conditional. */
+
+ if (arg->argRequestPos == rt->rtMaxRequestPos)
+ {
+ fprintf(file, "#if\tTypeCheck\n");
+ fprintf(file, "\tif (msgh_size != %d + (", rt->rtRequestSize);
+ WriteCheckArgSize(file, arg);
+ fprintf(file, "))\n");
+
+ WriteMsgError(file, "MIG_BAD_ARGUMENTS");
+ fprintf(file, "#endif\t/* TypeCheck */\n");
+ }
+ else
+ {
+ /* If there aren't any more variable-sized arguments after this,
+ then we must check for exact msg-size and we don't need to
+ update msgh_size. */
+
+ boolean_t LastVarArg = arg->argRequestPos+1 == rt->rtNumRequestVar;
+
+ /* calculate the actual size in bytes of the data field. note
+ that this quantity must be a multiple of four. hence, if
+ the base type size isn't a multiple of four, we have to
+ round up. note also that btype->itNumber must
+ divide btype->itTypeSize (see itCalculateSizeInfo). */
+
+ fprintf(file, "\tmsgh_size_delta = ");
+ WriteCheckArgSize(file, arg);
+ fprintf(file, ";\n");
+ fprintf(file, "#if\tTypeCheck\n");
+
+ /* Don't decrement msgh_size until we've checked that
+ it won't underflow. */
+
+ if (LastVarArg)
+ fprintf(file, "\tif (msgh_size != %d + msgh_size_delta)\n",
+ rt->rtRequestSize);
+ else
+ fprintf(file, "\tif (msgh_size < %d + msgh_size_delta)\n",
+ rt->rtRequestSize);
+ WriteMsgError(file, "MIG_BAD_ARGUMENTS");
+
+ if (!LastVarArg)
+ fprintf(file, "\tmsgh_size -= msgh_size_delta;\n");
+
+ fprintf(file, "#endif\t/* TypeCheck */\n");
+ }
+ fprintf(file, "\n");
+}
+
+static const char *
+InArgMsgField(register const argument_t *arg)
+{
+ static char buffer[100];
+
+ /*
+ * Inside the kernel, the request and reply port fields
+ * really hold ipc_port_t values, not mach_port_t values.
+ * Hence we must cast the values.
+ */
+
+ if (IsKernelServer &&
+ ((akIdent(arg->argKind) == akeRequestPort) ||
+ (akIdent(arg->argKind) == akeReplyPort)))
+ sprintf(buffer, "(ipc_port_t) In%dP->%s",
+ arg->argRequestPos, arg->argMsgField);
+ else
+ sprintf(buffer, "In%dP->%s",
+ arg->argRequestPos, arg->argMsgField);
+
+ return buffer;
+}
+
+static void
+WriteExtractArgValue(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *it = arg->argType;
+
+ if (arg->argMultiplier > 1)
+ WriteCopyType(file, it, "%s", "/* %s */ %s / %d",
+ arg->argVarName, InArgMsgField(arg), arg->argMultiplier);
+ else if (it->itInTrans != strNULL)
+ WriteCopyType(file, it, "%s", "/* %s */ %s(%s)",
+ arg->argVarName, it->itInTrans, InArgMsgField(arg));
+ else
+ WriteCopyType(file, it, "%s", "/* %s */ %s",
+ arg->argVarName, InArgMsgField(arg));
+ fprintf(file, "\n");
+}
+
+static void
+WriteInitializeCount(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *ptype = arg->argParent->argType;
+ register const ipc_type_t *btype = ptype->itElement;
+
+ /*
+ * Initialize 'count' argument for variable-length inline OUT parameter
+ * with maximum allowed number of elements.
+ */
+
+ fprintf(file, "\t%s = %d;\n", arg->argVarName,
+ ptype->itNumber/btype->itNumber);
+
+ /*
+ * If the user passed in a count, then we use the minimum.
+ * We can't let the user completely override our maximum,
+ * or the user might convince the server to overwrite the buffer.
+ */
+
+ if (arg->argCInOut != argNULL) {
+ const char *msgfield = InArgMsgField(arg->argCInOut);
+
+ fprintf(file, "\tif (%s < %s)\n", msgfield, arg->argVarName);
+ fprintf(file, "\t\t%s = %s;\n", arg->argVarName, msgfield);
+ }
+
+ fprintf(file, "\n");
+}
+
+static void
+WriteInitializePtr(FILE *file, register const argument_t *arg)
+{
+ if (akCheck(arg->argKind, akbVarNeeded))
+ fprintf(file, "\t%sP = %s;\n",
+ arg->argVarName, arg->argVarName);
+ else
+ fprintf(file, "\t%sP = OutP->%s;\n",
+ arg->argVarName, arg->argMsgField);
+}
+
+static void
+WriteTypeCheckArg(FILE *file, register const argument_t *arg)
+{
+ if (akCheck(arg->argKind, akbRequest)) {
+ WriteTypeCheck(file, arg);
+
+ if (akCheck(arg->argKind, akbVariable))
+ WriteCheckMsgSize(file, arg);
+ }
+}
+
+static void
+WriteAdjustRequestMsgPtr(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *ptype = arg->argType;
+
+ fprintf(file,
+ "\tIn%dP = (Request *) ((char *) In%dP + msgh_size_delta - %d);\n\n",
+ arg->argRequestPos+1, arg->argRequestPos,
+ ptype->itTypeSize + ptype->itPadSize);
+}
+
+static void
+WriteTypeCheckRequestArgs(FILE *file, register const routine_t *rt)
+{
+ register const argument_t *arg;
+ register const argument_t *lastVarArg;
+
+ lastVarArg = argNULL;
+ for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) {
+
+ /*
+ * Advance message pointer if the last request argument was
+ * variable-length and the request position will change.
+ */
+ if (lastVarArg != argNULL &&
+ lastVarArg->argRequestPos < arg->argRequestPos)
+ {
+ WriteAdjustRequestMsgPtr(file, lastVarArg);
+ lastVarArg = argNULL;
+ }
+
+ /*
+ * Type-check the argument.
+ */
+ WriteTypeCheckArg(file, arg);
+
+ /*
+ * Remember whether this was variable-length.
+ */
+ if (akCheckAll(arg->argKind, akbVariable|akbRequest))
+ lastVarArg = arg;
+ }
+}
+
+static void
+WriteExtractArg(FILE *file, register const argument_t *arg)
+{
+ if (akCheckAll(arg->argKind, akbSendRcv|akbVarNeeded))
+ WriteExtractArgValue(file, arg);
+
+ if ((akIdent(arg->argKind) == akeCount) &&
+ akCheck(arg->argKind, akbReturnSnd))
+ {
+ register ipc_type_t *ptype = arg->argParent->argType;
+
+ if (ptype->itInLine && ptype->itVarArray)
+ WriteInitializeCount(file, arg);
+ }
+
+ if (akCheckAll(arg->argKind, akbReturnSnd|akbPointer))
+ WriteInitializePtr(file, arg);
+}
+
+static void
+WriteServerCallArg(FILE *file, register const argument_t *arg)
+{
+ const ipc_type_t *it = arg->argType;
+ boolean_t NeedClose = FALSE;
+
+ if (arg->argByReferenceServer)
+ fprintf(file, "&");
+
+ if ((it->itInTrans != strNULL) &&
+ akCheck(arg->argKind, akbSendRcv) &&
+ !akCheck(arg->argKind, akbVarNeeded))
+ {
+ fprintf(file, "%s(", it->itInTrans);
+ NeedClose = TRUE;
+ }
+
+ if (akCheck(arg->argKind, akbPointer))
+ fprintf(file, "%sP", arg->argVarName);
+ else if (akCheck(arg->argKind, akbVarNeeded))
+ fprintf(file, "%s", arg->argVarName);
+ else if (akCheck(arg->argKind, akbSendRcv)) {
+ if (akCheck(arg->argKind, akbIndefinite)) {
+ fprintf(file, "(In%dP->%s%s.msgt_inline) ",
+ arg->argRequestPos,
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ fprintf(file, "? %s ", InArgMsgField(arg));
+ fprintf(file, ": *((%s **)%s)",
+ FetchServerType(arg->argType->itElement),
+ InArgMsgField(arg));
+ }
+ else
+ fprintf(file, "%s", InArgMsgField(arg));
+ }
+ else
+ fprintf(file, "OutP->%s", arg->argMsgField);
+
+ if (NeedClose)
+ fprintf(file, ")");
+
+ if (!arg->argByReferenceServer && (arg->argMultiplier > 1))
+ fprintf(file, " / %d", arg->argMultiplier);
+}
+
+static void
+WriteDestroyArg(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *it = arg->argType;
+
+ if (akCheck(arg->argKind, akbIndefinite)) {
+ /*
+ * Deallocate only if out-of-line.
+ */
+ argument_t *count = arg->argCount;
+ ipc_type_t *btype = it->itElement;
+ int multiplier = btype->itTypeSize / btype->itNumber;
+
+ fprintf(file, "\tif (!In%dP->%s%s.msgt_inline)\n",
+ arg->argRequestPos,
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ fprintf(file, "\t\t%smig_deallocate(* (vm_offset_t *) %s, ",
+ SubrPrefix, InArgMsgField(arg));
+ if (multiplier > 1)
+ fprintf(file, "%d * ", multiplier);
+ fprintf(file, " %s);\n", InArgMsgField(count));
+ } else {
+ if (akCheck(arg->argKind, akbVarNeeded))
+ fprintf(file, "\t%s(%s);\n", it->itDestructor, arg->argVarName);
+ else
+ fprintf(file, "\t%s(%s);\n", it->itDestructor,
+ InArgMsgField(arg));
+ }
+}
+
+static void
+WriteDestroyPortArg(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *it = arg->argType;
+
+ /*
+ * If a translated port argument occurs in the body of a request
+ * message, and the message is successfully processed, then the
+ * port right should be deallocated. However, the called function
+ * didn't see the port right; it saw the translation. So we have
+ * to release the port right for it.
+ */
+
+ if ((it->itInTrans != strNULL) &&
+ (it->itOutName == MACH_MSG_TYPE_PORT_SEND))
+ {
+ fprintf(file, "\n");
+ fprintf(file, "\tif (IP_VALID(%s))\n", InArgMsgField(arg));
+ fprintf(file, "\t\t%sipc_port_release_send(%s);\n",
+ SubrPrefix, InArgMsgField(arg));
+ }
+}
+
+/*
+ * Check whether WriteDestroyPortArg would generate any code for arg.
+ */
+static boolean_t
+CheckDestroyPortArg(register const argument_t *arg)
+{
+ register const ipc_type_t *it = arg->argType;
+
+ if ((it->itInTrans != strNULL) &&
+ (it->itOutName == MACH_MSG_TYPE_PORT_SEND))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+WriteServerCall(FILE *file, const routine_t *rt)
+{
+ boolean_t NeedClose = FALSE;
+
+ fprintf(file, "\t");
+ if (rt->rtServerReturn != argNULL)
+ {
+ const argument_t *arg = rt->rtServerReturn;
+ const ipc_type_t *it = arg->argType;
+
+ fprintf(file, "OutP->%s = ", arg->argMsgField);
+ if (it->itOutTrans != strNULL)
+ {
+ fprintf(file, "%s(", it->itOutTrans);
+ NeedClose = TRUE;
+ }
+ }
+ fprintf(file, "%s(", rt->rtServerName);
+ WriteList(file, rt->rtArgs, WriteServerCallArg, akbServerArg, ", ", "");
+ if (NeedClose)
+ fprintf(file, ")");
+ fprintf(file, ");\n");
+}
+
+static void
+WriteGetReturnValue(FILE *file, register const routine_t *rt)
+{
+ if (rt->rtServerReturn != rt->rtRetCode)
+ fprintf(file, "\tOutP->%s = KERN_SUCCESS;\n",
+ rt->rtRetCode->argMsgField);
+}
+
+static void
+WriteCheckReturnValue(FILE *file, register const routine_t *rt)
+{
+ if (rt->rtServerReturn == rt->rtRetCode)
+ {
+ fprintf(file, "\tif (OutP->%s != KERN_SUCCESS)\n",
+ rt->rtRetCode->argMsgField);
+ fprintf(file, "\t\treturn;\n");
+ }
+}
+
+static void
+WritePackArgType(FILE *file, register const argument_t *arg)
+{
+ fprintf(file, "\n");
+
+ WritePackMsgType(file, arg->argType,
+ arg->argType->itIndefinite ? d_NO : arg->argDeallocate,
+ arg->argLongForm, !IsKernelServer,
+ "OutP->%s", "%s", arg->argTTName);
+}
+
+static void
+WritePackArgValue(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *it = arg->argType;
+
+ fprintf(file, "\n");
+
+ if (it->itInLine && it->itVarArray) {
+
+ if (it->itString) {
+ /*
+ * Copy variable-size C string with mig_strncpy.
+ * Save the string length (+ 1 for trailing 0)
+ * in the argument`s count field.
+ */
+ fprintf(file,
+ "\tOutP->%s = %smig_strncpy(OutP->%s, %s, %d);\n",
+ arg->argCount->argMsgField,
+ SubrPrefix,
+ arg->argMsgField,
+ arg->argVarName,
+ it->itNumber);
+ }
+ else {
+ register argument_t *count = arg->argCount;
+ register ipc_type_t *btype = it->itElement;
+
+ /* Note btype->itNumber == count->argMultiplier */
+
+ if (it->itIndefinite) {
+ /*
+ * If we are packing argument, it must be from
+ * a local variable.
+ */
+ fprintf(file, "\tif (%sP != %s) {\n",
+ arg->argVarName,
+ arg->argVarName);
+ fprintf(file, "\t\tOutP->%s%s.msgt_inline = FALSE;\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ if (arg->argDeallocate == d_YES)
+ fprintf(file, "\t\tOutP->%s%s.msgt_deallocate = TRUE;\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ else if (arg->argDeallocate == d_MAYBE)
+ fprintf(file, "\t\tOutP->%s%s.msgt_deallocate = %s;\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "",
+ arg->argDealloc->argVarName);
+ fprintf(file, "\t\t*((%s **)OutP->%s) = %sP;\n",
+ FetchServerType(btype),
+ arg->argMsgField,
+ arg->argVarName);
+ if (!arg->argRoutine->rtSimpleFixedReply)
+ fprintf(file, "\t\tmsgh_simple = FALSE;\n");
+ fprintf(file, "\t}\n\telse {\n\t");
+ }
+ fprintf(file, "\tmemcpy(OutP->%s, %s, ",
+ arg->argMsgField, arg->argVarName);
+ if (btype->itTypeSize > 1)
+ fprintf(file, "%d * ",
+ btype->itTypeSize);
+ fprintf(file, "%s);\n",
+ count->argVarName);
+ if (it->itIndefinite)
+ fprintf(file, "\t}\n");
+ }
+ }
+ else if (arg->argMultiplier > 1)
+ WriteCopyType(file, it, "OutP->%s", "/* %s */ %d * %s",
+ arg->argMsgField,
+ arg->argMultiplier,
+ arg->argVarName);
+ else if (it->itOutTrans != strNULL)
+ WriteCopyType(file, it, "OutP->%s", "/* %s */ %s(%s)",
+ arg->argMsgField, it->itOutTrans, arg->argVarName);
+ else
+ WriteCopyType(file, it, "OutP->%s", "/* %s */ %s",
+ arg->argMsgField, arg->argVarName);
+}
+
+static void
+WriteCopyArgValue(FILE *file, register const argument_t *arg)
+{
+ fprintf(file, "\n");
+ WriteCopyType(file, arg->argType, "/* %d */ OutP->%s", "In%dP->%s",
+ arg->argRequestPos, arg->argMsgField);
+}
+
+static void
+WriteAdjustMsgSimple(FILE *file, register const argument_t *arg)
+{
+ /* akbVarNeeded must be on */
+
+ if (!arg->argRoutine->rtSimpleFixedReply)
+ {
+ fprintf(file, "\n");
+ fprintf(file, "\tif (MACH_MSG_TYPE_PORT_ANY(%s))\n", arg->argVarName);
+ fprintf(file, "\t\tmsgh_simple = FALSE;\n");
+ }
+}
+
+static void
+WriteAdjustMsgCircular(FILE *file, register const argument_t *arg)
+{
+ fprintf(file, "\n");
+
+ if (arg->argType->itOutName == MACH_MSG_TYPE_POLYMORPHIC)
+ fprintf(file, "\tif (%s == MACH_MSG_TYPE_PORT_RECEIVE)\n",
+ arg->argPoly->argVarName);
+
+ /*
+ * The carried port right can be accessed in OutP->XXXX. Normally
+ * the server function stuffs it directly there. If it is InOut,
+ * then it has already been copied into the reply message.
+ * If the server function deposited it into a variable (perhaps
+ * because the reply message is variable-sized) then it has already
+ * been copied into the reply message. Note we must use InHeadP
+ * (or In0P->Head) and OutHeadP to access the message headers,
+ * because of the variable-sized messages.
+ */
+
+ fprintf(file, "\tif (IP_VALID((ipc_port_t) InHeadP->msgh_reply_port) &&\n");
+ fprintf(file, "\t IP_VALID((ipc_port_t) OutP->%s) &&\n", arg->argMsgField);
+ fprintf(file, "\t %sipc_port_check_circularity((ipc_port_t) OutP->%s, (ipc_port_t) InHeadP->msgh_reply_port))\n",
+ SubrPrefix, arg->argMsgField);
+ fprintf(file, "\t\tOutHeadP->msgh_bits |= MACH_MSGH_BITS_CIRCULAR;\n");
+}
+
+/*
+ * Calculate the size of a variable-length message field.
+ */
+static void
+WriteArgSize(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *ptype = arg->argType;
+ register int bsize = ptype->itElement->itTypeSize;
+ register const argument_t *count = arg->argCount;
+
+ if (ptype->itIndefinite) {
+ /*
+ * Check descriptor. If out-of-line, use standard size.
+ */
+ fprintf(file, "(OutP->%s%s.msgt_inline) ? ",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ }
+
+ if (bsize % 4 != 0)
+ fprintf(file, "(");
+
+ if (bsize > 1)
+ fprintf(file, "%d * ", bsize);
+ if (ptype->itString)
+ /* get count from descriptor in message */
+ fprintf(file, "OutP->%s", count->argMsgField);
+ else
+ /* get count from argument */
+ fprintf(file, "%s", count->argVarName);
+
+ /*
+ * If the base type size is not a multiple of sizeof(int) [4],
+ * we have to round up.
+ */
+ if (bsize % 4 != 0)
+ fprintf(file, " + 3) & ~3");
+
+ if (ptype->itIndefinite) {
+ fprintf(file, " : sizeof(%s *)",
+ FetchServerType(ptype->itElement));
+ }
+}
+
+/*
+ * Adjust message size and advance reply pointer.
+ * Called after packing a variable-length argument that
+ * has more arguments following.
+ */
+static void
+WriteAdjustMsgSize(FILE *file, register const argument_t *arg)
+{
+ register routine_t *rt = arg->argRoutine;
+ register ipc_type_t *ptype = arg->argType;
+
+ /* There are more Out arguments. We need to adjust msgh_size
+ and advance OutP, so we save the size of the current field
+ in msgh_size_delta. */
+
+ fprintf(file, "\tmsgh_size_delta = ");
+ WriteArgSize(file, arg);
+ fprintf(file, ";\n");
+
+ if (rt->rtNumReplyVar == 1)
+ /* We can still address the message header directly. Fill
+ in the size field. */
+
+ fprintf(file, "\tOutP->Head.msgh_size = %d + msgh_size_delta;\n",
+ rt->rtReplySize);
+ else
+ if (arg->argReplyPos == 0)
+ /* First variable-length argument. The previous msgh_size value
+ is the minimum reply size. */
+
+ fprintf(file, "\tmsgh_size = %d + msgh_size_delta;\n",
+ rt->rtReplySize);
+ else
+ fprintf(file, "\tmsgh_size += msgh_size_delta;\n");
+
+ fprintf(file,
+ "\tOutP = (Reply *) ((char *) OutP + msgh_size_delta - %d);\n",
+ ptype->itTypeSize + ptype->itPadSize);
+}
+
+/*
+ * Calculate the size of the message. Called after the
+ * last argument has been packed.
+ */
+static void
+WriteFinishMsgSize(FILE *file, register const argument_t *arg)
+{
+ /* No more Out arguments. If this is the only variable Out
+ argument, we can assign to msgh_size directly. */
+
+ if (arg->argReplyPos == 0) {
+ fprintf(file, "\tOutP->Head.msgh_size = %d + (",
+ arg->argRoutine->rtReplySize);
+ WriteArgSize(file, arg);
+ fprintf(file, ");\n");
+ }
+ else {
+ fprintf(file, "\tmsgh_size += ");
+ WriteArgSize(file, arg);
+ fprintf(file, ";\n");
+ }
+}
+
+static void
+WritePackArg(FILE *file, register const argument_t *arg)
+{
+ if (akCheck(arg->argKind, akbReplyInit))
+ WritePackArgType(file, arg);
+
+ if ((akIdent(arg->argKind) == akePoly) &&
+ akCheck(arg->argKind, akbReturnSnd))
+ WriteAdjustMsgSimple(file, arg);
+
+ if (akCheckAll(arg->argKind, akbReturnSnd|akbVarNeeded))
+ WritePackArgValue(file, arg);
+ else if (akCheckAll(arg->argKind, akbReturnSnd|akbVariable)) {
+ register const ipc_type_t *it = arg->argType;
+
+ if (it->itString) {
+ /* Need to call strlen to calculate the size of the argument. */
+ fprintf(file, "\tOutP->%s = strlen(OutP->%s) + 1;\n",
+ arg->argCount->argMsgField, arg->argMsgField);
+ } else if (it->itIndefinite) {
+ /*
+ * We know that array is in reply message.
+ */
+ fprintf(file, "\tif (%sP != OutP->%s) {\n",
+ arg->argVarName,
+ arg->argMsgField);
+ fprintf(file, "\t\tOutP->%s%s.msgt_inline = FALSE;\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ if (arg->argDeallocate == d_YES)
+ fprintf(file, "\t\tOutP->%s%s.msgt_deallocate = TRUE;\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ else if (arg->argDeallocate == d_MAYBE)
+ fprintf(file, "\t\tOutP->%s%s.msgt_deallocate = %s;\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "",
+ arg->argDealloc->argVarName);
+ fprintf(file, "\t\t*((%s **)OutP->%s) = %sP;\n",
+ FetchServerType(it->itElement),
+ arg->argMsgField,
+ arg->argVarName);
+ if (!arg->argRoutine->rtSimpleFixedReply)
+ fprintf(file, "\t\tmsgh_simple = FALSE;\n");
+ fprintf(file, "\t}\n");
+ }
+ }
+
+ if (akCheck(arg->argKind, akbReplyCopy))
+ WriteCopyArgValue(file, arg);
+
+ /*
+ * If this is a KernelServer, and the reply message contains
+ * a receive right, we must check for the possibility of a
+ * port/message circularity. If queueing the reply message
+ * would cause a circularity, we mark the reply message
+ * with the circular bit.
+ */
+
+ if (IsKernelServer &&
+ akCheck(arg->argKind, akbReturnSnd) &&
+ ((arg->argType->itOutName == MACH_MSG_TYPE_PORT_RECEIVE) ||
+ (arg->argType->itOutName == MACH_MSG_TYPE_POLYMORPHIC)))
+ WriteAdjustMsgCircular(file, arg);
+}
+
+/*
+ * Handle reply arguments - fill in message types and copy arguments
+ * that need to be copied.
+ */
+static void
+WritePackReplyArgs(FILE *file, register const routine_t *rt)
+{
+ register const argument_t *arg;
+ register const argument_t *lastVarArg;
+
+ lastVarArg = argNULL;
+ for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) {
+
+ /*
+ * Adjust message size and advance message pointer if
+ * the last reply argument was variable-length and the
+ * request position will change.
+ */
+ if (lastVarArg != argNULL &&
+ lastVarArg->argReplyPos < arg->argReplyPos)
+ {
+ WriteAdjustMsgSize(file, lastVarArg);
+ lastVarArg = argNULL;
+ }
+
+ /*
+ * Copy the argument
+ */
+ WritePackArg(file, arg);
+
+ /*
+ * Remember whether this was variable-length.
+ */
+ if (akCheckAll(arg->argKind, akbReturnSnd|akbVariable))
+ lastVarArg = arg;
+ }
+
+ /*
+ * Finish the message size.
+ */
+ if (lastVarArg != argNULL)
+ WriteFinishMsgSize(file, lastVarArg);
+}
+
+static void
+WriteFieldDecl(FILE *file, const argument_t *arg)
+{
+ WriteFieldDeclPrim(file, arg, FetchServerType);
+}
+
+static void
+WriteRoutine(FILE *file, register const routine_t *rt)
+{
+ fprintf(file, "\n");
+
+ fprintf(file, "/* %s %s */\n", rtRoutineKindToStr(rt->rtKind), rt->rtName);
+ fprintf(file, "mig_internal void _X%s\n", rt->rtName);
+ fprintf(file, "\t(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)\n");
+
+ fprintf(file, "{\n");
+ WriteStructDecl(file, rt->rtArgs, WriteFieldDecl, akbRequest, "Request");
+ WriteStructDecl(file, rt->rtArgs, WriteFieldDecl, akbReply, "Reply");
+
+ WriteVarDecls(file, rt);
+
+ WriteList(file, rt->rtArgs, WriteCheckDecl, akbRequestQC, "\n", "\n");
+ WriteList(file, rt->rtArgs,
+ IsKernelServer ? WriteTypeDeclOut : WriteTypeDeclIn,
+ akbReplyInit, "\n", "\n");
+
+ WriteList(file, rt->rtArgs, WriteLocalVarDecl,
+ akbVarNeeded, ";\n", ";\n\n");
+ WriteList(file, rt->rtArgs, WriteLocalPtrDecl,
+ akbPointer, ";\n", ";\n\n");
+
+ WriteCheckHead(file, rt);
+
+ WriteTypeCheckRequestArgs(file, rt);
+ WriteList(file, rt->rtArgs, WriteExtractArg, akbNone, "", "");
+
+ WriteServerCall(file, rt);
+ WriteGetReturnValue(file, rt);
+
+ WriteReverseList(file, rt->rtArgs, WriteDestroyArg, akbDestroy, "", "");
+
+ /*
+ * For one-way routines, it doesn`t make sense to check the return
+ * code, because we return immediately afterwards. However,
+ * kernel servers may want to deallocate port arguments - and the
+ * deallocation must not be done if the return code is not KERN_SUCCESS.
+ */
+ if (rt->rtOneWay || rt->rtNoReplyArgs)
+ {
+ if (IsKernelServer)
+ {
+ if (rtCheckMaskFunction(rt->rtArgs, akbSendBody|akbSendRcv,
+ CheckDestroyPortArg))
+ {
+ WriteCheckReturnValue(file, rt);
+ }
+ WriteReverseList(file, rt->rtArgs, WriteDestroyPortArg,
+ akbSendBody|akbSendRcv, "", "");
+ }
+ }
+ else
+ {
+ WriteCheckReturnValue(file, rt);
+
+ if (IsKernelServer)
+ WriteReverseList(file, rt->rtArgs, WriteDestroyPortArg,
+ akbSendBody|akbSendRcv, "", "");
+
+ WriteReplyInit(file, rt);
+ WritePackReplyArgs(file, rt);
+ WriteReplyHead(file, rt);
+ }
+
+ fprintf(file, "}\n");
+}
+
+void
+WriteServer(FILE *file, const statement_t *stats)
+{
+ register const statement_t *stat;
+
+ WriteProlog(file);
+ for (stat = stats; stat != stNULL; stat = stat->stNext)
+ switch (stat->stKind)
+ {
+ case skRoutine:
+ WriteRoutine(file, stat->stRoutine);
+ break;
+ case skImport:
+ case skSImport:
+ WriteImport(file, stat->stFileName);
+ break;
+ case skUImport:
+ break;
+ default:
+ fatal("WriteServer(): bad statement_kind_t (%d)",
+ (int) stat->stKind);
+ }
+ WriteEpilog(file, stats);
+}