Linux open error on directory

34 views
Skip to first unread message

Tom Collins

unread,
Aug 22, 2025, 10:31:11 AMAug 22
to SemWare TSE Pro Text Editor
I'm using Linux TSE 4.50.6, and have run into an issue on a newer Rocky Linux 8.10 VM.  Using OpenFile(), TSE fails to open some files and directories (to include the startup files) that it should.  For my test case, I tried to display the list of files in my home directory, and saw the error "Path not found:/home/tcollins/*.*".  To debug, I used strace:

_newselect(1, [0], NULL, NULL, {tv_sec=0, tv_usec=100}) = 0 (Timeout)
gettimeofday({tv_sec=1755872009, tv_usec=10459}, NULL) = 0
_newselect(1, [0], NULL, NULL, {tv_sec=0, tv_usec=100}) = 1 (in [0], left {tv_sec=0, tv_usec=98})
read(0, "\r", 10)                       = 1
write(1, "\33[?25h\33[2 q\33[?25h\33[2 q\33[?25l\33[4;"..., 341) = 341
write(1, "\33[?25l\33[2;1H\33[?25h", 18) = 18
stat64("/home", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
stat64("/home/tcollins", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/tcollins", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat64("/home/tcollins/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/home/tcollins", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 3
getdents(3, 0x992ee70, 2144)            = -1 EOVERFLOW (Value too large for defined data type)
close(3)                                = 0
stat64("/home", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
stat64("/home/tcollins", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/tcollins", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
write(1, "\33[?25l\33[4;2H\33[38;5;16m\33[48;5;250"..., 566) = 566
gettimeofday({tv_sec=1755872009, tv_usec=12344}, NULL) = 0

Seems like something with the getdents() call.  Possibly related:


Thanks!

Carlo Hogeveen

unread,
Aug 22, 2025, 10:49:35 AMAug 22
to sem...@googlegroups.com

 

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

 

 

Henriques18

unread,
Aug 22, 2025, 2:12:58 PMAug 22
to SemWare TSE Pro text editor
Could you try just * and not *.*

Op vrijdag 22 augustus 2025 om 16:49:35 UTC+2 schreef Carlo Hogeveen:

Tom Collins

unread,
Aug 22, 2025, 2:27:10 PMAug 22
to sem...@googlegroups.com
"*", "*.*", and just hitting <Tab> after the path in the EditFile() prompt fail in the same manner.  TSE interprets <Tab> as "*.*".

--

---
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.

Carlo Hogeveen

unread,
Aug 22, 2025, 2:44:11 PMAug 22
to sem...@googlegroups.com

S.E. Mitchell

unread,
Aug 22, 2025, 5:08:53 PMAug 22
to sem...@googlegroups.com
It is basically what Carlo alluded to.
TSE is a 32-bit app.
While stat() works fine, opendir() and readdir() fail when "something"
to do with the mount is 64-bit.
Until I get a 64-bit version, the only work-around I can think of is:
When opendir() fails, check to see if it is errno is EOVERFLOW, and if
so, capture the output of a small 64-bit program that does opendir(),
readdir() on the path given. Store it in memory in an appropriate
format.
On subsequent query's to get the next file, use what was captured earlier.
It might work :)
> --
>
> ---
> 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/CAODxuObR0Djk02pjtheLfw40O%2Bg0mXw9bU8xeWr9n40gMUm8_g%40mail.gmail.com.

Tom Collins

unread,
Aug 22, 2025, 5:21:04 PMAug 22
to sem...@googlegroups.com
Very good.  That workaround is an interesting technique - maybe just always use the 64-bit program for opendir()+readdir() on Linux.  

If you put this together, I'll be happy to test it.  Thanks!

knud van eeden

unread,
Aug 23, 2025, 6:28:23 AMAug 23
to sem...@googlegroups.com


  • Add -D_FILE_OFFSET_BITS=64 (or use readdir64 explicitly).
wfg K
---

 fix (requires a rebuild by TSE)

  • Recompile TSE’s Linux build as a 32-bit program with large-file support:
    • Add -D_FILE_OFFSET_BITS=64 (or use readdir64 explicitly).
    • Or switch to calling getdents64 at the syscall level.
  • This makes the 32-bit binary compatible with filesystems exposing 64-bit inode numbers.


---


You’ve hit the classic 32-bit getdents() EOVERFLOW trap.

What’s happening

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.


How to confirm quickly

  1. Check the fs type of your home:
df -T ~
# likely “xfs”
  1. Quick reproducer (shows the same EOVERFLOW for a 32-bit build that uses old ABI):
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.)


Practical workarounds (no rebuild of TSE)

A) Work under a filesystem that doesn’t overflow 32-bit dirents

  • If your home is on XFS, create a workspace on ext4 and work there.
    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)
    
  • Alternatively, if you already have an ext4 partition (or a mounted share) available, point TSE to use that path.

