>From 5d53920602e234e4d99ae2d502e662ee3699978e 4 Oct 2024 12:01:22 -0400
From: "James K. Lowden" <[email protected]>
Date: Sat 15 Feb 2025 12:50:54 PM EST
Subject: [PATCH] 12 new 'cobol' FE files
gcc/cobol/ChangeLog
* posix/.gitignore: New file.
* posix/Makefile: New file.
* posix/README.md: New file.
* posix/headers: New file.
* posix/scrape.awk: New file.
* posix/udf-gen: New file.
* posix/c/posix_errno.c: New file.
* posix/udf/Makefile: New file.
* posix/udf/posix-errno.cbl: New file.
* posix/udf/posix-exit.cbl: New file.
* posix/udf/t/errno.cbl: New file.
* posix/udf/t/exit.cbl: New file.
---
gcc/cobol/posix/.gitignore | +++-
gcc/cobol/posix/Makefile | +++++++++++++-
gcc/cobol/posix/README.md |
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
gcc/cobol/posix/c/posix_errno.c | +++++-
gcc/cobol/posix/headers | +++++++++++++++++++++++++++++++++++++-
gcc/cobol/posix/scrape.awk | +++++++++++++++++++-
gcc/cobol/posix/udf-gen |
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
gcc/cobol/posix/udf/Makefile | +++++++++++++++++++++++++++-
gcc/cobol/posix/udf/posix-errno.cbl | ++++++++++++++++++-
gcc/cobol/posix/udf/posix-exit.cbl | ++++++++++++-
gcc/cobol/posix/udf/t/errno.cbl | ++++++++++++++++++++++++++-
gcc/cobol/posix/udf/t/exit.cbl | +++++++++++++++
12 files changed, 590 insertions(+), 12 deletions(-)
diff --git a/gcc/cobol/posix/.gitignore b/gcc/cobol/posix/.gitignore
new file mode 100644
index 00000000000..a65b24a10dc
--- /dev/null
+++ b/gcc/cobol/posix/.gitignore
@@ -0,0 +1,3 @@
+posix.txt
+prototypes.c
+prototypes.cpp
diff --git a/gcc/cobol/posix/Makefile b/gcc/cobol/posix/Makefile
new file mode 100644
index 00000000000..471769482b2
--- /dev/null
+++ b/gcc/cobol/posix/Makefile
@@ -0,0 +1,13 @@
+prototypes.c: headers prototypes.cpp
+ cat $^ > $@~
+ @mv $@~ $@
+
+prototypes.cpp: posix.txt
+ awk -F'[/.]' '{ print $$6 }' $^ | \
+ while read F; do echo "/* $$F */" && man 2 $$F | \
+ ./scrape.awk -v funcname=$$6; done > $@~
+ @mv $@~ $@
+
+posix.txt:
+ zgrep -l 'POSIX[.]' /usr/share/man/man2/*z > $@~
+ @mv $@~ $@
diff --git a/gcc/cobol/posix/README.md b/gcc/cobol/posix/README.md
new file mode 100644
index 00000000000..f126a8be990
--- /dev/null
+++ b/gcc/cobol/posix/README.md
@@ -0,0 +1,65 @@
+# GCC COBOL Posix Functions and Adapter
+
+## Purpose
+
+ISO COBOL does not specify any relationship to any particular
+operating system, and does not reference Posix. The raw capability is
+there, of course, via the `CALL` statement. But that's not very
+convenient, and offers no parameter validation.
+
+GCC COBOL as of this writing works *only* in a Posix environment. This
+directory exists to make using OS-provided functions a bit more convenient.
+
+## Contents
+
+The machine-shop tools are in this directory. Things directly usable
+by a COBOL program are in the `udf/` and `c/` directories.
+
+- `scrape.awk` extracts function prototypes from the SYNOPSIS of a man page.
+- `udf-gen` reads function declarations and, for each one, produces a
+ COBOL User Defined Function (UDF) that calls the function.
+- `Makefile` produces a list of function prototypes from Section 2 of
+ the manual.
+- `c/` contains helper functions in C that cannot be expressed in
+ COBOL. For example, the C `errno` "variable" may be a macro, and may
+ not be declared except by `errno.h`, which is not accessible to
+ COBOL.
+- `udf/Makefile` builds some infrastructure and examples:
+ - `udf/libposix-errno.so`, to get at the C `errno` variable and its
+ string representation.
+ - `udf/posix-mkdir.cbl` automatically from the manual, using `udf-gen`.
+ - `udf/t/errno` and
+ - `udf/t/exit` as examples of COBOL programs using these Posix UDFs.
+
+## Prerequisites
+
+`udf-gen` is a Python program that imports
+the [PLY pycparser module](http://www.dabeaz.com/ply/) module, which must be
installed.
+
+`udf-gen` is lightly documented, use `udf-gen --help`. It can be a
+little tedious to set up the first time, but if you want to use more a
+few functions, it will be faster than doing the work by hand.
+
+## Limitations
+
+`udf-gen` does not
+
+- generate a working UDF for function parameters of type `struct`,
+ such as is used by **stat**(2). This is because the information is
+ not available in a standardized way in the SYNOPSIS of a man page.
+- define helpful Level 88 values for "magic" numbers, such as
+ permission bits in **chmod**(2).
+
+None of this is particularly difficult; it's just a matter of time and
+need. The `scrape.awk` script finds 560 functions in the Ubuntu LTS
+22.04 manual. Which of those is important is for users to decide.
+
+## Other Options
+
+IBM and Microfocus both supply intrinsic functions to interface with
+the OS, each in their own way. GnuCOBOL implements some of those functions.
+
+## Portability
+
+The UDF produced by `udf-gen` is pure ISO COBOL. The code should be
+compilable by any ISO COBOL compiler.
diff --git a/gcc/cobol/posix/c/posix_errno.c b/gcc/cobol/posix/c/posix_errno.c
new file mode 100644
index 00000000000..f87e271ced4
--- /dev/null
+++ b/gcc/cobol/posix/c/posix_errno.c
@@ -0,0 +1,5 @@
+#include <errno.h>
+
+int posix_errno() {
+ return errno;
+}
diff --git a/gcc/cobol/posix/headers b/gcc/cobol/posix/headers
new file mode 100644
index 00000000000..b17c0f30cb3
--- /dev/null
+++ b/gcc/cobol/posix/headers
@@ -0,0 +1,37 @@
+#include <stddef.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#define loff_t ssize_t
+#define socklen_t size_t
+#define fd_set struct fd_set
+#define id_t unsigned int
+// typedef int mqd_t;
+#define mqd_t int
+// typedef unsigned long int nfds_t;
+#define nfds_t unsigned long int
+
+#if 0
+typedef struct
+{
+ unsigned long int __val[(1024 / (8 * sizeof (unsigned long int)))];
+} __sigset_t;
+define struct py_sigset_t \
+{ \
+ unsigned long int __val[(1024 / (8 * sizeof (unsigned long int)))]; \
+};
+#else
+#define kernel_sigset_t sigset_t
+#define old_kernel_sigset_t sigset_t
+#endif
+
+#if 0
+typedef enum
+{
+ P_ALL,
+ P_PID,
+ P_PGID
+} idtype_t;
+#else
+#define idtype_t int
+#endif
diff --git a/gcc/cobol/posix/scrape.awk b/gcc/cobol/posix/scrape.awk
new file mode 100755
index 00000000000..4d244d0ee3d
--- /dev/null
+++ b/gcc/cobol/posix/scrape.awk
@@ -0,0 +1,19 @@
+#! /usr/bin/awk -f
+
+/^UNIMPLEMENTED/ {
+ exit
+}
+
+/^DESCRIPTION/ {
+ exit
+}
+
+/struct sched_param {$/ {
+ exit
+}
+
+/SYNOPSIS/,/DESCRIPTION/ {
+ if( /([.][.]|[{},;]) *$/ ) {
+ print
+ }
+}
diff --git a/gcc/cobol/posix/udf-gen b/gcc/cobol/posix/udf-gen
new file mode 100755
index 00000000000..e6207085b1d
--- /dev/null
+++ b/gcc/cobol/posix/udf-gen
@@ -0,0 +1,350 @@
+#! /usr/bin/python3
+
+# Copyright (c) 2024 Symas Corporation
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the Symas Corporation nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import sys, os, getopt, re, copy
+from pycparser import c_parser, c_generator, c_ast, parse_file
+
+def starify(param):
+ stars = ""
+ while( isinstance(param, c_ast.PtrDecl) ):
+ q = ' '.join(param.quals)
+ stars = '*' + ' '.join((stars, q))
+ param = param.type
+ if( isinstance(param.type, c_ast.PtrDecl) ):
+ (stars, param) = starify(param.type)
+ if( isinstance(param, c_ast.TypeDecl) ):
+ return (stars, param)
+ return (stars, param.type)
+
+def linkage_str( i, name, param ) -> str:
+ if name == 'execve':
+ param.show()
+ if( isinstance(param, c_ast.EllipsisParam) ):
+ return (None, None, '...') # COBOL syntax error: no variadic UDF
+
+ is_array = False;
+ node = param
+
+ if( isinstance(node, c_ast.Decl) ):
+ node = node.type
+
+ if( isinstance(node, c_ast.ArrayDecl) ):
+ is_array = True;
+ node = node.type
+
+ (stars, node) = starify(node)
+
+ if( isinstance(node, c_ast.TypeDecl) ):
+ level = 1
+ item_name = ''
+ picture = ''
+ usage = ''
+ if node.declname:
+ item_name = 'Lk-' + node.declname
+
+ if is_array: # ignore level
+ if stars:
+ usage = 'Usage POINTER'
+ output = '01 FILLER.\n 02 %s %s %s OCCURS 100' \
+ % (item_name, picture, usage)
+ return (None, None, output)
+
+ if( isinstance(node.type, c_ast.Struct) ):
+ stars = None
+
+ if isinstance(node.type, c_ast.IdentifierType):
+ ctype = node.type.names[-1]
+ if ctype == 'void':
+ if not stars and not item_name:
+ return (None, None, None)
+ if ctype == 'char':
+ picture = 'X'
+ if stars[0] == '*':
+ picture = 'X ANY LENGTH'
+ if ctype == 'int' or \
+ ctype == 'long' or \
+ ctype == 'mode_t' or \
+ ctype == 'off_t' or \
+ ctype == 'size_t':
+ picture = '9(8)'
+ usage = 'Usage COMP'
+ stars = None
+
+ output = "%02d %s" % (level, ' '.join((item_name, 'PIC ' + picture,
usage)))
+ return (stars, item_name, output)
+
+ node.show()
+ return (None, None, '???')
+
+def using_str( i, name, param ) -> str:
+ item_name = ''
+ if( isinstance(param, c_ast.EllipsisParam) ):
+ return '...' # COBOL syntax error: no variadic UDF
+ node = param
+
+ if( isinstance(node, c_ast.Decl) ):
+ node = node.type
+
+ if( isinstance(node, c_ast.ArrayDecl) ):
+ node = node.type
+
+ (stars, node) = starify(node)
+
+ if( isinstance(node, c_ast.TypeDecl) ):
+ item_name = ''
+
+ if isinstance(node.type, c_ast.IdentifierType):
+ ctype = node.type.names[-1]
+ how = 'By Reference'
+ if ctype == 'int' or \
+ ctype == 'long' or \
+ ctype == 'mode_t' or \
+ ctype == 'off_t' or \
+ ctype == 'size_t':
+ how = 'By Value'
+ if node.declname:
+ item_name = '%s Lk-%s' % (how, node.declname)
+
+ return item_name
+
+def parameter_str( i, name, param ) -> str:
+ if( isinstance(param, c_ast.EllipsisParam) ):
+ return '...'
+
+ t = [0, 1, 2] # qual, type, name
+ is_array = False;
+ node = param
+
+ if( isinstance(node, c_ast.Decl) ):
+ node = node.type
+
+ if( isinstance(node, c_ast.ArrayDecl) ):
+ is_array = True;
+ node = node.type
+
+ (stars, node) = starify(node)
+
+ if( isinstance(node, c_ast.TypeDecl) ):
+ t[0] = ' '.join(node.quals)
+ item_name = ''
+ if node.declname:
+ item_name = 'Lk-' + node.declname
+ t[2] = ' '.join((stars, item_name))
+ if( node.declname == None ):
+ t[2] = ''
+ if( isinstance(node.type, c_ast.IdentifierType) ):
+ try:
+ t[1] = ' '.join(node.type.names)
+ except:
+ print("oops: node.type of %s is %s" % (name, str(node.type)))
+ return "could not parse %s arg[%d]" % (name, i)
+ if( isinstance(node.type, c_ast.Struct) ):
+ t[0] = ' '.join(node.quals)
+ t[1] = "struct " + node.type.name
+ if( isinstance(node, c_ast.ArrayDecl) ):
+ return parameter_str(i, name, node.type) + '[]'
+
+ try:
+ return ' '.join(t)
+ except:
+ print("oops: %s[%d]: {%s}" % (name, i, str(t)) )
+ param.show()
+
+class VisitPrototypes(c_ast.NodeVisitor):
+ def __init__(self):
+ self.done = set()
+
+ def type_of(self, node):
+ while( not isinstance(node.type, c_ast.TypeDecl) ):
+ node = node.type
+ return node.type.type.name
+
+ def visit_Decl(self, node):
+ name = node.name
+ if name in self.done:
+ return
+ self.done.add(name)
+
+ params = []
+ cbl_args = []
+ linkage_items = []
+ string_items = []
+ returns = '???'
+
+ if False and isinstance(node.type, c_ast.FuncDecl):
+ function_decl = node.type
+ print('Function: %s' % node.name)
+ if( node.type.args == None ):
+ print(' (no arguments)')
+ else:
+ for param_decl in node.type.args.params:
+ if( isinstance(param_decl, c_ast.EllipsisParam) ):
+ param_decl.show(offset=6)
+ continue
+ print(' Arg name: %s' % param_decl.name)
+ print(' Type:')
+ param_decl.type.show(offset=6)
+
+ if isinstance(node.type, c_ast.FuncDecl):
+ args = node.type.args
+ if isinstance(args, c_ast.ParamList):
+ #rint("params are %s (type %s)" % (str(args.params),
type(args.params)))
+ if( args == None ):
+ params.append('')
+ else:
+ for (i, param) in enumerate(args.params):
+ params.append(parameter_str(i, name, param))
+ cbl_args.append(using_str(i, name, param))
+ (stars, item, definition) = linkage_str(i, name, param)
+ if definition:
+ if stars:
+ string_items.append(item)
+ linkage_items.append(definition)
+
+ (stars, rets) = starify(node.type)
+
+ if isinstance(rets, c_ast.TypeDecl):
+ q = ' '.join(rets.quals)
+ if( isinstance(rets.type, c_ast.Struct) ):
+ t = "struct " + rets.type.name
+ else:
+ t = ' '.join(rets.type.names)
+ returns = ' '.join((q, t, stars))
+
+ if name == None:
+ return
+
+ # print the C version as a comment
+ cparams = [ x.replace('Lk-', '') for x in params ]
+ print( " * %s %s(%s)"
+ % (returns, name, ', '.join(cparams)) )
+
+ # print the UDF
+ print( ' Identification Division.')
+ sname = name
+ if( sname[0] == '_' ):
+ sname = sname[1:]
+ print( ' Function-ID. posix-%s.' % sname)
+
+ print( ' Data Division.')
+ print( ' Linkage Section.')
+ print( ' 77 Return-Value Binary-Long.')
+ for item in linkage_items:
+ print( ' %s.' % item.strip())
+ args = ',\n '.join(cbl_args)
+ args = 'using\n %s\n ' % args
+ print( ' Procedure Division %s Returning Return-Value.'
+ % args )
+ for item in string_items:
+ print( ' Inspect Backward %s ' % item +
+ 'Replacing Leading Space By Low-Value' )
+ using_args = ''
+ if args:
+ using_args = '%s' % args
+ print( ' Call "%s" %s Returning Return-Value.'
+ % (name, using_args) )
+ print( ' Goback.')
+ print( ' End Function posix-%s.' % sname)
+
+# Hard code a path to the fake includes
+# if not using cpp(1) environment variables.
+cpp_args = ['-I/home/jklowden/projects/3rd/pycparser/utils/fake_libc_include']
+
+for var in ('CPATH', 'C_INCLUDE_PATH'):
+ dir = os.getenv(var)
+ if dir:
+ cpp_args = ''
+
+def process(srcfile):
+ ast = parse_file(srcfile, use_cpp=True, cpp_args=cpp_args)
+ # print(c_generator.CGenerator().visit(ast))
+ v = VisitPrototypes()
+ v.visit(ast)
+
+__doc__ = """
+SYNOPSIS
+ udf-gen [-I include-path] [header-file ...]
+
+DESCRIPTION
+ For each C function declared in header-file,
+produce an ISO COBOL user-defined function definition to call it.
+If no filename is supplied, declarations are read from standard input.
+All output is written to standard output.
+
+ This Python script uses the PLY pycparser module,
+(http://www.dabeaz.com/ply/), which supplies a set of simplified "fake
+header files" to avoid parsing the (very complex) standard C header
+files. These alost suffice for parsing the Posix function
+declarations in Section 2 of the manual.
+
+ Use the -I option or the cpp(1) environment variables to direct
+the preprocessor to use the fake header files instead of the system
+header files.
+
+LIMITATIONS
+ udf-gen does not recognize C struct parameters, such as used by stat(2).
+
+ No attempt has been made to define "magic" values, such as would
+be needed for example by chmod(2).
+"""
+
+def main( argv=None ):
+ global cpp_args
+ if argv is None:
+ argv = sys.argv
+ # parse command line options
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "D:hI:m:", ["help"])
+ except getopt.error as msg:
+ print(msg)
+ print("for help use --help")
+ sys.exit(2)
+
+ # process options
+ astfile = None
+
+ for opt, arg in opts:
+ if opt in ("-h", "--help"):
+ print(__doc__)
+ sys.exit(0)
+ if opt == '-D':
+ cpp_args.append('-D%s ' % arg)
+ if opt == '-I':
+ cpp_args[0] = '-I' + arg
+
+ # process arguments
+ if not args:
+ args = ('/dev/stdin',)
+
+ for arg in args:
+ process(arg)
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/gcc/cobol/posix/udf/Makefile b/gcc/cobol/posix/udf/Makefile
new file mode 100644
index 00000000000..8321f2dde90
--- /dev/null
+++ b/gcc/cobol/posix/udf/Makefile
@@ -0,0 +1,27 @@
+LDFLAGS = -L $$(pwd) -Wl,-rpath -Wl,$$(pwd)
+
+all: t/exit t/errno
+
+t/exit: posix-exit.cbl t/exit.cbl
+ ../../built-gcobol $(FLAGS) -o $@ -I$$(pwd) $(lastword $^)
+
+t/errno: t/errno.cbl posix-mkdir.cbl | libposix-errno.so
+ ../../built-gcobol $(FLAGS) -o $@ -I$$(pwd) \
+ $(firstword $^) $(LDFLAGS) -lposix-errno
+
+libposix-errno.so: ../c/posix_errno.c posix-errno.o
+ gcc $(CFLAGS) -shared -o $@ $^
+
+posix-errno.o: posix-errno.cbl
+ ../../built-gcobol $(FLAGS) -fPIC -c -o $@ $^
+
+posix-mkdir.cbl:
+ man 2 mkdir | ../scrape.awk | \
+ ../udf-gen -D mode_t=unsigned\ long > $@~
+ @mv $@~ $@
+
+test: $(basename $(wildcard t/*.cbl))
+ t/errno
+
+clean:
+ rm -f *.o *.so $(basename $(wildcard t/*.cbl))
diff --git a/gcc/cobol/posix/udf/posix-errno.cbl
b/gcc/cobol/posix/udf/posix-errno.cbl
new file mode 100644
index 00000000000..9670637b3ba
--- /dev/null
+++ b/gcc/cobol/posix/udf/posix-errno.cbl
@@ -0,0 +1,18 @@
+ Identification Division.
+ Function-ID. posix-errno.
+
+ Data Division.
+ Linkage Section.
+ 77 Return-Value Binary-Long.
+ 01 Error-Msg PIC X ANY LENGTH.
+
+ Procedure Division
+ using Error-Msg
+ Returning Return-Value.
+ CALL "posix_errno"
+ returning Return-Value.
+ CALL "strerror"
+ using by value Return-Value
+ returning error-msg.
+ Goback.
+ END FUNCTION posix-errno.
diff --git a/gcc/cobol/posix/udf/posix-exit.cbl
b/gcc/cobol/posix/udf/posix-exit.cbl
new file mode 100644
index 00000000000..cd2ac1857e9
--- /dev/null
+++ b/gcc/cobol/posix/udf/posix-exit.cbl
@@ -0,0 +1,12 @@
+ Identification Division.
+ Function-ID. posix-exit.
+
+ Data Division.
+ Linkage Section.
+ 77 Return-Value Binary-Long.
+ 77 Exit-Status Binary-Long.
+
+ Procedure Division using Exit-Status Returning Return-Value.
+ CALL "_exit" using by value Exit-Status.
+ Goback.
+ END FUNCTION posix-exit.
\ No newline at end of file
diff --git a/gcc/cobol/posix/udf/t/errno.cbl b/gcc/cobol/posix/udf/t/errno.cbl
new file mode 100644
index 00000000000..22078ae3c31
--- /dev/null
+++ b/gcc/cobol/posix/udf/t/errno.cbl
@@ -0,0 +1,26 @@
+ COPY posix-mkdir.
+ COPY posix-errno.
+
+ Identification Division.
+ Program-ID. test-errno.
+ Data Division.
+ Working-Storage Section.
+ 77 Return-Value Binary-Long.
+ 77 Exit-Status Binary-Long Value 1.
+ 77 error-msg PIC X(100).
+ 77 errnum Binary-Long.
+ 77 Filename PIC X(100) Value '/'.
+
+ Procedure Division.
+ Display 'calling posix-mkdir with a foolish name ...'
+ Move Function posix-mkdir(Filename, 0) to Return-Value.
+ If Return-Value <> 0
+ Display 'calling posix-errno ...'
+ Move Function posix-errno(error-msg) to errnum
+ Display 'error: "' Filename '": ' error-msg ' (' errnum ')'
+ Goback with Error Status errnum
+ Else
+ Display 'Return-Value is ' Return-Value
+ End-If.
+
+ Goback.
diff --git a/gcc/cobol/posix/udf/t/exit.cbl b/gcc/cobol/posix/udf/t/exit.cbl
new file mode 100644
index 00000000000..4aed400b17a
--- /dev/null
+++ b/gcc/cobol/posix/udf/t/exit.cbl
@@ -0,0 +1,15 @@
+ COPY posix-exit.
+
+ Identification Division.
+ Program-ID. test-exit.
+ Data Division.
+ Working-Storage Section.
+ 77 Return-Value Binary-Long.
+ 77 Exit-Status Binary-Long Value 1.
+
+ Procedure Division.
+ Display 'calling posix-exit ...'
+ Move Function posix-exit(Exit-Status) to Return-Value.
+ * Does not return, Does not print
+ Display 'How did we get here?'
+ Goback.