diff --git a/libgcobol/libgcobol.h b/libgcobol/libgcobol.h
index 9fe6bf2a524..e4d0ab9fd78 100644
--- a/libgcobol/libgcobol.h
+++ b/libgcobol/libgcobol.h
@@ -59,17 +59,17 @@ extern "C" __int128 __gg__dirty_to_binary(const char
*dirty,
extern "C" __int128 __gg__binary_value_from_field( int *rdigits,
cblc_field_t *var);
-extern "C" int __gg__compare_2( cblc_field_t *left_side,
- unsigned char *left_location,
- size_t left_length,
- int left_attr,
- int left_flags,
- cblc_field_t *right_side,
- unsigned char *right_location,
- size_t right_length,
- int right_attr,
- int right_flags,
- int second_time_through);
+extern "C" int __gg__compare_2( cblc_field_t *left_side,
+ unsigned char *left_location,
+ size_t left_length,
+ uint64_t left_attr,
+ int left_flags,
+ cblc_field_t *right_side,
+ unsigned char *right_location,
+ size_t right_length,
+ uint64_t right_attr,
+ int right_flags,
+ int second_time_through);
extern "C" void __gg__int128_to_field(cblc_field_t *tgt,
__int128 value,
int source_rdigits,
@@ -144,4 +144,17 @@ void __gg__convert_encoding_length(char *pch,
const unsigned short *__gg__current_collation();
+// Warning: field_from_string uses charmap_t, so you can't safely feed
it
+// the results of __gg__iconverter without copying them.
+extern "C"
+void __gg__field_from_string( cblc_field_t *field,
+ size_t field_o,
+ size_t field_s,
+ const char *string,
+ size_t string_length);
+extern "C"
+void *__gg__memdup(const void *p, size_t size);
+
+enum {width_of_utf32 = 4};
+
#endif
diff --git a/libgcobol/posix/bin/Makefile b/libgcobol/posix/bin/Makefile
index 335f205068b..f3c8f911285 100644
--- a/libgcobol/posix/bin/Makefile
+++ b/libgcobol/posix/bin/Makefile
@@ -1,18 +1,37 @@
+.SUFFIXES: .scr .cbl
+
+ROOT = $(shell git rev-parse --show-toplevel)
+
#
# Demonstrate how to generate a new COBOL binding from a man page.
#
+SCRAPE = $(ROOT)/libgcobol/posix/bin/scrape.awk
+UDF.GEN = $(ROOT)/libgcobol/posix/bin/udf-gen
+
+posix-funcs:
+ test "$(FUNCS)"
+ for F in $(FUNCS); \
+ do man 2 $$F | col -b | $(SCRAPE) > posix-$$F.scr; \
+ $(MAKE) -f $(ROOT)/libgcobol/posix/bin/Makefile
posix-$${F}.cbl; done
+
+posix-$(FUNC).cbl:
+ man 2 $(FUNC) | col -b | $(SCRAPE) | \
+ $(UDF.GEN) -D mode_t=unsigned\ long > $@~
+ @mv $@~ $@
+
posix-mkdir.cbl:
- man 2 mkdir | ./scrape.awk | \
- ../udf-gen -D mode_t=unsigned\ long > $@~
+ man 2 mkdir | col -b | $(SCRAPE) | \
+ $(UDF.GEN) -D mode_t=unsigned\ long > $@~
@mv $@~ $@
# ... or
posix-stat-many.scr:
- man 2 stat | col -b | ./scrape.awk > $@~
+ man 2 stat | col -b | $(SCRAPE) > $@~
@mv $@~ $@
-.scr.cbl:
- ./udf-gen -D mode_t=unsigned\ long $^ > $@~
+%.cbl : %.scr
+ test -s $^
+ $(UDF.GEN) $(CPPFLAGS) $^ > $@~
@mv $@~ $@
diff --git a/libgcobol/posix/bin/scrape.awk
b/libgcobol/posix/bin/scrape.awk
index 4d244d0ee3d..ba83146b883 100755
--- a/libgcobol/posix/bin/scrape.awk
+++ b/libgcobol/posix/bin/scrape.awk
@@ -12,6 +12,7 @@
exit
}
+# Print lines that end in dots, a comma, a brace, or a semicolon.
/SYNOPSIS/,/DESCRIPTION/ {
if( /([.][.]|[{},;]) *$/ ) {
print
diff --git a/libgcobol/posix/bin/udf-gen b/libgcobol/posix/bin/udf-gen
index 4ad9f7fffe7..35c8caba268 100755
--- a/libgcobol/posix/bin/udf-gen
+++ b/libgcobol/posix/bin/udf-gen
@@ -30,6 +30,7 @@
import sys, os, getopt, re, copy
from pycparser import c_parser, c_generator, c_ast, parse_file
+from pycparser.plyparser import ParseError
def starify(param):
stars = ""
@@ -283,7 +284,11 @@ for var in ('CPATH', 'C_INCLUDE_PATH'):
cpp_args = ''
def process(srcfile):
- ast = parse_file(srcfile, use_cpp=True, cpp_args=cpp_args)
+ try:
+ ast = parse_file(srcfile, use_cpp=True, cpp_args=cpp_args)
+ except ParseError as oops:
+ print(oops, file=sys.stderr)
+ sys.exit(1)
# print(c_generator.CGenerator().visit(ast))
v = VisitPrototypes()
v.visit(ast)
diff --git a/libgcobol/posix/cpy/psx-lseek.cpy
b/libgcobol/posix/cpy/psx-lseek.cpy
new file mode 100644
index 00000000000..e53e071fcc1
--- /dev/null
+++ b/libgcobol/posix/cpy/psx-lseek.cpy
@@ -0,0 +1,14 @@
+ >> PUSH source format
+ >>SOURCE format is fixed
+
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * This file is in the public domain.
+ * Contributed by James K. Lowden of Cobolworx in November 2025.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+ >>DEFINE SEEK_SET AS 2
+ >>DEFINE SEEK_CUR AS 4
+ >>DEFINE SEEK_END AS 8
+
+ >> POP source format
+
diff --git a/libgcobol/posix/shim/lseek.cc b/libgcobol/posix/shim/lseek.cc
new file mode 100644
index 00000000000..52407ed1df7
--- /dev/null
+++ b/libgcobol/posix/shim/lseek.cc
@@ -0,0 +1,31 @@
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cassert>
+#include <map>
+
+#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
+
+extern "C" {
+
+off_t
+posix_lseek(int fd, off_t offset, int whence) {
+
+ static const std::map<int, int> whences {
+ { 2, SEEK_SET },
+ { 4, SEEK_CUR },
+ { 8, SEEK_END },
+ };
+
+ /*
+ * Map valid input whence value onto C standard library value.
+ * Invalid values are passed through and rejected by lseek(2) per its
documentation.
+ * (The caller always needs to check for errors anyway.)
+ */
+ auto p = whences.find(whence);
+ if( p != whences.end() ) whence = p.second;
+
+ return lseek(fd, offset, whence);
+}
+
+} // extern "C"
diff --git a/libgcobol/posix/udf/posix-lseek.cbl
b/libgcobol/posix/udf/posix-lseek.cbl
index ec007a92802..e82e3d0fe0e 100644
--- a/libgcobol/posix/udf/posix-lseek.cbl
+++ b/libgcobol/posix/udf/posix-lseek.cbl
@@ -11,12 +11,15 @@
01 Lk-fd PIC 9(8) Usage COMP.
01 Lk-offset Binary-Long.
01 Lk-whence Binary-Long.
+ 88 SEEK-SET VALUE 2.
+ 88 SEEK-CUR VALUE 4.
+ 88 SEEK-END VALUE 8.
Procedure Division using
By Value Lk-fd,
By Value Lk-offset,
By Value Lk-whence
Returning Return-Value.
- Call "lseek" using
+ Call "posix_lseek" using
By Value Lk-fd,
By Value Lk-offset,
By Value Lk-whence
diff --git a/libgcobol/posix/udf/posix-unlink.cbl
b/libgcobol/posix/udf/posix-unlink.cbl
index 16dab3eebaa..5285d7ab90d 100644
--- a/libgcobol/posix/udf/posix-unlink.cbl
+++ b/libgcobol/posix/udf/posix-unlink.cbl
@@ -19,11 +19,11 @@
Returning Return-Value.
Move Lk-pathname To Ws-pathname.
- Inspect Ws-pathname
- Replacing Trailing Space By Low-Value
+ D Inspect Ws-pathname
+ D Replacing Trailing Space By Low-Value
Inspect Backward Ws-pathname Replacing Leading Space,
- - By Low-Value.
+ By Low-Value.
Call "unlink" using
By Reference Ws-pathname,
Returning Return-Value.
diff --git a/libgcobol/stringbin.cc b/libgcobol/stringbin.cc
index acbc510ace2..713bc59f863 100644
--- a/libgcobol/stringbin.cc
+++ b/libgcobol/stringbin.cc
@@ -299,12 +299,14 @@ __gg__binary_to_string_ascii(char *result, int
digits, __int128 value)
bool
__gg__binary_to_string_encoded( char *result,
- int digits,
+ size_t digits,
__int128 value,
cbl_encoding_t encoding)
{
- charmap_t *charmap = __gg__get_charmap(encoding);
- zero_char = charmap->mapped_character(ascii_0);
+ // A non-zero retval means the number was too big to fit into the
desired
+ // number of digits.
+
+ zero_char = ascii_0;
// Note that this routine does not terminate the generated string with
a
// NUL. This routine is sometimes used to generate a NumericDisplay
string
@@ -317,8 +319,6 @@ __gg__binary_to_string_encoded( char *result,
value = -value;
}
- // A non-zero retval means the number was too big to fit into the
desired
- // number of digits:
bool retval = !!(value / mask);
// mask off the bottom digits to avoid garbage when value is too large
@@ -328,7 +328,13 @@ __gg__binary_to_string_encoded( char *result,
combined.run = digits;
combined.val128 = value;
string_from_combined(combined);
- memcpy(result, combined_string, digits);
+ size_t converted_bytes;
+ const char *converted = __gg__iconverter(DEFAULT_SOURCE_ENCODING,
+ encoding,
+ combined_string,
+ digits,
+ &converted_bytes);
+ memcpy(result, converted, converted_bytes);
return retval;
}
@@ -482,8 +488,8 @@ __gg__binary_to_packed( unsigned char *result,
extern "C"
__int128
__gg__numeric_display_to_binary(unsigned char *signp,
- const unsigned char *psz,
- int n,
+ const unsigned char *pdigits,
+ int ndigits,
cbl_encoding_t encoding)
{
/* This is specific to numeric display values.
@@ -507,12 +513,13 @@ __gg__numeric_display_to_binary(unsigned char
*signp,
/* We are assuming that 64-bit arithmetic is faster than 128-bit
arithmetic,
and so we build up a 128-bit result in three 64-bit pieces, and
assemble
them at the end. */
+ size_t digit_index = 0;
+ cbl_char_t ch;
charmap_t *charmap = __gg__get_charmap(encoding);
- unsigned char zero = charmap->mapped_character(ascii_0);
- unsigned char minus = charmap->mapped_character(ascii_minus);
+ cbl_char_t minus = charmap->mapped_character(ascii_minus);
- bool is_ebcdic = (zero == 0xF0);
+ bool is_ebcdic = charmap->is_like_ebcdic();
static const uint8_t lookup[] =
{
@@ -557,7 +564,7 @@ __gg__numeric_display_to_binary(unsigned char *signp,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x40
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x50
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x60
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x70
+ 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, // 0x70
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x80
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x90
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xa0
@@ -581,7 +588,7 @@ __gg__numeric_display_to_binary(unsigned char *signp,
bool is_negative = false;
// Pick up the original sign byte:
- unsigned char sign_byte = *signp;
+ cbl_char_t sign_byte = charmap->getch(signp, (size_t)0);
const unsigned char *mapper;
if( is_ebcdic )
@@ -599,7 +606,7 @@ __gg__numeric_display_to_binary(unsigned char *signp,
// forcing the zone to 0xF0. Note that this is harmless if
redundant, and
// harmless as well if the data SIGN IS SEPARATE. Whatever we do to
this
// byte will be undone at the end of the routine.
- *signp |= 0xF0;
+ charmap->putch(sign_byte|0xF0, signp, (size_t)0);
}
else
{
@@ -613,46 +620,49 @@ __gg__numeric_display_to_binary(unsigned char
*signp,
is_negative = true;
// Make it a valid positive digit by turning the zone to 0x30
- *signp &= 0x3F;
+ charmap->putch(sign_byte&0x3F, signp, (size_t)0);
}
}
// Digits 1 through 18 come from the bottom:
- if( n <= 18 )
+ if( ndigits <= 18 )
{
- count_bottom = n;
+ count_bottom = ndigits;
count_middle = 0;
count_top = 0;
}
- else if( n<= 36 )
+ else if( ndigits<= 36 )
{
count_bottom = 18;
- count_middle = n - 18;
+ count_middle = ndigits - 18;
count_top = 0;
}
else
{
count_bottom = 18;
count_middle = 18;
- count_top = n - 36;
+ count_top = ndigits - 36;
}
- if( n & 1 )
+ if( ndigits & 1 )
{
// We are dealing with an odd number of digits
if( count_top )
{
- top = mapper[*psz++];
+ ch = charmap->getch(pdigits, &digit_index);
+ top = mapper[ch];
count_top -= 1;
}
else if( count_middle )
{
- middle = mapper[*psz++];
+ ch = charmap->getch(pdigits, &digit_index);
+ middle = mapper[ch];
count_middle -= 1;
}
else
{
- bottom = mapper[*psz++];
+ ch = charmap->getch(pdigits, &digit_index);
+ bottom = mapper[ch];
count_bottom -= 1;
}
}
@@ -661,8 +671,10 @@ __gg__numeric_display_to_binary(unsigned char *signp,
while( count_top )
{
- add_me = mapper[*psz++] << 4;
- add_me += mapper[*psz++];
+ ch = charmap->getch(pdigits, &digit_index);
+ add_me = mapper[ch] << 4;
+ ch = charmap->getch(pdigits, &digit_index);
+ add_me += mapper[ch];
top *= 100 ;
top += lookup[add_me];
count_top -= 2;
@@ -670,8 +682,10 @@ __gg__numeric_display_to_binary(unsigned char *signp,
while( count_middle )
{
- add_me = mapper[*psz++] << 4;
- add_me += mapper[*psz++];
+ ch = charmap->getch(pdigits, &digit_index);
+ add_me = mapper[ch] << 4;
+ ch = charmap->getch(pdigits, &digit_index);
+ add_me += mapper[ch];
middle *= 100 ;
middle += lookup[add_me];
count_middle -= 2;
@@ -679,8 +693,10 @@ __gg__numeric_display_to_binary(unsigned char *signp,
while( count_bottom )
{
- add_me = mapper[*psz++] << 4;
- add_me += mapper[*psz++];
+ ch = charmap->getch(pdigits, &digit_index);
+ add_me = mapper[ch] << 4;
+ ch = charmap->getch(pdigits, &digit_index);
+ add_me += mapper[ch];
bottom *= 100 ;
bottom += lookup[add_me];
count_bottom -= 2;
@@ -700,7 +716,7 @@ __gg__numeric_display_to_binary(unsigned char *signp,
}
// Replace the original sign byte:
- *signp = sign_byte; // cppcheck-suppress redundantAssignment
+ charmap->putch(sign_byte, signp, (size_t)0);
return retval;
}
diff --git a/libgcobol/stringbin.h b/libgcobol/stringbin.h
index 48c4874292a..0f30a9ff701 100644
--- a/libgcobol/stringbin.h
+++ b/libgcobol/stringbin.h
@@ -36,7 +36,7 @@ bool __gg__binary_to_string_ascii(char *result,
__int128 value);
extern "C"
bool __gg__binary_to_string_encoded(char *result,
- int digits,
+ size_t digits, // Desired digits
__int128 value,
cbl_encoding_t encoding);
diff --git a/libgcobol/valconv.cc b/libgcobol/valconv.cc
index 00fa986bda5..012f881d4cd 100644
--- a/libgcobol/valconv.cc
+++ b/libgcobol/valconv.cc
@@ -226,6 +226,9 @@ __gg__string_to_numeric_edited( char * const dest,
// We need to expand the picture string. We assume that the caller
left
// enough room in dest to take the expanded picture string.
+ // Note that we do not put on a nul terminator, so if you need one,
it's
+ // your job to put it there.
+
int dlength = expand_picture(dest, picture);
// At the present time, I am taking a liberty. In principle, a 'V'
--
2.34.1