Why this works: ext4 typically presents inode numbers that fit 32-bit dirent (and glibc compatibility paths are well-tropled there), so readdir in a legacy 32-bit binary won’t overflow.

B) (XFS only, not always advisable) mount with inode32

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:

  • It’s per-filesystem, requires remount (and ideally entry in /etc/fstab).
  • Not all existing inodes will be “fixed” if they were already allocated with 64-bit values.
  • inode32 is considered legacy and may constrain allocation/placement; use only as a stopgap.

C) Use a different directory for TSE’s startup/config

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.)


The “right” fix (requires a rebuild by TSE)

  • Recompile TSE’s Linux build as a 32-bit program with large-file support:
    • Add -D_FILE_OFFSET_BITS=64 (or use readdir64 explicitly).
    • Or switch to calling getdents64 at the syscall level.
  • This makes the 32-bit binary compatible with filesystems exposing 64-bit inode numbers.

Quick checklist for your VM

  • [ ] df -T ~ → confirm fs (likely XFS).
  • [ ] Try ./d32_ok test above → shows that large-file 32-bit builds work.
  • [ ] Pick a workaround:
    • Use an ext4 working directory for TSE (fastest & safest).
    • (XFS) Remount with inode32 (stopgap, system-wide effect).
    • Relocate TSE’s startup/config to the ext4 directory.

If you tell me your exact home filesystem (df -T ~ output) and whether you can add a small 


Sent from Yahoo Mail on Samsung Galaxy S24 Ultra / 1 terabyte / artificial intelligence

S.E. Mitchell

unread,
Aug 23, 2025, 6:35:20 AMAug 23
to sem...@googlegroups.com
Currently, TSE for Linux can _only_ be compiled with Watcom C for Linux.
Watcom C does not support -D_FILE_OFFSET_BITS=64
Watcom C does not support readdir64

Watcom does have built-in asm, so perhaps there is a way around this?
But I have not found it yet.

I would love to be proved wrong!
--
> To view this discussion visit https://groups.google.com/d/msgid/semware/359083821.524112.1755944894941%40mail.yahoo.com.

knud van eeden

unread,
Aug 23, 2025, 7:43:09 AMAug 23
to sem...@googlegroups.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.


What works reliably with Watcom on 32-bit Linux

1) Open large files

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.

2) Seek to 64-bit offsets

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.

3) Read directory entries in 64-bit form

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.


Minimal syscall wrappers for OpenWatcom

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;
}

Large-file building blocks

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;
}

Notes & gotchas

  • Architecture: OpenWatcom’s Linux target is 32-bit i386. The syscall numbers above are the i386 ones. (If you ever cross-compile for x86-64, several numbers differ—most notably 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).

Bottom line

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:

