summaryrefslogtreecommitdiff
path: root/mig/user.c
diff options
context:
space:
mode:
Diffstat (limited to 'mig/user.c')
-rw-r--r--mig/user.c1332
1 files changed, 1332 insertions, 0 deletions
diff --git a/mig/user.c b/mig/user.c
new file mode 100644
index 0000000..a5ca5fb
--- /dev/null
+++ b/mig/user.c
@@ -0,0 +1,1332 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+
+#include <assert.h>
+
+#include "write.h"
+#include "error.h"
+#include "utils.h"
+#include "global.h"
+#include "mig_string.h"
+#include "cpu.h"
+
+/*************************************************************
+ * Writes the standard includes. The subsystem specific
+ * includes are in <SubsystemName>.h and writen by
+ * header:WriteHeader. Called by WriteProlog.
+ *************************************************************/
+static void
+WriteIncludes(FILE *file)
+{
+ if (IsKernelServer)
+ {
+ /*
+ * We want to get the user-side definitions of types
+ * like task_t, ipc_space_t, etc. in mach/mach_types.h.
+ */
+
+ fprintf(file, "#undef\tKERNEL\n");
+
+ if (InternalHeaderFileName != strNULL)
+ {
+ register char *cp;
+
+ /* Strip any leading path from InternalHeaderFileName. */
+ cp = strrchr(InternalHeaderFileName, '/');
+ if (cp == 0)
+ cp = InternalHeaderFileName;
+ else
+ cp++; /* skip '/' */
+ fprintf(file, "#include \"%s\"\n", cp);
+ }
+ }
+
+ if (UserHeaderFileName != strNULL)
+ {
+ register char *cp;
+
+ /* Strip any leading path from UserHeaderFileName. */
+ cp = strrchr(UserHeaderFileName, '/');
+ if (cp == 0)
+ cp = UserHeaderFileName;
+ else
+ cp++; /* skip '/' */
+ fprintf(file, "#include \"%s\"\n", cp);
+ }
+
+ 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/notify.h>\n");
+ fprintf(file, "#include <mach/mach_types.h>\n");
+ fprintf(file, "#include <mach/mig_errors.h>\n");
+ fprintf(file, "#include <mach/mig_support.h>\n");
+ fprintf(file, "#include <mach/msg_type.h>\n");
+ fprintf(file, "/* LINTLIBRARY */\n");
+ fprintf(file, "\n");
+}
+
+static void
+WriteGlobalDecls(FILE *file)
+{
+ if (RCSId != strNULL)
+ WriteRCSDecl(file, strconcat(SubsystemName, "_user"), RCSId);
+
+ fprintf(file, "#define msgh_request_port\tmsgh_remote_port\n");
+ fprintf(file, "#define msgh_reply_port\t\tmsgh_local_port\n");
+ fprintf(file, "\n");
+}
+
+/*************************************************************
+ * Writes the standard #includes, #defines, and
+ * RCS declaration. Called by WriteUser.
+ *************************************************************/
+static void
+WriteProlog(FILE *file)
+{
+ WriteIncludes(file);
+ WriteBogusDefines(file);
+ WriteGlobalDecls(file);
+}
+
+/*ARGSUSED*/
+static void
+WriteEpilog(FILE *file)
+{
+}
+
+static const_string_t
+WriteHeaderPortType(const argument_t *arg)
+{
+ if (arg->argType->itInName == MACH_MSG_TYPE_POLYMORPHIC)
+ return arg->argPoly->argVarName;
+ else
+ return arg->argType->itInNameStr;
+}
+
+static void
+WriteRequestHead(FILE *file, const routine_t *rt)
+{
+ if (rt->rtMaxRequestPos > 0)
+ fprintf(file, "\tInP = &Mess.In;\n");
+
+ if (rt->rtSimpleFixedRequest) {
+ fprintf(file, "\tInP->Head.msgh_bits =");
+ if (!rt->rtSimpleSendRequest)
+ fprintf(file, " MACH_MSGH_BITS_COMPLEX|");
+ fprintf(file, "\n");
+ fprintf(file, "\t\tMACH_MSGH_BITS(%s, %s);\n",
+ WriteHeaderPortType(rt->rtRequestPort),
+ WriteHeaderPortType(rt->rtUReplyPort));
+ } else {
+ fprintf(file, "\tInP->Head.msgh_bits = msgh_simple ?\n");
+ fprintf(file, "\t\tMACH_MSGH_BITS(%s, %s) :\n",
+ WriteHeaderPortType(rt->rtRequestPort),
+ WriteHeaderPortType(rt->rtUReplyPort));
+ fprintf(file, "\t\t(MACH_MSGH_BITS_COMPLEX|\n");
+ fprintf(file, "\t\t MACH_MSGH_BITS(%s, %s));\n",
+ WriteHeaderPortType(rt->rtRequestPort),
+ WriteHeaderPortType(rt->rtUReplyPort));
+ }
+
+ fprintf(file, "\t/* msgh_size passed as argument */\n");
+
+ /*
+ * KernelUser stubs need to cast the request and reply ports
+ * from ipc_port_t to mach_port_t.
+ */
+
+ if (IsKernelUser)
+ fprintf(file, "\tInP->%s = (mach_port_t) %s;\n",
+ rt->rtRequestPort->argMsgField,
+ rt->rtRequestPort->argVarName);
+ else
+ fprintf(file, "\tInP->%s = %s;\n",
+ rt->rtRequestPort->argMsgField,
+ rt->rtRequestPort->argVarName);
+
+ if (akCheck(rt->rtUReplyPort->argKind, akbUserArg)) {
+ if (IsKernelUser)
+ fprintf(file, "\tInP->%s = (mach_port_t) %s;\n",
+ rt->rtUReplyPort->argMsgField,
+ rt->rtUReplyPort->argVarName);
+ else
+ fprintf(file, "\tInP->%s = %s;\n",
+ rt->rtUReplyPort->argMsgField,
+ rt->rtUReplyPort->argVarName);
+ } else if (rt->rtOneWay || IsKernelUser)
+ fprintf(file, "\tInP->%s = MACH_PORT_NULL;\n",
+ rt->rtUReplyPort->argMsgField);
+ else
+ fprintf(file, "\tInP->%s = %smig_get_reply_port();\n",
+ rt->rtUReplyPort->argMsgField, SubrPrefix);
+
+ fprintf(file, "\tInP->Head.msgh_seqno = 0;\n");
+ fprintf(file, "\tInP->Head.msgh_id = %d;\n", rt->rtNumber + SubsystemBase);
+}
+
+/*************************************************************
+ * Writes declarations for the message types, variables
+ * and return variable if needed. Called by WriteRoutine.
+ *************************************************************/
+static void
+WriteVarDecls(FILE *file, const routine_t *rt)
+{
+ fprintf(file, "\tunion {\n");
+ fprintf(file, "\t\tRequest In;\n");
+ if (!rt->rtOneWay)
+ fprintf(file, "\t\tReply Out;\n");
+ fprintf(file, "\t} Mess;\n");
+ fprintf(file, "\n");
+
+ fprintf(file, "\tregister Request *InP = &Mess.In;\n");
+ if (!rt->rtOneWay)
+ fprintf(file, "\tregister Reply *OutP = &Mess.Out;\n");
+ fprintf(file, "\n");
+
+ if (!rt->rtOneWay || rt->rtProcedure)
+ fprintf(file, "\tmach_msg_return_t msg_result;\n");
+
+ if (!rt->rtSimpleFixedRequest)
+ fprintf(file, "\tboolean_t msgh_simple = %s;\n",
+ strbool(rt->rtSimpleSendRequest));
+ else if (!rt->rtOneWay &&
+ !(rt->rtSimpleCheckReply && rt->rtSimpleReceiveReply)) {
+ fprintf(file, "#if\tTypeCheck\n");
+ fprintf(file, "\tboolean_t msgh_simple;\n");
+ fprintf(file, "#endif\t/* TypeCheck */\n");
+ }
+
+ if (rt->rtNumRequestVar > 0)
+ fprintf(file, "\tunsigned int msgh_size;\n");
+ else if (!rt->rtOneWay && !rt->rtNoReplyArgs)
+ {
+ fprintf(file, "#if\tTypeCheck\n");
+ fprintf(file, "\tunsigned int msgh_size;\n");
+ fprintf(file, "#endif\t/* TypeCheck */\n");
+ }
+
+ /* if either request or reply is variable, we need msgh_size_delta */
+ if ((rt->rtMaxRequestPos > 0) ||
+ (rt->rtMaxReplyPos > 0))
+ fprintf(file, "\tunsigned int msgh_size_delta;\n");
+
+ fprintf(file, "\n");
+}
+
+/*************************************************************
+ * Writes code to call the user provided error procedure
+ * when a MIG error occurs. Called by WriteMsgSend,
+ * WriteMsgCheckReceive, WriteMsgSendReceive, WriteCheckIdentity,
+ * WriteRetCodeCheck, WriteTypeCheck, WritePackArgValue.
+ *************************************************************/
+static void
+WriteMsgError(FILE *file, const routine_t *rt, const char *error_msg)
+{
+ if (rt->rtProcedure)
+ fprintf(file, "\t\t{ %s(%s); return; }\n", rt->rtErrorName, error_msg);
+ else if (rt->rtReturn != rt->rtRetCode)
+ {
+ fprintf(file, "\t\t{ %s(%s); ", rt->rtErrorName, error_msg);
+ if (rt->rtNumReplyVar > 0)
+ fprintf(file, "OutP = &Mess.Out; ");
+ fprintf(file, "return OutP->%s; }\n", rt->rtReturn->argMsgField);
+ }
+ else
+ fprintf(file, "\t\treturn %s;\n", error_msg);
+}
+
+/*************************************************************
+ * Writes the send call when there is to be no subsequent
+ * receive. Called by WriteRoutine for SimpleProcedures
+ * or SimpleRoutines
+ *************************************************************/
+static void
+WriteMsgSend(FILE *file, const routine_t *rt)
+{
+ const char *MsgResult = (rt->rtProcedure)
+ ? "msg_result ="
+ : "return";
+
+ char SendSize[24];
+
+ if (rt->rtNumRequestVar == 0)
+ sprintf(SendSize, "%d", rt->rtRequestSize);
+ else
+ strcpy(SendSize, "msgh_size");
+
+ if (IsKernelUser)
+ {
+ fprintf(file, "\t%s %smach_msg_send_from_kernel(",
+ MsgResult, SubrPrefix);
+ fprintf(file, "&InP->Head, %s);\n", SendSize);
+ }
+ else
+ {
+ fprintf(file, "\t%s %smach_msg(&InP->Head, MACH_SEND_MSG|%s, %s, 0,",
+ MsgResult,
+ SubrPrefix,
+ rt->rtMsgOption->argVarName,
+ SendSize);
+ fprintf(file,
+ " MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n"
+ );
+ }
+
+ if (rt->rtProcedure)
+ {
+ fprintf(file, "\tif (msg_result != MACH_MSG_SUCCESS)\n");
+ WriteMsgError(file, rt, "msg_result");
+ }
+}
+
+/*************************************************************
+ * Writes to code to check for error returns from receive.
+ * Called by WriteMsgSendReceive and WriteMsgRPC
+ *************************************************************/
+static void
+WriteMsgCheckReceive(FILE *file, const routine_t *rt, const char *success)
+{
+ fprintf(file, "\tif (msg_result != %s) {\n", success);
+ if (!akCheck(rt->rtUReplyPort->argKind, akbUserArg) && !IsKernelUser)
+ {
+ /* If we aren't using a user-supplied reply port,
+ then deallocate the reply port on any message transmission
+ errors. */
+ fprintf(file, "\t\t%smig_dealloc_reply_port(%s);\n",
+ SubrPrefix, "InP->Head.msgh_reply_port");
+ }
+ WriteMsgError(file, rt, "msg_result");
+ fprintf(file, "\t}\n");
+
+ /*
+ * If not using a user supplied reply port, tell the port
+ * allocator we're done with the port.
+ */
+ if (!akCheck(rt->rtUReplyPort->argKind, akbUserArg) && !IsKernelUser)
+ {
+ fprintf(file, "\t%smig_put_reply_port(InP->Head.msgh_reply_port);\n",
+ SubrPrefix);
+ }
+}
+
+/*************************************************************
+ * Writes the send and receive calls and code to check
+ * for errors. Normally the rpc code is generated instead
+ * although, the subsytem can be compiled with the -R option
+ * which will cause this code to be generated. Called by
+ * WriteRoutine if UseMsgRPC option is false.
+ *************************************************************/
+static void
+WriteMsgSendReceive(FILE *file, const routine_t *rt)
+{
+ char SendSize[24];
+
+ if (rt->rtNumRequestVar == 0)
+ sprintf(SendSize, "%d", rt->rtRequestSize);
+ else
+ strcpy(SendSize, "msgh_size");
+
+ fprintf(file, "\tmsg_result = %smach_msg(&InP->Head, MACH_SEND_MSG|%s, %s, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n",
+ SubrPrefix,
+ rt->rtMsgOption->argVarName,
+ SendSize);
+
+ fprintf(file, "\tif (msg_result != MACH_MSG_SUCCESS)\n");
+ WriteMsgError(file, rt, "msg_result");
+ fprintf(file, "\n");
+
+ fprintf(file, "\tmsg_result = %smach_msg(&OutP->Head, MACH_RCV_MSG|%s%s, 0, sizeof(Reply), InP->Head.msgh_local_port, %s, MACH_PORT_NULL);\n",
+ SubrPrefix,
+ rt->rtMsgOption->argVarName,
+ rt->rtWaitTime != argNULL ? "|MACH_RCV_TIMEOUT" : "",
+ rt->rtWaitTime != argNULL ? rt->rtWaitTime->argVarName : "MACH_MSG_TIMEOUT_NONE");
+ WriteMsgCheckReceive(file, rt, "MACH_MSG_SUCCESS");
+ fprintf(file, "\n");
+}
+
+/*************************************************************
+ * Writes the rpc call and the code to check for errors.
+ * This is the default code to be generated. Called by WriteRoutine
+ * for all routine types except SimpleProcedure and SimpleRoutine.
+ *************************************************************/
+static void
+WriteMsgRPC(FILE *file, const routine_t *rt)
+{
+ char SendSize[24];
+
+ if (rt->rtNumRequestVar == 0)
+ sprintf(SendSize, "%d", rt->rtRequestSize);
+ else
+ strcpy(SendSize, "msgh_size");
+
+ if (IsKernelUser)
+ fprintf(file, "\tmsg_result = %smach_msg_rpc_from_kernel(&InP->Head, %s, sizeof(Reply));\n",
+ SubrPrefix,
+ SendSize);
+ else
+ fprintf(file, "\tmsg_result = %smach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|%s%s, %s, sizeof(Reply), InP->Head.msgh_reply_port, %s, MACH_PORT_NULL);\n",
+ SubrPrefix,
+ rt->rtMsgOption->argVarName,
+ rt->rtWaitTime != argNULL ? "|MACH_RCV_TIMEOUT" : "",
+ SendSize,
+ rt->rtWaitTime != argNULL? rt->rtWaitTime->argVarName : "MACH_MSG_TIMEOUT_NONE");
+ WriteMsgCheckReceive(file, rt, "MACH_MSG_SUCCESS");
+ fprintf(file, "\n");
+}
+
+/*************************************************************
+ * Sets the correct value of the dealloc flag and calls
+ * Utils:WritePackMsgType to fill in the ipc msg type word(s)
+ * in the request message. Called by WriteRoutine for each
+ * argument that is to be sent in the request message.
+ *************************************************************/
+static void
+WritePackArgType(FILE *file, const argument_t *arg)
+{
+ WritePackMsgType(file, arg->argType,
+ arg->argType->itIndefinite ? d_NO : arg->argDeallocate,
+ arg->argLongForm, TRUE,
+ "InP->%s", "%s", arg->argTTName);
+ fprintf(file, "\n");
+}
+
+/*************************************************************
+ * Writes code to copy an argument into the request message.
+ * Called by WriteRoutine for each argument that is to placed
+ * in the request message.
+ *************************************************************/
+static void
+WritePackArgValue(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *it = arg->argType;
+ register const char *ref = arg->argByReferenceUser ? "*" : "";
+
+ 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,
+ "\tInP->%s = %smig_strncpy(InP->%s, %s, %d);\n",
+ arg->argCount->argMsgField,
+ SubrPrefix,
+ arg->argMsgField,
+ arg->argVarName,
+ it->itNumber);
+ }
+ else {
+
+ /*
+ * Copy in variable-size inline array with memcpy,
+ * after checking that number of elements doesn`t
+ * exceed declared maximum.
+ */
+ register const argument_t *count = arg->argCount;
+ register const char *countRef = count->argByReferenceUser ? "*" :"";
+ register const ipc_type_t *btype = it->itElement;
+
+ /* Note btype->itNumber == count->argMultiplier */
+
+ fprintf(file, "\tif (%s%s > %d) {\n",
+ countRef, count->argVarName,
+ it->itNumber/btype->itNumber);
+ if (it->itIndefinite) {
+ fprintf(file, "\t\tInP->%s%s.msgt_inline = FALSE;\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ if (arg->argDeallocate == d_YES)
+ fprintf(file, "\t\tInP->%s%s.msgt_deallocate = TRUE;\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ else if (arg->argDeallocate == d_MAYBE)
+ fprintf(file, "\t\tInP->%s%s.msgt_deallocate = %s%s;\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "",
+ arg->argDealloc->argByReferenceUser ? "*" : "",
+ arg->argDealloc->argVarName);
+ fprintf(file, "\t\t*((%s **)InP->%s) = %s%s;\n",
+ FetchUserType(btype),
+ arg->argMsgField,
+ ref, arg->argVarName);
+ if (!arg->argRoutine->rtSimpleFixedRequest)
+ fprintf(file, "\t\tmsgh_simple = FALSE;\n");
+ }
+ else
+ WriteMsgError(file, arg->argRoutine, "MIG_ARRAY_TOO_LARGE");
+
+ fprintf(file, "\t}\n\telse {\n");
+
+ fprintf(file, "\t\tmemcpy(InP->%s, %s%s, ", arg->argMsgField,
+ ref, arg->argVarName);
+ if (btype->itTypeSize > 1)
+ fprintf(file, "%d * ", btype->itTypeSize);
+ fprintf(file, "%s%s);\n",
+ countRef, count->argVarName);
+ fprintf(file, "\t}\n");
+ }
+ }
+ else if (arg->argMultiplier > 1)
+ WriteCopyType(file, it, "InP->%s", "/* %s */ %d * %s%s",
+ arg->argMsgField, arg->argMultiplier,
+ ref, arg->argVarName);
+ else
+ WriteCopyType(file, it, "InP->%s", "/* %s */ %s%s",
+ arg->argMsgField, ref, arg->argVarName);
+ fprintf(file, "\n");
+}
+
+static void
+WriteAdjustMsgSimple(FILE *file, register const argument_t *arg)
+{
+ if (!arg->argRoutine->rtSimpleFixedRequest)
+ {
+ register const char *ref = arg->argByReferenceUser ? "*" : "";
+
+ fprintf(file, "\tif (MACH_MSG_TYPE_PORT_ANY(%s%s))\n",
+ ref, arg->argVarName);
+ fprintf(file, "\t\tmsgh_simple = FALSE;\n");
+ fprintf(file, "\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, "(InP->%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, "InP->%s", count->argMsgField);
+ else
+ /* get count from argument */
+ fprintf(file, "%s%s",
+ count->argByReferenceUser ? "*" : "",
+ 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 *)",
+ FetchUserType(ptype->itElement));
+ }
+}
+
+/*
+ * Adjust message size and advance request pointer.
+ * Called after packing a variable-length argument that
+ * has more arguments following.
+ */
+static void
+WriteAdjustMsgSize(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *ptype = arg->argType;
+
+ /* There are more In arguments. We need to adjust msgh_size
+ and advance InP, 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 (arg->argRequestPos == 0)
+ /* First variable-length argument. The previous msgh_size value
+ is the minimum request size. */
+
+ fprintf(file, "\tmsgh_size = %d + msgh_size_delta;\n",
+ arg->argRoutine->rtRequestSize);
+ else
+ fprintf(file, "\tmsgh_size += msgh_size_delta;\n");
+
+ fprintf(file,
+ "\tInP = (Request *) ((char *) InP + 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 In arguments. If this is the only variable In
+ argument, the previous msgh_size value is the minimum
+ request size. */
+
+ if (arg->argRequestPos == 0) {
+ fprintf(file, "\tmsgh_size = %d + (",
+ arg->argRoutine->rtRequestSize);
+ WriteArgSize(file, arg);
+ fprintf(file, ");\n");
+ }
+ else {
+ fprintf(file, "\tmsgh_size += ");
+ WriteArgSize(file, arg);
+ fprintf(file, ";\n");
+ }
+}
+
+static void
+WriteInitializeCount(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *ptype = arg->argCInOut->argParent->argType;
+ register const ipc_type_t *btype = ptype->itElement;
+
+ fprintf(file, "\tif (%s%s < %d)\n",
+ arg->argByReferenceUser ? "*" : "",
+ arg->argVarName,
+ ptype->itNumber/btype->itNumber);
+ fprintf(file, "\t\tInP->%s = %s%s;\n",
+ arg->argMsgField,
+ arg->argByReferenceUser ? "*" : "",
+ arg->argVarName);
+ fprintf(file, "\telse\n");
+ fprintf(file, "\t\tInP->%s = %d;\n",
+ arg->argMsgField, ptype->itNumber/btype->itNumber);
+ fprintf(file, "\n");
+}
+
+/*
+ * Called for every argument. Responsible for packing that
+ * argument into the request message.
+ */
+static void
+WritePackArg(FILE *file, register const argument_t *arg)
+{
+ if (akCheck(arg->argKind, akbRequest))
+ WritePackArgType(file, arg);
+
+ if ((akIdent(arg->argKind) == akePoly) &&
+ akCheckAll(arg->argKind, akbSendSnd|akbUserArg))
+ WriteAdjustMsgSimple(file, arg);
+
+ if ((akIdent(arg->argKind) == akeCountInOut) &&
+ akCheck(arg->argKind, akbSendSnd))
+ WriteInitializeCount(file, arg);
+ else if (akCheckAll(arg->argKind, akbSendSnd|akbSendBody))
+ WritePackArgValue(file, arg);
+}
+
+/*
+ * Generate code to fill in all of the request arguments and their
+ * message types.
+ */
+static void
+WriteRequestArgs(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 request argument was variable-length and the
+ * request position will change.
+ */
+ if (lastVarArg != argNULL &&
+ lastVarArg->argRequestPos < arg->argRequestPos)
+ {
+ WriteAdjustMsgSize(file, lastVarArg);
+ lastVarArg = argNULL;
+ }
+
+ /*
+ * Copy the argument
+ */
+ WritePackArg(file, arg);
+
+ /*
+ * Remember whether this was variable-length.
+ */
+ if (akCheckAll(arg->argKind, akbSendSnd|akbSendBody|akbVariable))
+ lastVarArg = arg;
+ }
+
+ /*
+ * Finish the message size.
+ */
+ if (lastVarArg != argNULL)
+ WriteFinishMsgSize(file, lastVarArg);
+}
+
+/*************************************************************
+ * Writes code to check that the return msgh_id is correct and that
+ * the size of the return message is correct. Called by
+ * WriteRoutine.
+ *************************************************************/
+static void
+WriteCheckIdentity(FILE *file, const routine_t *rt)
+{
+ fprintf(file, "\tif (OutP->Head.msgh_id != %d) {\n",
+ rt->rtNumber + SubsystemBase + 100);
+ fprintf(file, "\t\tif (OutP->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)\n");
+ WriteMsgError(file, rt, "MIG_SERVER_DIED");
+ fprintf(file, "\t\telse {\n");
+ fprintf(file, "\t\t\t%smig_dealloc_reply_port(%s);\n\t",
+ SubrPrefix,"InP->Head.msgh_reply_port");
+ WriteMsgError(file, rt, "MIG_REPLY_MISMATCH");
+ fprintf(file, "\t\t}\n\t}\n");
+ fprintf(file, "\n");
+ fprintf(file, "#if\tTypeCheck\n");
+
+ if (rt->rtSimpleCheckReply && rt->rtSimpleReceiveReply)
+ {
+ /* Expecting a simple message. We can factor out the check for
+ a simple message, since the error reply message is also simple.
+ */
+
+ if (!rt->rtNoReplyArgs)
+ fprintf(file, "\tmsgh_size = OutP->Head.msgh_size;\n\n");
+
+ fprintf(file,
+ "\tif ((OutP->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||\n");
+ if (rt->rtNoReplyArgs)
+ fprintf(file, "\t (OutP->Head.msgh_size != %d))\n",
+ rt->rtReplySize);
+ else {
+ fprintf(file, "\t ((msgh_size %s %d) &&\n",
+ (rt->rtNumReplyVar > 0) ? "<" : "!=",
+ rt->rtReplySize);
+ fprintf(file, "\t ((msgh_size != sizeof(mig_reply_header_t)) ||\n");
+ fprintf(file, "\t (OutP->RetCode == KERN_SUCCESS))))\n");
+ }
+ }
+ else {
+ /* Expecting a complex message, or may vary at run time. */
+
+ fprintf(file, "\tmsgh_size = OutP->Head.msgh_size;\n");
+ fprintf(file, "\tmsgh_simple = !(OutP->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX);\n");
+ fprintf(file, "\n");
+
+ fprintf(file, "\tif (((msgh_size %s %d)",
+ (rt->rtNumReplyVar > 0) ? "<" : "!=",
+ rt->rtReplySize);
+
+ if (rt->rtSimpleCheckReply)
+ /* if rtSimpleReceiveReply was true, then we would have
+ executed the code above. So we know that the message
+ is complex. */
+ fprintf(file, " || msgh_simple");
+ fprintf(file, ") &&\n");
+
+ fprintf(file, "\t ((msgh_size != sizeof(mig_reply_header_t)) ||\n");
+ fprintf(file, "\t !msgh_simple ||\n");
+ fprintf(file, "\t (OutP->RetCode == KERN_SUCCESS)))\n");
+ }
+ WriteMsgError(file, rt, "MIG_TYPE_ERROR");
+ fprintf(file, "#endif\t/* TypeCheck */\n");
+ fprintf(file, "\n");
+}
+
+/*************************************************************
+ * Write code to generate error handling code if the RetCode
+ * argument of a Routine is not KERN_SUCCESS.
+ *************************************************************/
+static void
+WriteRetCodeCheck(FILE *file, const routine_t *rt)
+{
+ fprintf(file, "\tif (OutP->RetCode != KERN_SUCCESS)\n");
+ WriteMsgError(file, rt, "OutP->RetCode");
+ fprintf(file, "\n");
+}
+
+/*************************************************************
+ * Writes code to check that the type of each of the arguments
+ * in the reply message is what is expected. Called by
+ * WriteRoutine for each argument in the reply message.
+ *************************************************************/
+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, akbReplyQC))
+ {
+ fprintf(file, "\tif (* (int *) &OutP->%s != * (int *) &%sCheck)\n",
+ arg->argTTName, arg->argVarName);
+ }
+ else
+ {
+ fprintf(file, "\tif (");
+ if (!it->itIndefinite) {
+ fprintf(file, "(OutP->%s%s.msgt_inline != %s) ||\n\t ",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "",
+ strbool(it->itInLine));
+ }
+ fprintf(file, "(OutP->%s%s.msgt_longform != %s) ||\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "",
+ strbool(arg->argLongForm));
+ if (it->itOutName == MACH_MSG_TYPE_POLYMORPHIC)
+ {
+ if (!rt->rtSimpleCheckReply)
+ fprintf(file, "\t (MACH_MSG_TYPE_PORT_ANY(OutP->%s.msgt%s_name) && msgh_simple) ||\n",
+ arg->argTTName,
+ arg->argLongForm ? "l" : "");
+ }
+ else
+ fprintf(file, "\t (OutP->%s.msgt%s_name != %s) ||\n",
+ arg->argTTName,
+ arg->argLongForm ? "l" : "",
+ it->itOutNameStr);
+ if (!it->itVarArray)
+ fprintf(file, "\t (OutP->%s.msgt%s_number != %d) ||\n",
+ arg->argTTName,
+ arg->argLongForm ? "l" : "",
+ it->itNumber);
+ fprintf(file, "\t (OutP->%s.msgt%s_size != %d))\n",
+ arg->argTTName,
+ arg->argLongForm ? "l" : "",
+ it->itSize);
+ }
+ WriteMsgError(file, rt, "MIG_TYPE_ERROR");
+ 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, "(OutP->%s%s.msgt_inline) ? ",
+ arg->argTTName, arg->argLongForm ? ".msgtl_header" : "");
+ }
+
+ if (btype->itTypeSize % 4 != 0)
+ fprintf(file, "(");
+
+ if (multiplier > 1)
+ fprintf(file, "%d * ", multiplier);
+
+ fprintf(file, "OutP->%s", 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 *)", FetchUserType(btype));
+}
+
+static void
+WriteCheckMsgSize(FILE *file, register const argument_t *arg)
+{
+ register const routine_t *rt = arg->argRoutine;
+
+ /* If there aren't any more Out args after this, then
+ we can use the msgh_size_delta value directly in
+ the TypeCheck conditional. */
+
+ if (arg->argReplyPos == rt->rtMaxReplyPos)
+ {
+ fprintf(file, "#if\tTypeCheck\n");
+ fprintf(file, "\tif (msgh_size != %d + (",
+ rt->rtReplySize);
+ WriteCheckArgSize(file, arg);
+ fprintf(file, "))\n");
+
+ WriteMsgError(file, rt, "MIG_TYPE_ERROR");
+ 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->argReplyPos+1 == rt->rtNumReplyVar;
+
+ /* 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->rtReplySize);
+ else
+ fprintf(file, "\tif (msgh_size < %d + msgh_size_delta)\n",
+ rt->rtReplySize);
+ WriteMsgError(file, rt, "MIG_TYPE_ERROR");
+
+ if (!LastVarArg)
+ fprintf(file, "\tmsgh_size -= msgh_size_delta;\n");
+
+ fprintf(file, "#endif\t/* TypeCheck */\n");
+ }
+ fprintf(file, "\n");
+}
+
+/*************************************************************
+ * Write code to copy an argument from the reply message
+ * to the parameter. Called by WriteRoutine for each argument
+ * in the reply message.
+ *************************************************************/
+static void
+WriteExtractArgValue(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *argType = arg->argType;
+ register const char *ref = arg->argByReferenceUser ? "*" : "";
+
+ if (argType->itInLine && argType->itVarArray) {
+
+ if (argType->itString) {
+ /*
+ * Copy out variable-size C string with mig_strncpy.
+ */
+ fprintf(file, "\t(void) %smig_strncpy(%s%s, OutP->%s, %d);\n",
+ SubrPrefix,
+ ref,
+ arg->argVarName,
+ arg->argMsgField,
+ argType->itNumber);
+ }
+ else if (argType->itIndefinite) {
+ /*
+ * If data was returned out-of-line,
+ * change user`s pointer to point to it.
+ * If data was returned in-line but doesn`t fit,
+ * allocate a new buffer, copy the data to it,
+ * and change user`s pointer to point to it.
+ * If data was returned in-line and fits,
+ * copy to buffer.
+ */
+ const argument_t *count = arg->argCount;
+ const char *countRef = count->argByReferenceUser ? "*" : "";
+ const ipc_type_t *btype = argType->itElement;
+
+ fprintf(file, "\tif (!OutP->%s%s.msgt_inline)\n",
+ arg->argTTName,
+ arg->argLongForm ? ".msgtl_header" : "");
+ fprintf(file, "\t %s%s = *((%s **)OutP->%s);\n",
+ ref, arg->argVarName,
+ FetchUserType(btype), arg->argMsgField);
+ fprintf(file, "\telse if (OutP->%s", count->argMsgField);
+ if (btype->itNumber > 1)
+ fprintf(file, " / %d", btype->itNumber);
+ fprintf(file, " > %s%s) {\n", countRef, count->argVarName);
+ fprintf(file, "\t %smig_allocate((vm_offset_t *)%s,\n\t\t",
+ SubrPrefix, arg->argVarName); /* no ref! */
+ if (btype->itTypeSize != btype->itNumber)
+ fprintf(file, "%d * ", btype->itTypeSize/btype->itNumber);
+ fprintf(file, "OutP->%s);\n", count->argMsgField);
+ fprintf(file, "\t memcpy(%s%s, OutP->%s, ", ref, arg->argVarName,
+ arg->argMsgField);
+ if (btype->itTypeSize != btype->itNumber)
+ fprintf(file, "%d * ", btype->itTypeSize/btype->itNumber);
+ fprintf(file, "OutP->%s);\n", count->argMsgField);
+ fprintf(file, "\t}\n");
+ fprintf(file, "\telse {\n");
+
+ fprintf(file, "\t memcpy(%s%s, OutP->%s, ", ref, arg->argVarName,
+ arg->argMsgField);
+ if (btype->itTypeSize != btype->itNumber)
+ fprintf(file, "%d * ", btype->itTypeSize/btype->itNumber);
+ fprintf(file, "OutP->%s);\n", count->argMsgField);
+ fprintf(file, "\t}\n");
+ }
+ else {
+
+ /*
+ * Copy out variable-size inline array with memcpy,
+ * after checking that number of elements doesn`t
+ * exceed user`s maximum.
+ */
+ register const argument_t *count = arg->argCount;
+ register const char *countRef = count->argByReferenceUser ? "*" :"";
+ register const ipc_type_t *btype = argType->itElement;
+
+ /* Note count->argMultiplier == btype->itNumber */
+
+ fprintf(file, "\tif (OutP->%s", count->argMsgField);
+ if (btype->itNumber > 1)
+ fprintf(file, " / %d", btype->itNumber);
+ fprintf(file, " > %s%s) {\n",
+ countRef, count->argVarName);
+
+ /*
+ * If number of elements is too many for user receiving area,
+ * fill user`s area as much as possible. Return the correct
+ * number of elements.
+ */
+ fprintf(file, "\t\tmemcpy(%s%s, OutP->%s, ", ref, arg->argVarName,
+ arg->argMsgField);
+ if (btype->itTypeSize > 1)
+ fprintf(file, "%d * ", btype->itTypeSize);
+ fprintf(file, "%s%s);\n",
+ countRef, count->argVarName);
+
+ fprintf(file, "\t\t%s%s = OutP->%s",
+ countRef, count->argVarName, count->argMsgField);
+ if (btype->itNumber > 1)
+ fprintf(file, " / %d", btype->itNumber);
+ fprintf(file, ";\n");
+ WriteMsgError(file,arg->argRoutine, "MIG_ARRAY_TOO_LARGE");
+
+ fprintf(file, "\t}\n\telse {\n");
+
+ fprintf(file, "\t\tmemcpy(%s%s, OutP->%s, ", ref, arg->argVarName,
+ arg->argMsgField);
+ if (btype->itTypeSize != btype->itNumber)
+ fprintf(file, "%d * ", btype->itTypeSize/btype->itNumber);
+ fprintf(file, "OutP->%s);\n", count->argMsgField);
+ fprintf(file, "\t}\n");
+ }
+ }
+ else if (arg->argMultiplier > 1)
+ WriteCopyType(file, argType,
+ "%s%s", "/* %s%s */ OutP->%s / %d",
+ ref, arg->argVarName, arg->argMsgField,
+ arg->argMultiplier);
+ else
+ WriteCopyType(file, argType,
+ "%s%s", "/* %s%s */ OutP->%s",
+ ref, arg->argVarName, arg->argMsgField);
+ fprintf(file, "\n");
+}
+
+static void
+WriteExtractArg(FILE *file, register const argument_t *arg)
+{
+ register const routine_t *rt = arg->argRoutine;
+
+ if (akCheck(arg->argKind, akbReply))
+ WriteTypeCheck(file, arg);
+
+ if (akCheckAll(arg->argKind, akbVariable|akbReply))
+ WriteCheckMsgSize(file, arg);
+
+ /* Now that the RetCode is type-checked, check its value.
+ Must abort immediately if it isn't KERN_SUCCESS, because
+ in that case the reply message is truncated. */
+
+ if (arg == rt->rtRetCode)
+ WriteRetCodeCheck(file, rt);
+
+ if (akCheckAll(arg->argKind, akbReturnRcv))
+ WriteExtractArgValue(file, arg);
+}
+
+static void
+WriteAdjustReplyMsgPtr(FILE *file, register const argument_t *arg)
+{
+ register const ipc_type_t *ptype = arg->argType;
+
+ fprintf(file,
+ "\tOutP = (Reply *) ((char *) OutP + msgh_size_delta - %d);\n\n",
+ ptype->itTypeSize + ptype->itPadSize);
+}
+
+static void
+WriteReplyArgs(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 reply argument was
+ * variable-length and the reply position will change.
+ */
+ if (lastVarArg != argNULL &&
+ lastVarArg->argReplyPos < arg->argReplyPos)
+ {
+ WriteAdjustReplyMsgPtr(file, lastVarArg);
+ lastVarArg = argNULL;
+ }
+
+ /*
+ * Copy the argument
+ */
+ WriteExtractArg(file, arg);
+
+ /*
+ * Remember whether this was variable-length.
+ */
+ if (akCheckAll(arg->argKind, akbReturnRcv|akbVariable))
+ lastVarArg = arg;
+ }
+}
+
+/*************************************************************
+ * Writes code to return the return value. Called by WriteRoutine
+ * for routines and functions.
+ *************************************************************/
+static void
+WriteReturnValue(FILE *file, const routine_t *rt)
+{
+ if (rt->rtReturn == rt->rtRetCode)
+ /* If returning RetCode, we have already checked that it is
+ KERN_SUCCESS */
+ fprintf(file, "\treturn KERN_SUCCESS;\n");
+
+ else
+ {
+ if (rt->rtNumReplyVar > 0)
+ fprintf(file, "\tOutP = &Mess.Out;\n");
+
+ fprintf(file, "\treturn OutP->%s;\n", rt->rtReturn->argMsgField);
+ }
+}
+
+/*************************************************************
+ * Writes the elements of the message type declaration: the
+ * msg_type structure, the argument itself and any padding
+ * that is required to make the argument a multiple of 4 bytes.
+ * Called by WriteRoutine for all the arguments in the request
+ * message first and then the reply message.
+ *************************************************************/
+static void
+WriteFieldDecl(FILE *file, const argument_t *arg)
+{
+ WriteFieldDeclPrim(file, arg, FetchUserType);
+}
+
+static void
+WriteStubDecl(FILE *file, register const routine_t *rt)
+{
+ fprintf(file, "\n");
+ fprintf(file, "/* %s %s */\n", rtRoutineKindToStr(rt->rtKind), rt->rtName);
+ fprintf(file, "mig_external %s %s\n", ReturnTypeStr(rt), rt->rtUserName);
+ fprintf(file, "(\n");
+ WriteList(file, rt->rtArgs, WriteUserVarDecl, akbUserArg, ",\n", "\n");
+ fprintf(file, ")\n");
+ fprintf(file, "{\n");
+}
+
+/*************************************************************
+ * Writes all the code comprising a routine body. Called by
+ * WriteUser for each routine.
+ *************************************************************/
+static void
+WriteRoutine(FILE *file, register const routine_t *rt)
+{
+ /* write the stub's declaration */
+
+ WriteStubDecl(file, rt);
+
+ /* typedef of structure for Request and Reply messages */
+
+ WriteStructDecl(file, rt->rtArgs, WriteFieldDecl, akbRequest, "Request");
+ if (!rt->rtOneWay)
+ WriteStructDecl(file, rt->rtArgs, WriteFieldDecl, akbReply, "Reply");
+
+ /* declarations for local vars: Union of Request and Reply messages,
+ InP, OutP and return value */
+
+ WriteVarDecls(file, rt);
+
+ /* declarations and initializations of the mach_msg_type_t variables
+ for each argument */
+
+ WriteList(file, rt->rtArgs, WriteTypeDeclIn, akbRequest, "\n", "\n");
+ if (!rt->rtOneWay)
+ WriteList(file, rt->rtArgs, WriteCheckDecl, akbReplyQC, "\n", "\n");
+
+ /* fill in all the request message types and then arguments */
+
+ WriteRequestArgs(file, rt);
+
+ /* fill in request message head */
+
+ WriteRequestHead(file, rt);
+ fprintf(file, "\n");
+
+ /* Write the send/receive or rpc call */
+
+ if (rt->rtOneWay)
+ WriteMsgSend(file, rt);
+ else
+ {
+ if (UseMsgRPC)
+ WriteMsgRPC(file, rt);
+ else
+ WriteMsgSendReceive(file, rt);
+
+ /* Check the values that are returned in the reply message */
+
+ WriteCheckIdentity(file, rt);
+
+ /* If the reply message has no Out parameters or return values
+ other than the return code, we can type-check it and
+ return it directly. */
+
+ if (rt->rtNoReplyArgs)
+ {
+ WriteTypeCheck(file, rt->rtRetCode);
+
+ fprintf(file, "\treturn OutP->RetCode;\n");
+ }
+ else {
+ WriteReplyArgs(file, rt);
+
+ /* return the return value, if any */
+
+ if (rt->rtProcedure)
+ fprintf(file, "\t/* Procedure - no return needed */\n");
+ else
+ WriteReturnValue(file, rt);
+ }
+ }
+
+ fprintf(file, "}\n");
+}
+
+/*************************************************************
+ * Writes out the xxxUser.c file. Called by mig.c
+ *************************************************************/
+void
+WriteUser(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 skUImport:
+ WriteImport(file, stat->stFileName);
+ break;
+ case skSImport:
+ break;
+ default:
+ fatal("WriteUser(): bad statement_kind_t (%d)",
+ (int) stat->stKind);
+ }
+ WriteEpilog(file);
+}
+
+/*************************************************************
+ * Writes out individual .c user files for each routine. Called by mig.c
+ *************************************************************/
+void
+WriteUserIndividual(const statement_t *stats)
+{
+ register const statement_t *stat;
+
+ for (stat = stats; stat != stNULL; stat = stat->stNext)
+ switch (stat->stKind)
+ {
+ case skRoutine:
+ {
+ FILE *file;
+ register char *filename;
+
+ filename = strconcat(UserFilePrefix,
+ strconcat(stat->stRoutine->rtName, ".c"));
+ file = fopen(filename, "w");
+ if (file == NULL)
+ fatal("fopen(%s): %s", filename,
+ unix_error_string(errno));
+ WriteProlog(file);
+
+ {
+ /* Write all the imports. */
+ const statement_t *s;
+ for (s = stats; s != stNULL; s = s->stNext)
+ switch (s->stKind)
+ {
+ case skImport:
+ case skUImport:
+ WriteImport(file, s->stFileName);
+ break;
+ }
+ }
+
+ WriteRoutine(file, stat->stRoutine);
+ WriteEpilog(file);
+ fclose(file);
+ strfree(filename);
+ }
+ break;
+ case skImport:
+ case skUImport:
+ break;
+ case skSImport:
+ break;
+ default:
+ fatal("WriteUserIndividual(): bad statement_kind_t (%d)",
+ (int) stat->stKind);
+ }
+}