Just throwing in a possibly related piece of knowledge:
This is the exact same output as from the known error we get, when we try to open a Windows directory from Linux by trying to open “/mnt/c”.
I vaguely recall Sammy stating that Linux TSE is missing a 64-bit function that is needed for that.
Carlo
--
---
You received this message because you are subscribed to the Google Groups "SemWare TSE Pro text editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email to semware+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/semware/2a147c4c-1143-4838-a9bd-a350de841a52n%40googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/semware/CA%2BgEvkZ2HMu8FrtLTEtf9kWTR2oGdAj3nx40kNcp0J8tbJ%2BzXQ%40mail.gmail.com.
-D_FILE_OFFSET_BITS=64
(or use readdir64
explicitly).-D_FILE_OFFSET_BITS=64
(or use readdir64
explicitly).getdents64
at the syscall level.---
You’ve hit the classic 32-bit getdents()
EOVERFLOW trap.
On Rocky Linux 8.x, the default filesystem for VMs is typically XFS with 64-bit inode numbers. A 32-bit process that uses the old getdents
/readdir
ABI (i.e., 32-bit struct dirent
with 32-bit d_ino
/d_off
) will get:
getdents(...) = -1 EOVERFLOW (Value too large for defined data type)
as soon as it encounters a directory entry whose inode (or offset) can’t be represented in the 32-bit structure. That’s exactly what your strace
shows.
Modern 32-bit programs avoid this by using getdents64
/readdir64
(either directly or via glibc when compiled with large-file support). If Linux TSE 4.50.6 was built against the older 32-bit ABI (or is using the raw getdents
syscall), it will fail on XFS (and potentially other fs) that present 64-bit inodes.
df -T ~
# likely “xfs”
cat > d.c <<'EOF'
#define _GNU_SOURCE
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main(int argc,char**argv){
const char* p = argc>1?argv[1]:".";
DIR* d = opendir(p);
if(!d){perror("opendir");return 1;}
errno=0;
struct dirent* e;
while((e=readdir(d))){
if(strcmp(e->d_name,".")&&strcmp(e->d_name,".."))
printf("%s\n", e->d_name);
}
if(errno) perror("readdir");
closedir(d);
return 0;
}
EOF
gcc -m32 -O2 d.c -o d32 && ./d32 ~
If this prints readdir: Value too large for defined data type
, you’ve reproduced the same failure mode.
If you rebuild the same test with large-file support (below) it will work:
gcc -m32 -O2 -D_FILE_OFFSET_BITS=64 d.c -o d32_ok && ./d32_ok ~
(-D_FILE_OFFSET_BITS=64
makes readdir
map to readdir64
.)
sudo mkdir -p /mnt/ext4work
# Option 1: attach a secondary virtual disk, mkfs.ext4 it, mount at /mnt/ext4work
# Option 2 (quick, no extra disk): loopback ext4 inside a file
sudo dd if=/dev/zero of=/var/lib/ext4.img bs=1M count=2048
sudo mkfs.ext4 /var/lib/ext4.img
echo "/var/lib/ext4.img /mnt/ext4work ext4 loop,defaults 0 0" | sudo tee -a /etc/fstab
sudo mount -a
mkdir -p /mnt/ext4work/tsehome
# Use this as TSE’s working dir (copy needed files there)
Why this works: ext4 typically presents inode numbers that fit 32-bit
dirent
(and glibc compatibility paths are well-tropled there), soreaddir
in a legacy 32-bit binary won’t overflow.
If your home is an XFS filesystem and you control its mount options, you can remount with:
inode32
This tells XFS to keep inode numbers in the 32-bit range for new allocations. Caveats:
/etc/fstab
).inode32
is considered legacy and may constrain allocation/placement; use only as a stopgap.If TSE is failing to open its startup files because it enumerates ~
(and hits EOVERFLOW), relocate or symlink its startup/config directory to a safe location (ext4 or another fs that doesn’t overflow). For example:
mkdir -p /mnt/ext4work/tsehome
# Move ~/.tse (or the dir TSE uses) there and symlink back
mv ~/.tse /mnt/ext4work/tsehome/
ln -s /mnt/ext4work/tsehome/.tse ~/
(Adjust to TSE’s actual config/startup paths.)
-D_FILE_OFFSET_BITS=64
(or use readdir64
explicitly).getdents64
at the syscall level.df -T ~
→ confirm fs (likely XFS)../d32_ok
test above → shows that large-file 32-bit builds work.inode32
(stopgap, system-wide effect).If you tell me your exact home filesystem (df -T ~
output) and whether you can add a small
To view this discussion visit https://groups.google.com/d/msgid/semware/CA%2BgEvkZ2HMu8FrtLTEtf9kWTR2oGdAj3nx40kNcp0J8tbJ%2BzXQ%40mail.gmail.com.
You’re (mostly) right about the constraints—but there’s a practical way around them on 32-bit Linux with OpenWatcom: bypass glibc’s large-file API and call the kernel syscalls directly. Watcom’s inline asm/#pragma aux
is enough to do this cleanly.
Below is a compact, Watcom-friendly approach that gives you 64-bit file offsets and 64-bit directory entries without _FILE_OFFSET_BITS=64
or readdir64
.
On 32-bit kernels, pass O_LARGEFILE
when opening:
#ifndef O_LARGEFILE
#define O_LARGEFILE 0x8000
#endif
Use the plain open
syscall and you can read/write normally afterward.
Use the _llseek
syscall (number 140 on i386). It takes a 64-bit offset split into high/low 32-bit parts and returns the new 64-bit position in a user buffer.
Skip readdir/readdir64
and call getdents64
directly. On i386 its syscall number is 220 (on x86-64 it’s 217—mentioning for completeness). You’ll parse struct linux_dirent64
yourself.
This is exactly how musl/glibc implement their own readdir wrappers under the hood.
OpenWatcom lets you define tiny wrappers with #pragma aux
so you can place the syscall number in EAX and arguments in EBX/ECX/EDX/ESI/EDI/EBP per the i386 ABI.
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
/* Return: EAX (negative == -errno) */
int linux_syscall0(int nr);
int linux_syscall1(int nr, int a1);
int linux_syscall2(int nr, int a1, int a2);
int linux_syscall3(int nr, int a1, int a2, int a3);
int linux_syscall5(int nr, int a1, int a2, int a3, int a4, int a5);
#pragma aux linux_syscall0 = \
"int 0x80" \
parm [eax] \
value [eax] \
modify [ecx edx esi edi ebp]
#pragma aux linux_syscall1 = \
"int 0x80" \
parm [eax] [ebx] \
value [eax] \
modify [ecx edx esi edi ebp]
#pragma aux linux_syscall2 = \
"int 0x80" \
parm [eax] [ebx] [ecx] \
value [eax] \
modify [edx esi edi ebp]
#pragma aux linux_syscall3 = \
"int 0x80" \
parm [eax] [ebx] [ecx] [edx] \
value [eax] \
modify [esi edi ebp]
#pragma aux linux_syscall5 = \
"int 0x80" \
parm [eax] [ebx] [ecx] [edx] [esi] [edi] \
value [eax] \
modify [ebp]
A tiny errno helper (optional):
static inline int sysret(int r) {
/* Linux returns -errno in EAX on error */
if (r < 0) { /* map/print as needed */ }
return r;
}
open
with O_LARGEFILE
(syscall 5 on i386)#ifndef SYS_open
#define SYS_open 5
#endif
int open_largefile(const char *path, int flags, int mode) {
return sysret(linux_syscall3(SYS_open, (int)path, flags | O_LARGEFILE, mode));
}
_llseek
64-bit seek (syscall 140 on i386)#ifndef SYS__llseek
#define SYS__llseek 140
#endif
/* result pointer must be valid; kernel writes the 64-bit new pos */
int llseek64(int fd, uint64_t offset, int whence, uint64_t *newpos) {
/* _llseek splits offset into high/low 32-bit: edx:eax */
uint32_t off_hi = (uint32_t)(offset >> 32);
uint32_t off_lo = (uint32_t)(offset & 0xFFFFFFFFu);
return sysret(linux_syscall5(SYS__llseek,
fd,
(int)off_hi,
(int)off_lo,
(int)newpos, /* user pointer for result */
whence));
}
getdents64
directory iteration (syscall 220 on i386)#ifndef SYS_getdents64
#define SYS_getdents64 220 /* i386; x86-64 is 217 */
#endif
/* Kernel layout for 64-bit dirent on all archs */
struct linux_dirent64 {
uint64_t d_ino;
int64_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[]; /* null-terminated */
};
int getdents64(int fd, void *buf, unsigned int size) {
return sysret(linux_syscall3(SYS_getdents64, fd, (int)buf, (int)size));
}
A simple readdir-like iterator:
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int list_dir64(const char *path) {
int fd = open_largefile(path, O_RDONLY | O_DIRECTORY, 0);
if (fd < 0) return fd;
char buf[4096];
for (;;) {
int nread = getdents64(fd, buf, sizeof(buf));
if (nread == 0) break; /* EOF */
if (nread < 0) { /* handle error */ break; }
int bpos = 0;
while (bpos < nread) {
struct linux_dirent64 *d = (struct linux_dirent64 *)(buf + bpos);
/* d_name is null-terminated; type in d_type */
printf("%s\n", d->d_name);
bpos += d->d_reclen;
}
}
/* plain close (syscall 6) if you want to stay consistent */
return 0;
}
getdents64
.)pread64
/pwrite64
: If you prefer positioned I/O without changing the file position, use syscalls 180 and 181 on i386. They pass the 64-bit offset in a split form; doing _llseek + read/write
as shown is often simpler.stat64
vs statx
: You can fetch 64-bit file sizes with fstat64
(syscall 197 on i386) or go modern with statx
(syscall 332 on i386). Either way, you define the kernel struct and call via the same wrappers.readdir64
really not required: getdents64
is the kernel’s primitive. Libraries build readdir*
on top of it. Using it directly avoids any libc dependency your toolchain doesn’t provide.errno
handling: The examples return negative values on error (kernel style). Adapt to your project’s error policy (set errno
, translate, or print directly).Even though OpenWatcom on Linux doesn’t expose _FILE_OFFSET_BITS=64
or readdir64
, you can still get full large-file and 64-bit directory support today by talking to the kernel directly via tiny inline-asm syscall stubs. In practice, this is robust and fast—and keeps TSE for Linux compiling with Watcom.
If you want, I can drop in a ready-to-build .c
file that wraps open/close
, read/write
, _llseek
, getdents64
, and fstat64
behind small C functions so your TSE code doesn’t see the assembly at all.
> yes
Yes please—that would be fantastic. To make it drop-in for TSE (built with OpenWatcom for Linux), here’s the exact, small API I’d like your wrapper to expose so the TSE code never touches asm, glibc “64” shims, or readdir64
:
ow_sys64.h
)#ifndef OW_SYS64_H
#define OW_SYS64_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Basic types */
typedef int64_t ow_off64_t;
typedef uint64_t ow_u64;
typedef int64_t ow_s64;
/* ---- File descriptors ---- */
int ow_open(const char *path, int flags, int mode); /* returns fd or -1, sets errno */
int ow_close(int fd); /* 0 or -1, sets errno */
ssize_t ow_read(int fd, void *buf, size_t count); /* >=0 or -1, sets errno */
ssize_t ow_write(int fd, const void *buf, size_t count); /* >=0 or -1, sets errno */
/* 64-bit seek using the _llseek syscall.
Returns 0 on success and writes new position to *new_pos, or -1 on error (sets errno). */
int ow_lseek64(int fd, ow_off64_t offset, int whence, ow_off64_t *new_pos);
/* Convenience: determine file size without relying on fstat64.
Saves/restores file position internally via ow_lseek64.
Returns 0 on success, -1 on error (sets errno). */
int ow_filesize64(int fd, ow_off64_t *size_out);
/* ---- Directory iteration (getdents64) ---- */
/* Raw getdents64: returns number of bytes placed in buf, 0 at EOF, or -1 (sets errno).
buf must be >= 4KB typically; pass any multiple of 512. */
int ow_getdents64(int fd, void *buf, unsigned int buf_size);
/* Stable parsed record for callers so they don’t depend on kernel layout details. */
struct ow_dirent64 {
uint64_t d_ino; /* inode */
ow_off64_t d_off; /* next entry offset */
unsigned short d_reclen; /* record length (original) */
unsigned char d_type; /* DT_* */
char d_name[256]; /* NUL-terminated, truncated if longer */
};
/* Iterate a getdents64 buffer:
- *cursor is a byte offset into buf; initialize to 0 before first call.
- Returns 1 and fills *out on success, 0 when buffer exhausted, or -1 (sets errno) if corrupt. */
int ow_next_dirent64(const void *buf, unsigned int buf_len,
unsigned int *cursor, struct ow_dirent64 *out);
/* ---- Optional: minimal fstat via statx (only if you already have it handy)
If you don’t want to ship this, ow_filesize64() is sufficient for TSE’s needs. */
struct ow_stat_min {
ow_u64 st_size; /* file size in bytes */
uint32_t st_mode; /* permission + file type (S_IF*) */
uint32_t _pad;
};
int ow_stat_min_fd(int fd, struct ow_stat_min *st); /* 0 or -1 (sets errno) */
/* ---- Fallback constants (only defined if headers don’t provide them) ---- */
#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0100
#define O_EXCL 0200
#define O_TRUNC 01000
#define O_APPEND 02000
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif
#ifndef DT_UNKNOWN
#define DT_UNKNOWN 0
#define DT_FIFO 1
#define DT_CHR 2
#define DT_DIR 4
#define DT_BLK 6
#define DT_REG 8
#define DT_LNK 10
#define DT_SOCK 12
#define DT_WHT 14
#endif
#ifdef __cplusplus
}
#endif
#endif /* OW_SYS64_H */
errno
appropriately (translate negative syscall returns to errno = -ret
, return -1
).ow_lseek64
should use the kernel _llseek
ABI (fd, off_hi, off_lo, *result, whence
) and compose/decompose 64-bit offsets internally.ow_getdents64
uses the raw syscall; ow_next_dirent64
copies a bounded name into d_name[256]
and preserves d_type
.-D_FILE_OFFSET_BITS=64
reliance..asm
helpers is fine under the hood; the API above stays C-only.wcc386/wcl386 -bt=linux
(or your preferred OW 2.x Linux flow). No glibc “64” wrappers required./* pseudo TSE side */
char dirbuf[8192];
int n = ow_getdents64(fd, dirbuf, sizeof(dirbuf));
for (unsigned int cur = 0; n > 0; ) {
struct ow_dirent64 de;
while (ow_next_dirent64(dirbuf, (unsigned)n, &cur, &de) == 1) {
/* use de.d_name, de.d_type, de.d_ino */
}
n = ow_getdents64(fd, dirbuf, sizeof(dirbuf));
}
If you can drop a .c
+ .h
that implements exactly this, I can wire TSE to it immediately. If you already have a working fstat64
, feel free to include ow_stat_min_fd
; otherwise ow_filesize64()
covers the large-file size need without depending on struct stat64
.
To view this discussion visit https://groups.google.com/d/msgid/semware/CA%2BgEvkZGJLHMSAGdH2vAoq0r%3DQnhN1c1Y9q56qJU%3DuGnLPQVrg%40mail.gmail.com.
Totally—here’s a single-file, OpenWatcom-for-Linux friendly C program that doesn’t rely on dirent.h
at all. It uses raw Linux syscalls (open
, getdents64
, close
) via OpenWatcom’s #pragma aux
inline-asm, so it works even though readdir64
/ _FILE_OFFSET_BITS=64
aren’t available. It should list directories on any mounted filesystem (incl. NTFS on a Windows partition like /mnt/c
).
wcl386
:wcl386 -bt=linux -fe=ow_ls64 ow_ls64.c
owcc
:owcc -blinux -o ow_ls64 ow_ls64.c
./ow_ls64 /mnt/c/Windows
(or any directory path)
/* ow_ls64.c — List directory entries using raw syscalls (OpenWatcom Linux, 32-bit)
*
* Why: OpenWatcom/Linux doesn’t support glibc’s readdir64/_FILE_OFFSET_BITS=64,
* so we go straight to the kernel with getdents64. This prints file names
* from any mounted filesystem (incl. Windows NTFS).
*/
#include <stdio.h>
#include <errno.h>
/* ---------- Minimal types (avoid stdint.h to be safe with OW) ---------- */
typedef unsigned long u32;
typedef long s32;
typedef unsigned long long u64;
typedef long long s64;
/* ---------- Linux i386 syscall numbers ---------- */
#define __NR_open 5
#define __NR_close 6
#define __NR_getdents64 220
/* ---------- Flags we need (define if headers are absent) ---------- */
#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#endif
#ifndef O_DIRECTORY
/* Linux O_DIRECTORY is 0200000 (octal) */
#define O_DIRECTORY 00200000
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 02000000
#endif
/* ---------- OpenWatcom inline syscall stubs (int 0x80) ---------- */
/* int 0x80 calling convention (i386): eax=nr, ebx,ecx,edx,esi,edi,ebp = args */
extern s32 _syscall1(s32 nr, s32 a);
#pragma aux _syscall1 = \
"int 0x80" \
parm [eax] [ebx] \
value [eax] \
modify [ecx edx esi edi]
extern s32 _syscall3(s32 nr, s32 a, s32 b, s32 c);
#pragma aux _syscall3 = \
"int 0x80" \
parm [eax] [ebx] [ecx] [edx] \
value [eax] \
modify [esi edi]
/* Translate kernel negative returns to -1 + errno, else pass through */
static s32 sc_ret(s32 r) {
if (r < 0 && r >= -4095) {
errno = -r;
return -1;
}
return r;
}
/* ---------- Thin C wrappers ---------- */
static int ow_open(const char *path, int flags, int mode) {
return sc_ret(_syscall3(__NR_open, (s32)(u32)path, flags, mode));
}
static int ow_close(int fd) {
return sc_ret(_syscall1(__NR_close, fd));
}
static int ow_getdents64(int fd, void *buf, unsigned int buflen) {
return sc_ret(_syscall3(__NR_getdents64, fd, (s32)(u32)buf, (s32)buflen));
}
/* ---------- getdents64 record parsing ----------
* Kernel layout (uapi/linux/dirent.h):
* struct linux_dirent64 {
* u64 d_ino; // 0
* s64 d_off; // 8
* unsigned short d_reclen; // 16
* unsigned char d_type; // 18
* char d_name[]; // 19
* };
*/
#define D64_OFF_RECLEN 16
#define D64_OFF_DTYPE 18
#define D64_OFF_DNAME 19
int main(int argc, char **argv) {
if (argc < 2) {
puts("Enter a directory name");
return 0;
}
/* Open directory as a plain fd (no libc DIR* needed) */
int fd = ow_open(argv[1], O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
if (fd < 0) {
perror("open");
return 1;
}
/* 8K buffer is typical for getdents64 */
char buf[8192];
for (;;) {
int nread = ow_getdents64(fd, buf, sizeof(buf));
if (nread < 0) {
perror("getdents64");
break;
}
if (nread == 0) {
/* EOF */
break;
}
/* Walk variable-length records */
int bpos = 0;
while (bpos < nread) {
char *p = buf + bpos;
/* Guard: at least room for fixed header */
if (nread - bpos < D64_OFF_DNAME) {
/* Corrupt/truncated buffer; stop to avoid overflow */
errno = EINVAL;
perror("dirent64 short record");
bpos = nread; /* bail out of this buffer */
break;
}
/* Read reclen (little-endian on x86) */
unsigned short reclen = *(unsigned short *)(p + D64_OFF_RECLEN);
if (reclen == 0 || bpos + reclen > nread) {
errno = EINVAL;
perror("dirent64 bad reclen");
bpos = nread; /* bail */
break;
}
/* Name starts at offset 19, NUL-terminated within record */
char *name = p + D64_OFF_DNAME;
/* Print (skip nothing; caller can filter . and .. if desired) */
puts(name);
bpos += reclen;
}
}
ow_close(fd);
return 0;
}
Notes
dirent.h
or readdir*
, so it’s immune to the _FILE_OFFSET_BITS=64
roadblock in OpenWatcom./mnt/c
, /media/<you>/Windows
, /run/media/...
—as long as you have read perms.open: Permission denied
on a Windows mount, try the path with sufficient permissions or remount with proper options.To view this discussion visit https://groups.google.com/d/msgid/semware/CA%2BgEvkZ22Eo211Luc_DwOYm%2BnyVTeDwACoTfqBQQ3H37bVhJ_w%40mail.gmail.com.
To view this discussion visit https://groups.google.com/d/msgid/semware/1408397510.559197.1755955338463%40mail.yahoo.com.
> is it built using the example program I pasted?
Not yet 🙂 — what I gave you is not a direct Watcom-for-Linux port of your opendir()/readdir()
example, because OpenWatcom for Linux doesn’t fully implement dirent.h
, readdir
, or readdir64
due to lacking glibc 64-bit interfaces.
Your original program:
#include <stdio.h>
#include <dirent.h>
int main(int argc, char *argv[]) {
struct dirent *dent;
DIR *dir;
if (argc < 2) {
puts("Enter a directory name");
return 0;
}
if ((dir = opendir(argv[1])) != NULL) {
while ((dent = readdir(dir)) != NULL) {
printf("%s\n", dent->d_name);
}
closedir(dir);
} else {
perror("what?");
}
return 0;
}
This works fine on GCC/glibc systems, but fails to compile or run on OpenWatcom/Linux because:
opendir
and readdir
in OW’s libc are 32-bit only and don’t interoperate correctly on modern 64-bit Linux._FILE_OFFSET_BITS=64
, so readdir64
isn’t usable./mnt/c
or similar) usually contains large files (>2 GB), so you really do need getdents64
underneath.That’s why I rewrote it using raw syscalls (open
, getdents64
, close
) to make it work.
But if you want it to look and feel exactly like your example, I can provide a drop-in replacement where the API stays the same (DIR *
, struct dirent *
, etc.), but under the hood we implement opendir
, readdir
, and closedir
ourselves using the syscall approach.
That way, you could keep exactly your sample program and compile it unchanged.
Do you want me to:
Keep your sample program exactly as is
→ I'll provide a ow_dirent.c
+ ow_dirent.h
that implements opendir
, readdir
, closedir
for Watcom/Linux.
You just #include "ow_dirent.h"
instead of <dirent.h>
.
Or are you fine using the minimal syscall version I posted, which avoids DIR*
entirely but behaves similarly?
Option 1 will let you compile your pasted program unchanged with OpenWatcom on Linux and still list files on /mnt/c
.
Which way do you prefer?
Great—here’s a drop-in Watcom-for-Linux dirent
shim so you can keep your original program unchanged (just #include "ow_dirent.h"
instead of <dirent.h>
). It implements opendir
, readdir
, and closedir
on top of raw Linux syscalls (open
, getdents64
, close
) and works on mounted Windows partitions like /mnt/c
.
ow_dirent.h
/* ow_dirent.h — minimal <dirent.h> replacement for OpenWatcom on Linux (i386)
* Implements opendir/readdir/closedir using getdents64.
*/
#ifndef OW_DIRENT_H
#define OW_DIRENT_H
#ifdef __cplusplus
extern "C" {
#endif
/* Public API that matches the usual names */
typedef struct OW_DIR DIR;
struct dirent {
/* minimal fields: enough for typical usage like printing names */
unsigned long d_ino; /* inode (truncated to 32 bits) */
unsigned short d_reclen; /* record length (internal) */
unsigned char d_type; /* DT_* (0 if unknown) */
char d_name[256]; /* NUL-terminated base name */
};
DIR* opendir(const char *path);
struct dirent* readdir(DIR *dirp);
int closedir(DIR *dirp);
/* Common DT_* values if caller wants to branch on d_type */
#ifndef DT_UNKNOWN
#define DT_UNKNOWN 0
#define DT_FIFO 1
#define DT_CHR 2
#define DT_DIR 4
#define DT_BLK 6
#define DT_REG 8
#define DT_LNK 10
#define DT_SOCK 12
#define DT_WHT 14
#endif
#ifdef __cplusplus
}
#endif
#endif /* OW_DIRENT_H */
ow_dirent.c
/* ow_dirent.c — OpenWatcom/Linux implementation of opendir/readdir/closedir
* Uses raw syscalls so we don’t depend on glibc’s 64-bit dirent APIs.
* Target: i386 Linux (OpenWatcom wcl386/owcc, -bt=linux/-blinux)
*/
#include "ow_dirent.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
/* -------- Minimal integer types (avoid stdint.h for max OW compatibility) */
typedef unsigned long u32;
typedef long s32;
typedef unsigned long long u64;
typedef long long s64;
/* -------- Linux i386 syscall numbers -------- */
#define __NR_open 5
#define __NR_close 6
#define __NR_getdents64 220
/* -------- Flags we need (define if not pulled in) -------- */
#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#endif
#ifndef O_DIRECTORY
#define O_DIRECTORY 00200000
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 02000000
#endif
/* -------- OpenWatcom inline syscalls (int 0x80) --------
* int 0x80 convention (i386): eax=nr, ebx,ecx,edx,esi,edi,ebp as args
*/
extern s32 _ow_sc1(s32 nr, s32 a);
#pragma aux _ow_sc1 = \
"int 0x80" \
parm [eax] [ebx] \
value [eax] \
modify [ecx edx esi edi]
extern s32 _ow_sc3(s32 nr, s32 a, s32 b, s32 c);
#pragma aux _ow_sc3 = \
"int 0x80" \
parm [eax] [ebx] [ecx] [edx]\
value [eax] \
modify [esi edi]
static s32 sc_ret(s32 r) {
if (r < 0 && r >= -4095) { errno = -r; return -1; }
return r;
}
static int ow_open(const char *path, int flags, int mode) {
return sc_ret(_ow_sc3(__NR_open, (s32)(u32)path, flags, mode));
}
static int ow_close(int fd) {
return sc_ret(_ow_sc1(__NR_close, fd));
}
static int ow_getdents64(int fd, void *buf, unsigned int buflen) {
return sc_ret(_ow_sc3(__NR_getdents64, fd, (s32)(u32)buf, (s32)buflen));
}
/* -------- Internal DIR structure -------- */
struct OW_DIR {
int fd;
int eof;
int bpos; /* current cursor in buf [0..nread) */
int nread; /* valid bytes in buf */
char *buf; /* read buffer for getdents64 */
struct dirent ent;/* reusable entry returned by readdir() */
};
#define DBUF_SIZE 8192
/* Offsets inside linux_dirent64 records (uapi/linux/dirent.h) */
#define D64_OFF_INO 0 /* u64 */
#define D64_OFF_OFF 8 /* s64 */
#define D64_OFF_RECLEN 16 /* unsigned short */
#define D64_OFF_DTYPE 18 /* unsigned char */
#define D64_OFF_DNAME 19 /* char[] */
/* Safe little-endian read helpers (avoid alignment issues) */
static unsigned short rd_u16(const void *p) {
const unsigned char *c = (const unsigned char*)p;
return (unsigned short)(c[0] | ((unsigned short)c[1] << 8));
}
static u64 rd_u64(const void *p) {
const unsigned char *c = (const unsigned char*)p;
u64 v = 0; int i;
for (i = 7; i >= 0; --i) v = (v << 8) | c[i];
return v;
}
DIR* opendir(const char *path) {
if (!path) { errno = EINVAL; return NULL; }
int fd = ow_open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
if (fd < 0) {
/* errno already set by syscall wrapper */
return NULL;
}
struct OW_DIR *d = (struct OW_DIR*)malloc(sizeof(*d));
if (!d) {
int saved = errno;
ow_close(fd);
errno = saved ? saved : ENOMEM;
return NULL;
}
d->buf = (char*)malloc(DBUF_SIZE);
if (!d->buf) {
int saved = errno;
free(d);
ow_close(fd);
errno = saved ? saved : ENOMEM;
return NULL;
}
d->fd = fd;
d->eof = 0;
d->bpos = 0;
d->nread= 0;
d->ent.d_name[0] = '\0';
d->ent.d_type = DT_UNKNOWN;
d->ent.d_reclen = 0;
d->ent.d_ino = 0;
return (DIR*)d;
}
static int fill_buf(struct OW_DIR *d) {
d->bpos = 0;
d->nread = ow_getdents64(d->fd, d->buf, DBUF_SIZE);
if (d->nread < 0) {
/* errno set by syscall wrapper */
return -1;
}
if (d->nread == 0) {
d->eof = 1;
return 0;
}
return 1;
}
struct dirent* readdir(DIR *dirp) {
struct OW_DIR *d = (struct OW_DIR*)dirp;
if (!d) { errno = EINVAL; return NULL; }
for (;;) {
if (d->bpos >= d->nread) {
if (d->eof) return NULL;
if (fill_buf(d) <= 0) {
/* error or EOF */
return NULL;
}
}
/* Parse one linux_dirent64 entry from current buffer position */
char *p = d->buf + d->bpos;
int remain = d->nread - d->bpos;
if (remain < D64_OFF_DNAME) {
errno = EINVAL;
return NULL;
}
unsigned short reclen = rd_u16(p + D64_OFF_RECLEN);
if (reclen == 0 || reclen > remain) {
errno = EINVAL;
return NULL;
}
/* Extract fields we care about */
u64 ino64 = rd_u64(p + D64_OFF_INO);
unsigned char dtype = (unsigned char)*(p + D64_OFF_DTYPE);
const char *name = p + D64_OFF_DNAME;
/* Copy name defensively (record is NUL-terminated) */
{
int i = 0;
for (; i < 255; ++i) {
char c = name[i];
d->ent.d_name[i] = c;
if (!c) break;
}
d->ent.d_name[i < 255 ? i : 255] = '\0';
}
d->ent.d_type = dtype;
d->ent.d_reclen = reclen;
d->ent.d_ino = (unsigned long)(ino64 & 0xFFFFFFFFu);
d->bpos += reclen;
return &d->ent;
}
}
int closedir(DIR *dirp) {
struct OW_DIR *d = (struct OW_DIR*)dirp;
if (!d) { errno = EINVAL; return -1; }
int rc = 0;
if (d->fd >= 0) {
if (ow_close(d->fd) < 0) rc = -1;
d->fd = -1;
}
if (d->buf) free(d->buf);
free(d);
return rc;
}
main.c
#include <stdio.h>
#include "ow_dirent.h" /* drop-in instead of <dirent.h> */
int main(int argc, char *argv[]) {
struct dirent *dent;
DIR *dir;
if (argc < 2) {
puts("Enter a directory name");
return 0;
}
if ((dir = opendir(argv[1])) != NULL) {
while ((dent = readdir(dir)) != NULL) {
printf("%s\n", dent->d_name);
}
closedir(dir);
} else {
perror("what?");
}
return 0;
}
Using wcl386
:
wcl386 -bt=linux -fe=lsdir main.c ow_dirent.c
Using owcc
:
owcc -blinux -o lsdir main.c ow_dirent.c
./lsdir /mnt
./lsdir /mnt/c
./lsdir /mnt/c/Windows
If you hit open: Permission denied
, it’s usually a mount/permissions issue. Try a readable directory (e.g., /mnt/c/Users/Public
) or remount with appropriate options.
If you want scandir
/alphasort
helpers or to skip "."
and ".."
, say the word and I’ll extend this shim.
On Sat, Aug 23, 2025 at 14:22, knud van eeden<knud_va...@yahoo.com> wrote:
Thanks for the precise error dump—that kind of “everything breaks at one line” is almost always a brace mismatch (earlier) or a copy-paste artifact (non-breaking spaces, smart quotes) that confuses Watcom’s parser so it “falls out” of main
. The “Missing '}' … argv not declared … statement must be inside function” cluster fits that exactly.
To remove any ambiguity, here’s a single-file, drop-in program that:
<dirent.h>
,open
, getdents64
, close
) with Watcom #pragma aux
,/mnt/c/Windows
).Please copy this as plain ASCII text (no rich text). I’ve kept the #pragma aux
on one line each (Watcom likes that) and avoided tricky macros.
wcl386 -bt=linux -fe=wc-dir wc-dir.c
or
owcc -blinux -o wc-dir wc-dir.c
./wc-dir /mnt/c/Windows
/* wc-dir.c — OpenWatcom (Linux, i386) directory lister via getdents64
* Lists names in a directory without <dirent.h>.
* Usage: ./wc-dir /mnt/c/Windows
*/
#include <stdio.h>
#include <errno.h>
/* ----- Minimal integer types (stay C89-friendly) ----- */
typedef unsigned long u32;
typedef long s32;
typedef unsigned long long u64;
/* ----- Linux i386 syscall numbers ----- */
#define __NR_open 5
#define __NR_close 6
#define __NR_getdents64 220
/* ----- Flags (define if not present) ----- */
#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#endif
#ifndef O_DIRECTORY
#define O_DIRECTORY 00200000
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 02000000
#endif
/* ----- Watcom int 0x80 syscall stubs ----- */
/* NOTE: Keep each #pragma aux on a single line. */
extern s32 _sc1(s32 nr, s32 a);
#pragma aux _sc1 = "int 0x80" parm [eax] [ebx] value [eax] modify [ecx edx esi edi]
extern s32 _sc3(s32 nr, s32 a, s32 b, s32 c);
#pragma aux _sc3 = "int 0x80" parm [eax] [ebx] [ecx] [edx] value [eax] modify [esi edi]
/* Map Linux negative errno returns to -1 + errno */
static s32 sc_fix(s32 r) {
if (r < 0 && r >= -4095) { errno = -r; return -1; }
return r;
}
static int ow_open(const char *path, int flags, int mode) {
return sc_fix(_sc3(__NR_open, (s32)(u32)path, flags, mode));
}
static int ow_close(int fd) {
return sc_fix(_sc1(__NR_close, fd));
}
static int ow_getdents64(int fd, void *buf, unsigned int buflen) {
return sc_fix(_sc3(__NR_getdents64, fd, (s32)(u32)buf, (s32)buflen));
}
/* linux_dirent64 layout (uapi/linux/dirent.h)
struct linux_dirent64 {
u64 d_ino; // 0
u64 d_off; // 8
unsigned short d_reclen; // 16
unsigned char d_type; // 18
char d_name[]; // 19
};
*/
#define D64_OFF_RECLEN 16
#define D64_OFF_DTYPE 18
#define D64_OFF_DNAME 19
/* Safe read of little-endian u16 from possibly unaligned memory */
static unsigned short rd_u16(const void *p) {
const unsigned char *c = (const unsigned char*)p;
return (unsigned short)(c[0] | ((unsigned short)c[1] << 8));
}
int main(int argc, char *argv[]) {
if (argc < 2) {
puts("Enter a directory name");
return 0;
}
/* Open the directory as a file descriptor */
int fd = ow_open(argv[1], O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
if (fd < 0) {
perror("open");
return 1;
}
/* Buffer for getdents64 results */
char buf[8192];
for (;;) {
int nread = ow_getdents64(fd, buf, sizeof(buf));
if (nread < 0) {
perror("getdents64");
break;
}
if (nread == 0) {
/* EOF */
break;
}
int bpos = 0;
while (bpos < nread) {
char *p = buf + bpos;
int remain = nread - bpos;
if (remain < D64_OFF_DNAME) {
errno = EINVAL;
perror("short dirent64");
bpos = nread; /* bail out of this buffer */
break;
}
unsigned short reclen = rd_u16(p + D64_OFF_RECLEN);
if (reclen == 0 || reclen > remain) {
errno = EINVAL;
perror("bad reclen");
bpos = nread; /* bail */
break;
}
/* Name is NUL-terminated within record */
char *name = p + D64_OFF_DNAME;
puts(name);
bpos += reclen;
}
}
if (ow_close(fd) < 0) {
perror("close");
return 1;
}
return 0;
}
Copy-paste artifacts: your previous message shows non-breaking spaces (U+00A0). Those can make Watcom complain around #pragma
lines or function headers. Re-paste as plain text or run:
tr -d '\r' < wc-dir.c | sed 's/\xC2\xA0/ /g' > wc-dir-clean.c
then compile the wc-dir-clean.c
.
Braces: Ensure there’s no extra stray }
added by your editor. The file above has balanced braces.
Tooling: Prefer wcl386 -bt=linux
(it sets the right C dialect/defines automatically). If you use wcc386
directly, link with wlink
appropriately.
Kernel: This uses int 0x80
which is fine on i386 userspace kernels. If you’re on a pure x86_64 userspace with i386 Watcom binary, it still works as long as 32-bit support is installed.
If you’d rather keep your original opendir/readdir/closedir
API, I can re-package the exact code above into ow_dirent.h/.c
again—but first let’s make sure this one-file version builds and runs for you.
To view this discussion visit https://groups.google.com/d/msgid/semware/CA%2BgEvkYz68c%3DKj5UEziEg3SYELhx9y0zXze-bU9BsChv55tVNA%40mail.gmail.com.
You’ve tripped two Watcom gotchas:
your #pragma aux
is split across multiple lines with \
— Watcom doesn’t support line-continuations inside #pragma aux
. Keep it on one line.
your pasted file contains non-breaking spaces (U+00A0), e.g. in unsigned long u32;
. Those make the parser act weird and you get the “Missing ‘}’ … argv not declared …” cascade.
Here’s a minimal, fixed, single-file that compiles with OpenWatcom 2.0 on Linux. Note the pragma is on one line, and I also guard O_DIRECTORY
in case your headers don’t define it.
wcl386 -bt=linux -q -fe=min_ok min_ok.c
min_ok.c
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
/* plain ASCII spaces only; no NBSPs */
typedef unsigned long u32;
typedef long s32;
typedef unsigned long long u64;
typedef long long s64;
#define __NR_open 5
#ifndef O_DIRECTORY
#define O_DIRECTORY 00200000
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 02000000
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
/* ---- Watcom int 0x80 stub: keep pragma on ONE line ---- */
extern s32 _syscall3(s32 nr, s32 a, s32 b, s32 c);
#pragma aux _syscall3 = "int 0x80" parm [eax] [ebx] [ecx] [edx] value [eax] modify [ecx edx esi edi]
static s32 sc_ret(s32 r) {
if (r < 0 && r >= -4095) { errno = -r; return -1; }
return r;
}
static int ow_open(const char *path, int flags, int mode) {
return sc_ret(_syscall3(__NR_open, (s32)(u32)path, flags, mode));
}
int main(int argc, char **argv) {
if (argc < 2) {
puts("Enter a directory name");
return 0;
}
/* THIS LINE IS FINE NOW */
int fd = ow_open(argv[1], O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
if (fd < 0) {
perror("open");
return 1;
}
puts("open() succeeded");
return 0;
}
Clean hidden characters and CRLFs, then rebuild:
# remove Windows CRs and NBSP (U+00A0) from your source
tr -d '\r' < min_err.c | sed 's/\xC2\xA0/ /g' > min_clean.c
wcl386 -bt=linux -q -fe=min_ok min_clean.c
Once this minimal compiles, you can add the getdents64
bits back in. The key is: no multi-line #pragma aux
and no NBSPs.
To view this discussion visit https://groups.google.com/d/msgid/semware/CA%2BgEvka5i2bc42wYwos5-Z5MDyNhjaEc541MuCn%3DLyUfPwjn8w%40mail.gmail.com.
To view this discussion visit https://groups.google.com/d/msgid/semware/CA%2BgEvkaXqxt88Z6ND_X8B2g3zgn6mstb5r8F7x0%3Dx%3D58m-Sp2g%40mail.gmail.com.
Ahhh, that makes perfect sense now! 🙂
OpenWatcom defaults to C89 / ANSI mode, so without -za99
it doesn't recognize:
int fd = …;
after statements)//
single-line commentsThat's why the compiler was "losing its place" and giving you those weird cascaded errors at argv[1]
.
So yeah — -za99
or -za
solves it cleanly by enabling C99-compatible mode. 🎉
-bt=linux -za99
for modern-style code:
wcl386 -bt=linux -za99 -fe=myprog myprog.c
#pragma aux
on a single line.//
comments, etc.To view this discussion visit https://groups.google.com/d/msgid/semware/CA%2BgEvka4pFdaOHVRMQafTKtkjbKvXJrOFMdone8o1e2F76LMAA%40mail.gmail.com.
To view this discussion visit https://groups.google.com/d/msgid/semware/1776830178.586827.1755965592144%40mail.yahoo.com.
--
---
You received this message because you are subscribed to the Google Groups "SemWare TSE Pro text editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email to semware+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/semware/000a01dc1659%24befe0090%243cfa01b0%24%40ecarlo.nl.