Header contract (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 */

Behavior & error conventions

  • All functions return the usual Linux semantics and set 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.
  • Thread-safe and re-entrant (no static internal buffers).

Build expectations (OpenWatcom, 32-bit Linux target)

  • No -D_FILE_OFFSET_BITS=64 reliance.
  • Inline asm via OpenWatcom pragmas or tiny .asm helpers is fine under the hood; the API above stays C-only.
  • Link as usual with wcc386/wcl386 -bt=linux (or your preferred OW 2.x Linux flow). No glibc “64” wrappers required.

Minimal usage pattern (directory example)

/* 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.


Sent from Yahoo Mail on Samsung Galaxy S24 Ultra / 1 terabyte / artificial intelligence

S.E. Mitchell

unread,
Aug 23, 2025, 7:56:43 AMAug 23
to sem...@googlegroups.com
Very interesting!
I will definitely take a look at this!

On Sat, Aug 23, 2025 at 7:43 AM 'knud van eeden' via SemWare TSE Pro
> To view this discussion visit https://groups.google.com/d/msgid/semware/1379359118.529644.1755949379210%40mail.yahoo.com.

S.E. Mitchell

unread,
Aug 23, 2025, 8:30:50 AMAug 23
to sem...@googlegroups.com
Well, I could not get anything to work :(

Can you provide a Watcom for Linux version of the following, that
compiles and runs, and shows files on a Windows partition?

#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;
}

Thank you!

knud van eeden

unread,
Aug 23, 2025, 9:22:28 AMAug 23
to sem...@googlegroups.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).

Build (either way works)

  • With wcl386:
wcl386 -bt=linux -fe=ow_ls64 ow_ls64.c
  • With owcc:
owcc -blinux -o ow_ls64 ow_ls64.c

Run

./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

  • This doesn’t depend on dirent.h or readdir*, so it’s immune to the _FILE_OFFSET_BITS=64 roadblock in OpenWatcom.
  • It works on any mounted path—e.g. /mnt/c, /media/<you>/Windows, /run/media/...—as long as you have read perms.
  • If you get open: Permission denied on a Windows mount, try the path with sufficient permissions or remount with proper options.
Sent from Yahoo Mail on Samsung Galaxy S24 Ultra / 1 terabyte / artificial intelligence

Tom Collins

unread,
Aug 23, 2025, 9:25:55 AMAug 23
to sem...@googlegroups.com

knud van eeden

unread,
Aug 23, 2025, 9:32:00 AMAug 23
to sem...@googlegroups.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:

  1. opendir and readdir in OW’s libc are 32-bit only and don’t interoperate correctly on modern 64-bit Linux.
  2. Watcom doesn’t support _FILE_OFFSET_BITS=64, so readdir64 isn’t usable.
  3. Your Windows partition (mounted via /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:

  1. 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>.

  2. 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?

Sent from Yahoo Mail on Samsung Galaxy S24 Ultra / 1 terabyte / artificial intelligence

knud van eeden

unread,
Aug 23, 2025, 9:34:36 AMAug 23
to sem...@googlegroups.com
> Option 1

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.

1) Header: 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 */

2) Implementation: 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;
}

3) Your program (unchanged logic): 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;
}

4) Build instructions (OpenWatcom for Linux, 32-bit target)

Using wcl386:

wcl386 -bt=linux -fe=lsdir main.c ow_dirent.c

Using owcc:

owcc -blinux -o lsdir main.c ow_dirent.c

5) Run examples

./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.

Sent from Yahoo Mail on Samsung Galaxy S24 Ultra / 1 terabyte / artificial intelligence

On Sat, Aug 23, 2025 at 14:22, knud van eeden

S.E. Mitchell

