aboutsummaryrefslogtreecommitdiff
path: root/main/Mac cli
diff options
context:
space:
mode:
authorjohnvg2005-03-17 15:12:46 +0000
committerjohnvg2005-03-17 15:12:46 +0000
commitcdf0c13f4d2628030ccb4b96e2cc590e95aa3901 (patch)
treed70f0cfd09815a5a844d359c9ff8fb35495f787b /main/Mac cli
parenthandle \r as \n when scanning .abc files (diff)
cli for MacOSX with MACH_O, same as unix, except : and , are used
in paths and object files generated by the c compiler have different names git-svn-id: https://svn.cs.ru.nl/repos/clean-compiler/trunk@1519 1f8540f1-abd5-4d5b-9d24-4c5ce8603e2d
Diffstat (limited to 'main/Mac cli')
-rw-r--r--main/Mac cli/Clean.h28
-rw-r--r--main/Mac cli/CoclSystemDependent.dcl28
-rw-r--r--main/Mac cli/CoclSystemDependent.icl122
-rw-r--r--main/Mac cli/Makefile5
-rw-r--r--main/Mac cli/cDirectory.c339
-rw-r--r--main/Mac cli/ipc.c123
-rw-r--r--main/Mac cli/ipc.dcl14
-rw-r--r--main/Mac cli/ipc.h8
-rw-r--r--main/Mac cli/ipc.icl30
-rw-r--r--main/Mac cli/set_return_code.dcl9
-rw-r--r--main/Mac cli/set_return_code.icl18
-rw-r--r--main/Mac cli/set_return_code_c.c6
12 files changed, 730 insertions, 0 deletions
diff --git a/main/Mac cli/Clean.h b/main/Mac cli/Clean.h
new file mode 100644
index 0000000..1508c1b
--- /dev/null
+++ b/main/Mac cli/Clean.h
@@ -0,0 +1,28 @@
+
+#define Clean(a)
+
+typedef struct clean_string *CleanString;
+
+/* a string in Clean is:
+ struct clean_string {
+ int clean_string_length;
+ char clean_string_characters[clean_string_length];
+ };
+ The string does not end with a '\0' !
+*/
+
+/* CleanStringLength(clean_string) returns length of the clean_string in characters */
+#define CleanStringLength(clean_string) (*(unsigned int *)(clean_string))
+
+/* CleanStringCharacters(clean_string) returns a pointer to the characters of the clean_string */
+#define CleanStringCharacters(clean_string) ((char*)(1+(unsigned int *)(clean_string)))
+
+/* CleanStringSizeInts(string_length) return size of CleanString in integers */
+#define CleanStringSizeInts(string_length) (1+(((unsigned int)(string_length)+3)>>2))
+
+/* CleanStringVariable(clean_string,string_length) defines variable clean_string with length string_length,
+ before using the clean_string variable, cast to CleanString, except for the macros above */
+#define CleanStringVariable(clean_string,string_length) unsigned int clean_string[CleanStringSizeInts(string_length)]
+
+/* CleanStringSizeBytes(string_length) return size of CleanString in bytes */
+#define CleanStringSizeBytes(string_length) (4+(((unsigned int)(string_length)+3) & -4))
diff --git a/main/Mac cli/CoclSystemDependent.dcl b/main/Mac cli/CoclSystemDependent.dcl
new file mode 100644
index 0000000..abe673f
--- /dev/null
+++ b/main/Mac cli/CoclSystemDependent.dcl
@@ -0,0 +1,28 @@
+// this is for Windows
+definition module CoclSystemDependent
+
+//1.3
+from StdString import String
+from StdFile import Files
+//3.1
+/*2.0
+from StdFile import ::Files
+0.2*/
+
+// RWS split
+// from deltaIOSystem import DeviceSystem
+// from deltaEventIO import InitialIO, IOState
+
+PathSeparator
+ :== ','
+DirectorySeparator
+ :== ':'
+
+SystemDependentDevices :: [a]
+SystemDependentInitialIO :: [a]
+
+ensureCleanSystemFilesExists :: !String !*Files -> (!Bool, !*Files)
+set_compiler_id :: Int -> Int
+
+compiler_loop :: ([{#Char}] *st -> *(Bool, *st)) *st -> (!Bool, !*st)
+
diff --git a/main/Mac cli/CoclSystemDependent.icl b/main/Mac cli/CoclSystemDependent.icl
new file mode 100644
index 0000000..715b509
--- /dev/null
+++ b/main/Mac cli/CoclSystemDependent.icl
@@ -0,0 +1,122 @@
+// this is for Unix
+implementation module CoclSystemDependent
+
+import StdEnv
+import StdDebug
+import ArgEnv
+import ipc
+from filesystem import ensureDirectoryExists
+
+import code from "cDirectory_c.o"
+import code from "ipc_c.o"
+
+PathSeparator
+ :== ','
+DirectorySeparator
+ :== ':'
+
+SystemDependentDevices :: [a]
+SystemDependentDevices
+ = []
+
+SystemDependentInitialIO :: [a]
+SystemDependentInitialIO
+ = []
+
+set_compiler_id :: Int -> Int
+set_compiler_id compiler_id = compiler_id
+
+ensureCleanSystemFilesExists :: !String !*Files -> (!Bool, !*Files)
+// returned bool: now there is such a subfolder
+ensureCleanSystemFilesExists path env
+ = ensureDirectoryExists path env
+
+
+compiler_loop :: ([{#Char}] *st -> *(Bool, *st)) *st -> (!Bool, !*st)
+compiler_loop compile compile_state
+ | length commandArgs==3 && commandArgs!!0=="--pipe"
+ # commands_name= (commandArgs!!1);
+ # results_name= (commandArgs!!2);
+ = (True,compile_loop compile commands_name results_name compile_state)
+ # (r,compile_state)=compile commandArgs compile_state
+ = (r,compile_state)
+ where
+ commandArgs
+ = tl [arg \\ arg <-: getCommandLine]
+// ... Unix
+
+string_to_args string
+ = string_to_args 0;
+ where
+ l=size string;
+
+ string_to_args i
+ # end_spaces_i=skip_spaces i;
+ | end_spaces_i==l
+ = []
+ | string.[end_spaces_i]=='"'
+ # next_double_quote_i=skip_to_double_quote (end_spaces_i+1)
+ | next_double_quote_i>=l
+ = [string % (end_spaces_i,l-1)]
+ # arg=string % (end_spaces_i+1,next_double_quote_i-1);
+ = [arg : string_to_args (next_double_quote_i+1)];
+ # space_i=skip_to_space (end_spaces_i+1)
+ | space_i>=l
+ = [string % (end_spaces_i,l-1)]
+ # arg=string % (end_spaces_i,space_i-1);
+ = [arg : string_to_args (space_i+1)];
+
+ skip_spaces i
+ | i>=l
+ = l;
+ # c=string.[i];
+ | c==' ' || c=='\t'
+ = skip_spaces (i+1);
+ = i;
+
+ skip_to_space i
+ | i>=l
+ = l;
+ # c=string.[i];
+ | c==' ' || c=='\t'
+ = i;
+ = skip_to_space (i+1);
+
+ skip_to_double_quote i
+ | i>=l
+ = l;
+ # c=string.[i];
+ | c=='"'
+ = i;
+ = skip_to_double_quote (i+1);
+
+
+compile_loop :: ([{#Char}] *st -> *(Bool, *st)) {#Char} {#Char} *st -> *st
+compile_loop compile commands results compile_state
+ # r=open_pipes commands results;
+ | r<>0
+ = abort ("compile_loop\n");
+ = compile_files compile compile_state
+
+compile_files :: ([{#Char}] *st -> *(Bool, *st)) *st -> *st
+compile_files compile compile_state
+ # n = get_command_length;
+ | n==(-1)
+ = abort "compile_files 1";
+ # string=createArray n '\0';
+ # r=get_command string;
+ | r<>0
+ = abort ("compile_files 2 ");
+ # args=string_to_args (string % (0,size string-2))
+ = case args of
+ ["cocl":cocl_args]
+ # (ok,compile_state)=compile cocl_args compile_state
+ # result=if ok 0(-1);
+ # r=send_result result
+ | r<>0
+ -> abort "compile_files 3";
+ -> compile_files compile compile_state
+ ["quit"]
+ -> /* trace_n "quiting" */ compile_state;
+ _
+ -> abort "compile_files 4"
diff --git a/main/Mac cli/Makefile b/main/Mac cli/Makefile
new file mode 100644
index 0000000..f8ae7f3
--- /dev/null
+++ b/main/Mac cli/Makefile
@@ -0,0 +1,5 @@
+CC=gcc
+CFLAGS=-pedantic -Wall -W -O
+CPPFLAGS=
+
+all: cDirectory.o ipc.o set_return_code_c.o
diff --git a/main/Mac cli/cDirectory.c b/main/Mac cli/cDirectory.c
new file mode 100644
index 0000000..b95f19b
--- /dev/null
+++ b/main/Mac cli/cDirectory.c
@@ -0,0 +1,339 @@
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "Clean.h"
+
+#ifndef NAME_MAX
+#define NAME_MAX 500
+#endif
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+// error codes:
+#define NoDirError 0
+#define OtherDirError -1
+#define DoesntExist -2
+#define BadName -3
+#define NotEnoughSpace -4
+#define AlreadyExists -5
+#define NoPermission -6
+#define MoveIntoOffspring -7
+#define MoveAcrossDisks -8
+#define NotYetRemovable -9
+
+uid_t gUserId;
+gid_t gGroupId;
+struct DIR *gpDir;
+struct dirent *gpDirent;
+struct stat gFileStat;
+char *gPath;
+int gPathLength;
+CleanStringVariable(gFileName,NAME_MAX+1);
+
+static int unix_error_to_clean_error(int errCode)
+{
+ switch(errCode) {
+ case ENOENT:
+ case ENOTDIR: return DoesntExist;
+ case ENAMETOOLONG: return BadName;
+ case EMLINK:
+ case ENOSPC: return NotEnoughSpace;
+ case EISDIR:
+ case EEXIST: return AlreadyExists;
+ case EPERM:
+ case EACCES:
+ case EROFS: return NoPermission;
+ case EINVAL: return MoveIntoOffspring;
+ case EXDEV: return MoveAcrossDisks;
+ case ENOTEMPTY: return NotYetRemovable;
+ default: return OtherDirError;
+ };
+}
+
+static int openSearch(const char *path, int length)
+{
+ int i;
+
+ gPathLength = length;
+ gPath = (char*) malloc(gPathLength+NAME_MAX+2);
+ if (!gPath) {
+ gpDir = NULL;
+ return 0;
+ }
+ else {
+ memcpy(gPath, path, gPathLength);
+ gPath[gPathLength] = '\0';
+ gpDir = (struct DIR*) opendir(gPath);
+ if (!gpDir)
+ return unix_error_to_clean_error(errno);
+ gPath[gPathLength] = '/'; // filename will be added later
+ gUserId = getuid();
+ gGroupId = getgid();
+ return NoDirError;
+ };
+}
+
+int findFirstFileC(CleanString cs_path)
+{
+ int errCode;
+
+ errCode = openSearch(CleanStringCharacters(cs_path), CleanStringLength(cs_path));
+ if (errCode)
+ return errCode;
+ else
+ return findNextFileC(0);
+}
+
+void getCommonFileInfoC(int also_get_file_name,
+ CleanString *pFileName, int *pFileSizeLow, int *pFileSizeHigh,
+ int *pYear, int *pMonth, int *pDay, int *pDayNr,
+ int *pHours, int *pMinutes, int *pSeconds,
+ int *pIsDirectory, int *pIsReadOnly)
+// requires gFileName and gFileStat to be initialized
+{
+ struct tm *pModificationTime;
+ int mask;
+ static int null = 0;
+
+ *pFileName = also_get_file_name ? (CleanString) gFileName : (CleanString) &null;
+ *pFileSizeLow = gFileStat.st_size;
+ *pFileSizeHigh = 0;
+ pModificationTime= localtime(&gFileStat.st_mtime);
+ *pYear = pModificationTime->tm_year+1900;
+ *pMonth = pModificationTime->tm_mon+1;
+ *pDay = pModificationTime->tm_mday;
+ *pDayNr = pModificationTime->tm_wday+1;
+ *pHours = pModificationTime->tm_hour;
+ *pMinutes = pModificationTime->tm_min;
+ *pSeconds = pModificationTime->tm_sec % 60;
+ *pIsDirectory = (gFileStat.st_mode & S_IFDIR) != 0;
+ mask = gUserId==gFileStat.st_uid ? S_IWUSR
+ : gGroupId==gFileStat.st_gid ? S_IWGRP
+ : S_IWOTH;
+ *pIsReadOnly = (gFileStat.st_mode & mask) == 0;
+}
+
+int findNextFileC(int dummy)
+// return values: 0=ok, 1=no further files in directory
+{
+ int i;
+
+ gpDirent = readdir((void*)gpDir);
+ if (!gpDirent)
+ return 1;
+
+ for(i=0; gpDirent->d_name[i]!='\0' && i<NAME_MAX; i++) {
+ CleanStringCharacters(gFileName)[i] = gpDirent->d_name[i];
+ gPath[gPathLength+1+i] = gpDirent->d_name[i];
+ };
+ CleanStringLength(gFileName)= i;
+ gPath[gPathLength+1+i] = '\0';
+ gFileStat.st_size = 0;
+ gFileStat.st_mode = 0;
+ lstat(gPath,&gFileStat);
+ return 0;
+}
+
+int getPlatformIdC(int dummy)
+{
+#define UnixPlatform 0
+ return UnixPlatform;
+}
+
+void getWindowsFileInfoC()
+{}
+
+
+void getMacFileInfoC()
+{}
+
+void getUnixFileInfoC(int *pModeBits, int *pOwnerUserId, int *pOwnerGroupId,
+ int *pLAYear, int *pLAMonth, int *pLADay, int *pLADayNr, // last access time
+ int *pLAHours, int *pLAMinutes, int *pLASeconds) // dito
+{
+ struct tm *pModificationTime;
+
+ *pModeBits = gFileStat.st_mode;
+ *pOwnerUserId = gFileStat.st_uid;
+ *pOwnerGroupId = gFileStat.st_gid;
+ pModificationTime= localtime(&gFileStat.st_atime);
+ *pLAYear = pModificationTime->tm_year+1900;
+ *pLAMonth = pModificationTime->tm_mon+1;
+ *pLADay = pModificationTime->tm_mday;
+ *pLADayNr = pModificationTime->tm_wday+1;
+ *pLAHours = pModificationTime->tm_hour;
+ *pLAMinutes = pModificationTime->tm_min;
+ *pLASeconds = pModificationTime->tm_sec % 60;
+}
+
+
+void closeSearchC()
+{
+ free(gPath);
+ if (gpDir)
+ closedir((void*)gpDir);
+}
+
+int findSingleFileC(CleanString cs_path)
+{
+ int err,i,length;
+ char *path_chars;
+
+ gFileStat.st_size = 0;
+ gFileStat.st_mode = 0;
+
+ path_chars = CleanStringCharacters(cs_path);
+ if (lstat(path_chars,&gFileStat))
+ return unix_error_to_clean_error(errno);
+
+ i = CleanStringLength(cs_path)-2;
+ while (i>=0 && path_chars[i]!='/')
+ i--;
+ // the last path element ranges from path_chars[i+1] to path_chars[CleanStringLength(cs_path)-2]
+
+ length = CleanStringLength(cs_path)-2-i;
+ CleanStringLength(gFileName) = length;
+ memcpy(CleanStringCharacters(gFileName), path_chars+i+1, length);
+
+ return NoDirError;
+}
+
+void closeSingleSearchC()
+{}
+
+int createDirectoryC(CleanString cs_path)
+{
+ int err;
+
+ err = mkdir(CleanStringCharacters(cs_path),
+ S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+ // access rights for newly created directory: rwx--x--x
+ if (err)
+ return unix_error_to_clean_error(errno);
+ else
+ return NoDirError;
+}
+
+int fremoveC(CleanString cs_path)
+{
+ int err;
+
+ err = remove(CleanStringCharacters(cs_path));
+ if (err) {
+ if (errno==EEXIST)
+ return NotYetRemovable;
+ else
+ return unix_error_to_clean_error(errno);
+ }
+ else
+ return NoDirError;
+}
+
+// Try this code with slackware linux, if the testLinux program doesn't work
+//int fremoveC(CleanString cs_path)
+//{
+// int err;
+//
+// err = unlink(CleanStringCharacters(cs_path));
+// if (err) {
+// if (errno==EPERM) {
+// err = rmdir(CleanStringCharacters(cs_path));
+// if (err)
+// return unix_error_to_clean_error(errno);
+// else
+// return NoDirError;
+// }
+// else
+// return unix_error_to_clean_error(errno);
+// }
+// else
+// return NoDirError;
+//}
+
+#define OK 0
+#define STRING_TOO_SMALL 1
+
+int getCurrentDirectory_SE(CleanString cs)
+{
+ if (getcwd(CleanStringCharacters(cs), CleanStringLength(cs))) {
+ // success. convert C String to Clean string
+ int i;
+ i = 0;
+ while(CleanStringCharacters(cs)[i]!='\0')
+ i++;
+ CleanStringLength(cs) = i;
+ return OK;
+ }
+ else {
+ // failure
+ if (errno==EACCES) {
+ // the permission to read the current directory was denied (how ever)
+ // return root directory
+ CleanStringLength(cs) = 1;
+ CleanStringCharacters(cs)[0] = '/';
+ return OK;
+ }
+ else
+ return STRING_TOO_SMALL;
+ };
+}
+
+void get_mac_dir_parent_and_name_C()
+{
+}
+
+int setCurrentDirectoryC(CleanString csPath)
+{
+ int err;
+
+ err = chdir(CleanStringCharacters(csPath));
+ if (err)
+ return unix_error_to_clean_error(errno);
+ else
+ return NoDirError;
+}
+
+void getMacDiskNameC()
+{}
+
+void get_windows_disk_available_bits_C()
+{}
+
+void macRenameC()
+{}
+
+void macMoveC()
+{}
+
+int fmoveC(int overwrite, CleanString from, CleanString to)
+{
+ int err,clean_err;
+
+ if (overwrite) {
+ err = rename(CleanStringCharacters(from), CleanStringCharacters(to));
+ if (err && errno==ENOTDIR) {
+ // from is a directory and to a file
+ // try again after removing to
+ err = remove(CleanStringCharacters(to));
+ if (!err)
+ err = rename(CleanStringCharacters(from), CleanStringCharacters(to));
+ };
+ clean_err = err ? unix_error_to_clean_error(errno) : NoDirError;
+ }
+ else {
+ struct stat fileStat;
+ if (stat(CleanStringCharacters(to),&fileStat)) {
+ err = rename(CleanStringCharacters(from), CleanStringCharacters(to));
+ clean_err = err ? unix_error_to_clean_error(errno) : NoDirError;
+ }
+ else
+ clean_err = AlreadyExists;
+ };
+ return clean_err;
+}
diff --git a/main/Mac cli/ipc.c b/main/Mac cli/ipc.c
new file mode 100644
index 0000000..4ff823a
--- /dev/null
+++ b/main/Mac cli/ipc.c
@@ -0,0 +1,123 @@
+/*
+ Unix clm/cocl interface
+
+ Ronny Wichers Schreur
+
+*/
+# include <stdio.h>
+# include <stdlib.h>
+# include <stdarg.h>
+# include <strings.h>
+
+/*
+ Clean string
+ ============
+*/
+typedef struct clean_string {int length; char chars [1]; } *CleanString;
+
+# define Clean(ignore)
+# include "ipc.h"
+
+static void
+log (char *format, ...)
+{
+#ifdef DEBUG
+ va_list ap;
+
+ va_start (ap, format);
+ (void) fputs(" cocl: ", stderr);
+ (void) vfprintf(stderr, format, ap);
+ va_end(ap);
+#else /* ifndef DEBUG */
+#endif
+}
+
+static char *
+ConvertCleanString (CleanString string)
+{
+ int length;
+ char *copy;
+
+ length = string->length;
+ copy = malloc (length+1);
+ strncpy (copy, string->chars, length);
+ copy [length] = '\0';
+
+ return (copy);
+} /* ConvertCleanString */
+
+static FILE *commands, *results;
+# define COMMAND_BUFFER_SIZE 1024
+static char command_buffer[COMMAND_BUFFER_SIZE];
+
+static void
+crash (void)
+{
+ int *p;
+
+ p = NULL;
+ log ("crashing\n");
+ *p = 0;
+} /* crash */
+
+static void
+hang (void)
+{
+ log ("hanging\n");
+ for (;;)
+ ;
+} /* hang */
+
+int open_pipes (CleanString commands_clean, CleanString results_clean)
+{
+ char *commands_name, *results_name;
+
+ commands_name = ConvertCleanString (commands_clean);
+ results_name = ConvertCleanString (results_clean);
+
+ if ((commands = fopen(commands_name, "r")) == NULL)
+ {
+ log("commands = %s\n",commands_name);
+ perror("fopen commands");
+ return -1;
+ }
+ if ((results = fopen(results_name, "w")) == NULL)
+ {
+ log("results = %s\n",results_name);
+ perror("fopen results");
+ return -1;
+ }
+ return 0;
+}
+
+int get_command_length (void)
+{
+ log ("reading command\n");
+ if (fgets (command_buffer, COMMAND_BUFFER_SIZE, commands) == NULL)
+ return -1;
+ else
+ {
+ log ("command = %s", command_buffer);
+ return (strlen (command_buffer));
+ }
+}
+
+int get_command (CleanString cleanString)
+{
+ log ("%s\n", command_buffer);
+ strncpy (cleanString->chars, command_buffer, cleanString->length);
+ return (0);
+}
+
+int send_result (int result)
+{
+ int r;
+
+ if (fprintf (results, "%d\n", result) > 0)
+ r=0;
+ else
+ r=-1;
+ fflush (results);
+
+ return r;
+}
diff --git a/main/Mac cli/ipc.dcl b/main/Mac cli/ipc.dcl
new file mode 100644
index 0000000..ce686ae
--- /dev/null
+++ b/main/Mac cli/ipc.dcl
@@ -0,0 +1,14 @@
+definition module ipc;
+
+//1.3
+from StdString import String;
+//3.1
+
+open_pipes :: !String !String -> Int;
+// int open_pipes (CleanString commands_name,CleanString results_name);
+get_command_length :: Int;
+// int get_command_length ();
+get_command :: !String -> Int;
+// int get_command (CleanString cleanString);
+send_result :: !Int -> Int;
+// int send_result (int result);
diff --git a/main/Mac cli/ipc.h b/main/Mac cli/ipc.h
new file mode 100644
index 0000000..99a070e
--- /dev/null
+++ b/main/Mac cli/ipc.h
@@ -0,0 +1,8 @@
+int open_pipes (CleanString commands_name, CleanString results_name);
+Clean (open_pipes :: String String -> Int)
+int get_command_length (void);
+Clean (get_command_length :: Int)
+int get_command (CleanString cleanString);
+Clean (get_command :: String -> Int)
+int send_result (int result);
+Clean (send_result :: Int -> Int)
diff --git a/main/Mac cli/ipc.icl b/main/Mac cli/ipc.icl
new file mode 100644
index 0000000..55ca927
--- /dev/null
+++ b/main/Mac cli/ipc.icl
@@ -0,0 +1,30 @@
+implementation module ipc;
+
+//1.3
+from StdString import String;
+//3.1
+
+
+open_pipes :: !String !String -> Int;
+open_pipes a0 a1 = code {
+ ccall open_pipes "SS:I"
+}
+// int open_pipes (CleanString commands_name,CleanString results_name);
+
+get_command_length :: Int;
+get_command_length = code {
+ ccall get_command_length ":I"
+}
+// int get_command_length ();
+
+get_command :: !String -> Int;
+get_command a0 = code {
+ ccall get_command "S:I"
+}
+// int get_command (CleanString cleanString);
+
+send_result :: !Int -> Int;
+send_result a0 = code {
+ ccall send_result "I:I"
+}
+// int send_result (int result);
diff --git a/main/Mac cli/set_return_code.dcl b/main/Mac cli/set_return_code.dcl
new file mode 100644
index 0000000..e8ed7f8
--- /dev/null
+++ b/main/Mac cli/set_return_code.dcl
@@ -0,0 +1,9 @@
+definition module set_return_code;
+
+//1.3
+from StdString import String;
+//3.1
+
+:: *UniqueWorld :== World;
+set_return_code :: !Int !UniqueWorld -> UniqueWorld;
+// void set_return_code (int return_code);
diff --git a/main/Mac cli/set_return_code.icl b/main/Mac cli/set_return_code.icl
new file mode 100644
index 0000000..b4028f7
--- /dev/null
+++ b/main/Mac cli/set_return_code.icl
@@ -0,0 +1,18 @@
+implementation module set_return_code;
+
+import code from "set_return_code_c.o";
+
+import StdString;
+import StdDebug;
+
+:: *UniqueWorld :== World;
+
+set_return_code :: !Int !UniqueWorld -> UniqueWorld;
+set_return_code a0 a1 = code
+{
+ ccall set_return_code "I:V:A"
+ fill_a 0 1
+ pop_a 1
+}
+
+// void set_return_code (int return_code);
diff --git a/main/Mac cli/set_return_code_c.c b/main/Mac cli/set_return_code_c.c
new file mode 100644
index 0000000..a09e1ab
--- /dev/null
+++ b/main/Mac cli/set_return_code_c.c
@@ -0,0 +1,6 @@
+extern int return_code;
+
+void set_return_code (int code)
+{
+ return_code = code;
+}