unread,
Aug 23, 2025, 10:03:03 AMAug 23
to sem...@googlegroups.com
I could not get this to compile. I get:
Open Watcom C/C++ x86 32-bit Compile and Link Utility
Version 2.0 beta Aug 2 2025 02:48:51 (32-bit)
Copyright (c) 2002-2025 The Open Watcom Contributors. All Rights Reserved.
Portions Copyright (c) 1988-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See https://github.com/open-watcom/open-watcom-v2#readme for details.
wcc386 wc-dir.c
Open Watcom C x86 32-bit Optimizing Compiler
Version 2.0 beta Aug 2 2025 02:45:31 (32-bit)
Copyright (c) 2002-2025 The Open Watcom Contributors. All Rights Reserved.
Portions Copyright (c) 1984-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See https://github.com/open-watcom/open-watcom-v2#readme for details.
wc-dir.c(96): Error! E1077: Missing '}'
wc-dir.c(96): Warning! W107: Missing return value for function 'main'
wc-dir.c(96): Error! E1011: Symbol 'argv' has not been declared
wc-dir.c(96): Error! E1045: Subscript on non-array
wc-dir.c(96): Warning! W101: Parameter 2: Non-portable pointer conversion
wc-dir.c(96): Note! N2003: source conversion type is 'int'
wc-dir.c(96): Note! N2004: target conversion type is 'char const *'
wc-dir.c(96): Error! E1151: Parameter count does not agree with
previous definition
wc-dir.c(96): Note! N2002: 'ow_open' defined in: wc-dir.c(65)
wc-dir.c(96): Error! E1054: Expression must be constant
wc-dir.c(97): Error! E1099: Statement must be inside function.
Probable cause: missing {
wc-dir.c: 152 lines, included 1444, 2 warnings, 6 errors
Error: Compiler returned a bad status compiling 'wc-dir.c'

The offending line is:
int fd = ow_open(argv[1], O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
This is on Linux, using Watcom for Linux.
Any ideas?

On Sat, Aug 23, 2025 at 9:22 AM 'knud van eeden' via SemWare TSE Pro
> To view this discussion visit https://groups.google.com/d/msgid/semware/1408397510.559197.1755955338463%40mail.yahoo.com.

S.E. Mitchell

unread,
Aug 23, 2025, 10:08:50 AMAug 23
to sem...@googlegroups.com
I get a similar error compiling this one:
Open Watcom C x86 32-bit Optimizing Compiler
Version 2.0 beta Aug 2 2025 02:45:31 (32-bit)
Copyright (c) 2002-2025 The Open Watcom Contributors. All Rights Reserved.
Portions Copyright (c) 1984-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See https://github.com/open-watcom/open-watcom-v2#readme for details.
ow_dirent.c(102): Error! E1077: Missing '}'
ow_dirent.c(102): Warning! W107: Missing return value for function 'opendir'
ow_dirent.c(102): Error! E1011: Symbol 'path' has not been declared
ow_dirent.c(102): Warning! W102: Parameter 1: Type mismatch (warning)
ow_dirent.c(102): Note! N2003: source conversion type is 'int'
ow_dirent.c(102): Note! N2004: target conversion type is 'char const *'
ow_dirent.c(102): Error! E1054: Expression must be constant
ow_dirent.c(103): Error! E1099: Statement must be inside function.
Probable cause: missing {
ow_dirent.c(164): Error! E1077: Missing '}'
ow_dirent.c(164): Error! E1077: Missing '}'
ow_dirent.c(164): Error! E1011: Symbol 'd' has not been declared
ow_dirent.c(164): Error! E1033: Expression for '->' must be 'pointer
to struct or union'
ow_dirent.c(164): Error! E1033: Expression for '->' must be 'pointer
to struct or union'
ow_dirent.c(165): Error! E1033: Expression for '->' must be 'pointer
to struct or union'
ow_dirent.c(165): Error! E1033: Expression for '->' must be 'pointer
to struct or union'
ow_dirent.c(167): Error! E1099: Statement must be inside function.
Probable cause: missing {
ow_dirent.c(201): Error! E1061: Expecting data or function
declaration, but found '}'
ow_dirent.c(203): Error! E1026: Invalid declarator
ow_dirent.c(203): Error! E1009: Expecting ';' but found 'int'
ow_dirent.c(206): Error! E1077: Missing '}'
ow_dirent.c(206): Warning! W107: Missing return value for function 'closedir'
ow_dirent.c(207): Error! E1099: Statement must be inside function.
Probable cause: missing {
ow_dirent.c: 215 lines, included 1954, 3 warnings, 17 errors

This is the offending line:
int fd = ow_open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);

So same line as the single file example.

I think we're close! Maybe :) I hope!

S.E. Mitchell

unread,
Aug 23, 2025, 10:26:53 AMAug 23
to sem...@googlegroups.com
Here is a minimal example that does not compile:
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>

typedef unsigned long u32;
typedef long s32;
typedef unsigned long long u64;
typedef long long s64;

#define __NR_open 5

#ifndef O_CLOEXEC
#define O_CLOEXEC 02000000
#endif

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]

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;
}

/* Open directory as a plain fd (no libc DIR* needed) */
int fd = ow_open(argv[1], O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
// THIS IS LINE 42
if (fd < 0) {
perror("open");
return 1;
}

return 0;
}

I get:
$ wcl386 -q min_err.c
min_err.c(42): Error! E1077: Missing '}'
min_err.c(42): Warning! W107: Missing return value for function 'main'
min_err.c(42): Error! E1011: Symbol 'argv' has not been declared
min_err.c(42): Error! E1045: Subscript on non-array
min_err.c(42): Warning! W101: Parameter 2: Non-portable pointer conversion
min_err.c(42): Note! N2003: source conversion type is 'int'
min_err.c(42): Note! N2004: target conversion type is 'char const *'
min_err.c(42): Error! E1151: Parameter count does not agree with
previous definition
min_err.c(42): Note! N2002: 'ow_open' defined in: min_err.c(31)
min_err.c(42): Error! E1054: Expression must be constant
min_err.c(43): Error! E1099: Statement must be inside function.
Probable cause: missing {
Error: Compiler returned a bad status compiling 'min_err.c'

As before, this line:
int fd = ow_open(argv[1], O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
// THIS IS LINE 42
--

knud van eeden

unread,
Aug 23, 2025, 10:35:03 AMAug 23
to sem...@googlegroups.com

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:

  • compiles with OpenWatcom 2.0 (Linux, 32-bit),
  • does not include <dirent.h>,
  • uses raw Linux syscalls (open, getdents64, close) with Watcom #pragma aux,
  • lists directories (incl. Windows mounts like /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.

Build

wcl386 -bt=linux -fe=wc-dir wc-dir.c

or

owcc -blinux -o wc-dir wc-dir.c

Run

./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;
}

If you still see parser errors

  1. 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.

  2. Braces: Ensure there’s no extra stray } added by your editor. The file above has balanced braces.

  3. Tooling: Prefer wcl386 -bt=linux (it sets the right C dialect/defines automatically). If you use wcc386 directly, link with wlink appropriately.

  4. 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.


Sent from Yahoo Mail on Samsung Galaxy S24 Ultra / 1 terabyte / artificial intelligence

S.E. Mitchell

unread,
Aug 23, 2025, 10:42:15 AMAug 23
to sem...@googlegroups.com
Did you try my minimal example?

Anyway, I tried this one, also using tr command, and still got the
same error, at the same line:

wc-dir-clean.c(83): Error! E1077: Missing '}'
wc-dir-clean.c(83): Warning! W107: Missing return value for function 'main'
wc-dir-clean.c(83): Error! E1011: Symbol 'argv' has not been declared
wc-dir-clean.c(83): Error! E1045: Subscript on non-array
wc-dir-clean.c(83): Warning! W101: Parameter 2: Non-portable pointer conversion
wc-dir-clean.c(83): Note! N2003: source conversion type is 'int'
wc-dir-clean.c(83): Note! N2004: target conversion type is 'char const *'
wc-dir-clean.c(83): Error! E1151: Parameter count does not agree with
previous definition
wc-dir-clean.c(83): Note! N2002: 'ow_open' defined in: wc-dir-clean.c(47)
wc-dir-clean.c(83): Error! E1054: Expression must be constant
wc-dir-clean.c(84): Error! E1099: Statement must be inside function.
Probable cause: missing {
Error: Compiler returned a bad status compiling 'wc-dir-clean.c'

On Sat, Aug 23, 2025 at 10:35 AM 'knud van eeden' via SemWare TSE Pro
> To view this discussion visit https://groups.google.com/d/msgid/semware/711237723.569747.1755959694631%40mail.yahoo.com.

S.E. Mitchell

unread,
Aug 23, 2025, 10:49:28 AMAug 23
to sem...@googlegroups.com
For future correspondence, please use the minimal example I posted.
If we can get that one working, it should be easy to retrofit it into
the larger code.
Thanks!

knud van eeden

unread,
Aug 23, 2025, 10:59:38 AMAug 23
to sem...@googlegroups.com


You’ve tripped two Watcom gotchas:

  1. your #pragma aux is split across multiple lines with \ — Watcom doesn’t support line-continuations inside #pragma aux. Keep it on one line.

  2. 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.

Build

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;
}

If your existing file still errors

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.


Sent from Yahoo Mail on Samsung Galaxy S24 Ultra / 1 terabyte / artificial intelligence

S.E. Mitchell

unread,
Aug 23, 2025, 11:13:41 AMAug 23
to sem...@googlegroups.com
I took the min_ok.c
I get the same errors.
I ran the tr script, producing min_clean.
I compiled it - same errors.

Instead of asking the !@#$%^&* AI, can you try compiling it?
I mean, this is the 3rd or fourth time that it has given the same message.
I don't know where it is getting the junk about embedded \n characters.
I looked in binary mode, and it looks fine to me.
I think it requires human intervention :)


On Sat, Aug 23, 2025 at 10:59 AM 'knud van eeden' via SemWare TSE Pro
> To view this discussion visit https://groups.google.com/d/msgid/semware/328192319.577933.1755961169079%40mail.yahoo.com.

S.E. Mitchell

unread,
Aug 23, 2025, 11:21:09 AMAug 23
to sem...@googlegroups.com
Stop the presses!
Found it!
I asked a human :)
Has nothing to do with \n characters or anything like that.
By default Watcom is C89, and the code is C99.
So I added: -za99
And now it compiles!
Very cool!
Now to see if I can incorporate this into the editor.

Thanks so much!

knud van eeden

unread,
Aug 23, 2025, 11:52:34 AMAug 23
to sem...@googlegroups.com
Tada :-) :-)


Sent from Yahoo Mail on Samsung Galaxy S24 Ultra / 1 terabyte / artificial intelligence

S.E. Mitchell

unread,
Aug 23, 2025, 11:53:51 AMAug 23
to sem...@googlegroups.com
Got it grafted into the editor, and so far it works!
Thanks again!
--

On Sat, Aug 23, 2025 at 11:52 AM 'knud van eeden' via SemWare TSE Pro
> To view this discussion visit https://groups.google.com/d/msgid/semware/1192737087.583073.1755964342727%40mail.yahoo.com.

knud van eeden

unread,
Aug 23, 2025, 12:13:21 PMAug 23
to sem...@googlegroups.com
> C89 C99

Ahhh, that makes perfect sense now! 🙂

OpenWatcom defaults to C89 / ANSI mode, so without -za99 it doesn't recognize:

  • mixed declarations and code (like int fd = …; after statements)
  • // single-line comments
  • sometimes even trailing commas in enums/structs

That'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. 🎉


Takeaways for OpenWatcom + Linux syscalls

  • Always add -bt=linux -za99 for modern-style code:
    wcl386 -bt=linux -za99 -fe=myprog myprog.c
    
  • Keep #pragma aux on a single line.
  • No need to rewrite code to C89 style now — C99 mode lets you keep inline variable declarations, // comments, etc.



Sent from Yahoo Mail on Samsung Galaxy S24 Ultra / 1 terabyte / artificial intelligence

Tom Collins

unread,
Aug 25, 2025, 2:50:15 PMAug 25
to sem...@googlegroups.com
I can report that the updated .7 release of TSE works with my Rocky Linux install.  Awesome sauce!

Carlo Hogeveen

unread,
Aug 26, 2025, 3:19:27 AMAug 26
to sem...@googlegroups.com

@Tom Collins,

You wrote:
> I can report that the updated .7 release of TSE works with my Rocky Linux install. Awesome sauce!

Knud’s theoretical example in another thread might be indicative of an exception of one TSE command still not working on too-pure-64-bit systems.

If you have the time and would be so kind, could you go to your TSE directory and test if the following command works from there?

./sc32 'mac/*.s'

Thanks,
Carlo



Tom Collins

unread,
Aug 26, 2025, 9:26:33 AMAug 26
to sem...@googlegroups.com
Partially works.  Starts compiling files, and then hangs after 3-5 macros:

tcollins@pidgey:/tmp/tse$ Compiling............................................
Writing output to file '/tmp/tse/mac/function-list-c.mac'
Compiling............................................................................
Writing output to file '/tmp/tse/mac/cmpbuffers.mac'
Compiling...................................................................
Writing output to file '/tmp/tse/mac/where.mac'
Compiling..........................................................
Writing output to file '/tmp/tse/mac/setpalette.mac'




--

---
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.

Carlo Hogeveen

unread,
Aug 26, 2025, 9:39:29 AMAug 26
to sem...@googlegroups.com

Tom,

This looks more like a variant of David Boyd's problem, in which case it will be automagically resolved when Sammy reverts the bulk-compile change he did in 4.50.7.
Which might be additional good news for its 64-bit change.
Thank you for testing this,

Carlo




From: sem...@googlegroups.com <sem...@googlegroups.com> On Behalf Of Tom Collins
Sent: Tuesday, August 26, 2025 3:26 PM
To: sem...@googlegroups.com
Subject: Re: [TSE] Linux open error on directory

Partially works. Starts compiling files, and then hangs after 3-5 macros:

tcollins@pidgey:/tmp/tse$ Compiling............................................
Writing output to file '/tmp/tse/mac/function-list-c.mac'
Compiling............................................................................
Writing output to file '/tmp/tse/mac/cmpbuffers.mac'
Compiling...................................................................
Writing output to file '/tmp/tse/mac/where.mac'
Compiling..........................................................
Writing output to file '/tmp/tse/mac/setpalette.mac'



On Tue, Aug 26, 2025 at 3:19 AM Carlo Hogeveen <mailto:t...@ecarlo.nl> wrote:

@Tom Collins,

You wrote:
> I can report that the updated .7 release of TSE works with my Rocky Linux install. Awesome sauce!

Knud’s theoretical example in another thread might be indicative of an exception of one TSE command still not working on too-pure-64-bit systems.

If you have the time and would be so kind, could you go to your TSE directory and test if the following command works from there?

./sc32 'mac/*.s'

Thanks,
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 mailto:semware%2Bunsu...@googlegroups.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 mailto:semware+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/semware/CAODxuOb_hqZB19dFJaqvZfwp%3DNkPa-EDdNTQxFZPQ%3DUaxxK8yw%40mail.gmail.com?utm_medium=email&utm_source=footer.

knud van eeden

unread,
Aug 26, 2025, 10:04:56 AMAug 26
to sem...@googlegroups.com
1. Not reproducible.

2. TSE for Linux non-WSL version 4.50.7

3. Linux Rocky: version 9.5

4. Using in a terminal after changing to the tse directory:

./s32 'mac/*.s'

and

./sc32 'mac/*.si'

5. and after that checking that tse mac directory for the date / time
of those compiled .mac files, it shows that those (all) .mac files
are all compiled today, as expected.

6. Maybe when it appears to hang, press <ENTER> to continue.

7. Otherwise try with another version of TSE to see if it still reproduces.

8. Otherwise it is a Rocky 8.* issue maybe, so local issue.

9. Run it on the TSE mac directory and also on your own directory.

10. If it does not work only on your own directory then probably issue with 
a particular .s or .si file, so  troubleshoot with that .s file further locally.


with friendly greetings
Knud van Eeden

Inline image


Inline image

TSE 4.50 official release

Inline image









Reply all
Reply to author
Forward
0 new messages