[PATCH] newlib - m68k-atari-elf: Correct handling of CRLF in stdio.
<[email protected]> Thu, 14 May 2026 11:08:52 +0200
| Newsgroups | gmane.comp.lib.newlib |
|---|---|
| Message-ID | <1778749732533.7.70230@webmail-backend-production-7d55ff49-ggsdp> |
Atari computers uses CRLF as line endings the same way Windows do.
This patch enables text/binary mode for files and stdin/stdout/stderr streams.
It should work the same way as Cygwin do.
This patch have been tested successfully by the Atari community since beginning of March 2026.
Reconfigure is needed.
---
libgloss/m68k/Makefile.in | 2 +-
libgloss/m68k/atari/atari-open.c | 37 +++++----
libgloss/m68k/atari/atari-setmode.c | 74 ++++++++++++++++++
libgloss/m68k/atari/atari-tos.ld | 2 +-
libgloss/m68k/atari/atari-tos.specs | 3 +
libgloss/m68k/atari/atari-write.c | 61 +--------------
newlib/configure.host | 6 +-
newlib/libc/acinclude.m4 | 1 +
newlib/libc/stdio/flags.c | 2 +-
newlib/libc/stdio/stdio.c | 4 +
newlib/libc/sys/Makefile.inc | 3 +
newlib/libc/sys/atari/Makefile.inc | 1 +
newlib/libc/sys/atari/filemode.c | 94 +++++++++++++++++++++++
newlib/libc/sys/atari/include/io.h | 30 ++++++++
newlib/libc/sys/atari/include/sys/fcntl.h | 11 +++
newlib/libc/sys/atari/include/sys/stdio.h | 33 ++++++++
16 files changed, 283 insertions(+), 81 deletions(-)
create mode 100644 libgloss/m68k/atari/atari-setmode.c
create mode 100644 newlib/libc/sys/atari/Makefile.inc
create mode 100644 newlib/libc/sys/atari/filemode.c
create mode 100644 newlib/libc/sys/atari/include/io.h
create mode 100644 newlib/libc/sys/atari/include/sys/fcntl.h
create mode 100644 newlib/libc/sys/atari/include/sys/stdio.h
diff --git a/libgloss/m68k/Makefile.in b/libgloss/m68k/Makefile.in
index b6d1868bc..43d716a2f 100644
--- a/libgloss/m68k/Makefile.in
+++ b/libgloss/m68k/Makefile.in
@@ -216,7 +216,7 @@ ATARI_SRC_LIBGLOSS := atari-environ.c atari-execve.c atari-link.c atari-times.c
atari-sbrk.c atari-stat.c atari-unlink.c atari-write.c \
atari-chown.c atari-fork.c atari-getentropy.c atari-gettod.c atari-readlink.c \
atari-symlink.c atari-chdir.c atari-getcwd.c atari-mkdir.c atari-rmdir.c \
- atari-rename.c
+ atari-rename.c atari-setmode.c
ATARI_OBJS_LIBGLOSS := $(foreach source,$(ATARI_SRC_LIBGLOSS),$(basename $(source)).o)
diff --git a/libgloss/m68k/atari/atari-open.c b/libgloss/m68k/atari/atari-open.c
index c61071881..6c960b6a6 100644
--- a/libgloss/m68k/atari/atari-open.c
+++ b/libgloss/m68k/atari/atari-open.c
@@ -5,27 +5,15 @@
#include <unistd.h>
#include <_ansi.h>
+#include <fcntl.h>
+#include <io.h>
#include "atari-gem_errno.h"
#include "atari-traps.h"
-// I really don't like having these defines here,
-// fcntl.h where they are, also defines a function for "open",
-// that is different from the one libgloss is supposed to provide...
-#ifndef O_CREAT
-#define O_CREAT 0x0200
-#endif // O_CREAT
-#ifndef O_APPEND
-#define O_APPEND 0x0008
-#endif // O_APPEND
-#ifndef O_EXCL
-#define O_EXCL 0x0800
-#endif // O_EXCL
-#ifndef O_TRUNC
-#define O_TRUNC 0x0400
-#endif // O_TRUNC
+extern int atari_get_fmode(int* mode);
+extern int atari_setmode(short f, int mode);
-// mode is ignored. Those kind of settings is not supported by the st.
-int open(const char *buf, int flags, int mode)
+int open(const char *buf, int flags, ...)
{
int bios_handle = -1;
unsigned short bios_mode = (unsigned short)(flags & 0x3); // bits 0-1 the same for st and linux.
@@ -71,6 +59,21 @@ int open(const char *buf, int flags, int mode)
gem_error_to_errno(bios_handle);
bios_handle = -1;
}
+ else
+ {
+ int fmode = flags & (O_TEXT | O_BINARY);
+ if (fmode == 0)
+ {
+ // Initializes the correct default mode.
+ atari_get_fmode(&fmode);
+ }
+ if (atari_setmode((short)bios_handle, fmode) == -1)
+ {
+ // Close file. errno is alredy set.
+ trap1_3e((unsigned short)bios_handle);
+ return -1;
+ }
+ }
/*
If bios_handle is positive, then the low word is the gemdos handle, and the high word is zero.
*/
diff --git a/libgloss/m68k/atari/atari-setmode.c b/libgloss/m68k/atari/atari-setmode.c
new file mode 100644
index 000000000..89fe7511f
--- /dev/null
+++ b/libgloss/m68k/atari/atari-setmode.c
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2026 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+#include <unistd.h>
+#include <_ansi.h>
+#include <fcntl.h>
+#include <io.h>
+#include <errno.h>
+
+/*
+ EmuTOS allows for a maximum of 75 open files, and ordinary TOS even less.
+ But MultiTOS and MagiC allows for much more, so to be safe we allow for even more.
+*/
+#define MAX_FILE_DESCRIPTORS 256
+#define FILE_MODE_SHIFT 16
+
+static int _fmode = O_TEXT;
+
+unsigned char file_descriptor_modes[MAX_FILE_DESCRIPTORS] = {0};
+
+int atari_set_fmode(int mode)
+{
+ _fmode = mode;
+ return 0;
+}
+
+int atari_get_fmode(int* mode)
+{
+ *mode = _fmode;
+ return 0;
+}
+
+int atari_getmode(short fd)
+{
+ if (fd < 0 || fd >= MAX_FILE_DESCRIPTORS)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ int mode = ((int)file_descriptor_modes[fd]) << FILE_MODE_SHIFT;
+ if (mode == 0 && fd < 2)
+ {
+ /*
+ stdin, stdout and stderr isn't opened and assumed to exist.
+ So the open function never gets the chance to set default mode for them.
+ That's why we do this check and return O_TEXT if nothing else is set.
+ */
+ mode = O_TEXT;
+ }
+ return mode;
+}
+
+int atari_setmode(short fd, int mode)
+{
+ int oldMode = atari_getmode(fd);
+ if (oldMode != -1)
+ {
+ // We cannot get here without having enough entries in the array.
+ file_descriptor_modes[fd] = (unsigned char)((mode & (O_TEXT | O_BINARY)) >> FILE_MODE_SHIFT);
+ }
+ return oldMode;
+}
+
+int atari_istext_for_stdio (short fd)
+{
+ int mode = atari_getmode(fd);
+ if (mode != -1)
+ {
+ // Treat anything else than binary mode as text mode.
+ return (mode & O_BINARY) == 0 ? 1 : 0;
+ }
+ return 1;
+}
diff --git a/libgloss/m68k/atari/atari-tos.ld b/libgloss/m68k/atari/atari-tos.ld
index 6bbecc261..a2f0b7625 100644
--- a/libgloss/m68k/atari/atari-tos.ld
+++ b/libgloss/m68k/atari/atari-tos.ld
@@ -85,7 +85,7 @@ SECTIONS
LONG(__BSS_SEGMENT__ - __DATA_SEGMENT__); /* Length of the DATA segment */
LONG(__BSS_SEGMENT_END - __BSS_SEGMENT__); /* Length of the BSS segment */
LONG(0); /* Length of the symbol table */
- LONG(0); /* Reserved, should be 0 */
+ LONG(0x68e1f001); /* __M68K_ATARI_ELF__ signature */
LONG(0); /* Program flags */
SHORT(0); /* 0 = Relocation info present */
}
diff --git a/libgloss/m68k/atari/atari-tos.specs b/libgloss/m68k/atari/atari-tos.specs
index 02037132c..232afc3e8 100644
--- a/libgloss/m68k/atari/atari-tos.specs
+++ b/libgloss/m68k/atari/atari-tos.specs
@@ -8,3 +8,6 @@
*lib:
+ -latari-tos
+
+*cpp:
++ -D__M68K_ATARI_ELF__ -D__atari_os__
diff --git a/libgloss/m68k/atari/atari-write.c b/libgloss/m68k/atari/atari-write.c
index 9d6e59ed8..eba45038b 100644
--- a/libgloss/m68k/atari/atari-write.c
+++ b/libgloss/m68k/atari/atari-write.c
@@ -8,30 +8,6 @@
#include "atari-gem_errno.h"
#include "atari-traps.h"
-const char* lineEnding = "\r\n";
-
-int writeUntilDoneOrError(int fd, size_t len, const char* buf)
-{
- size_t written = 0;
- if (len == 0)
- {
- return 0;
- }
- do
- {
- int n = trap1_40((unsigned short)fd, len, buf);
- if (n < 0)
- {
- gem_error_to_errno(n);
- return -1;
- }
- written += n;
- buf += n;
- } while (written < len);
- return 0;
-}
-
-
_READ_WRITE_RETURN_TYPE write(int fd, const void *buf, size_t nbytes)
{
int numWritten = GEM_EIHNDL;
@@ -41,42 +17,7 @@ _READ_WRITE_RETURN_TYPE write(int fd, const void *buf, size_t nbytes)
{
fd = GSH_CONOUT; // Use console out for stderr.
}
- if (fd == GSH_CONOUT)
- {
- // When we write to stdout on Atari, we must add a \r after \n to
- // get the correct C output behaviour.
- const char* stream = (const char*)buf;
- size_t lastWrite = 0;
- for (size_t i = 0; i < nbytes; ++i)
- {
- if (stream[i] == '\n')
- {
- int len = i - lastWrite; // length up to but not including \n
- if (writeUntilDoneOrError(fd, len, stream + lastWrite) < 0)
- {
- return -1;
- }
- if (writeUntilDoneOrError(fd, 2, lineEnding) < 0)
- {
- return -1;
- }
- lastWrite = i + 1; // Include the \n
- }
- }
- if (lastWrite < nbytes)
- {
- int len = nbytes - lastWrite;
- if (writeUntilDoneOrError(fd, len, stream + lastWrite) < 0)
- {
- return -1;
- }
- }
- numWritten = nbytes;
- }
- else
- {
- numWritten = trap1_40((unsigned short)fd, nbytes, buf);
- }
+ numWritten = trap1_40((unsigned short)fd, nbytes, buf);
}
if (numWritten < 0)
{
diff --git a/newlib/configure.host b/newlib/configure.host
index 960cca215..3c30bf8a7 100644
--- a/newlib/configure.host
+++ b/newlib/configure.host
@@ -527,6 +527,10 @@ case "${host}" in
m68hc11-*-*|m6811-*-*|m6812-*-*|m68hc12-*-*)
;;
+ m68k-atari-elf)
+ sys_dir=atari
+ have_crt0="no"
+ ;;
m68k-sun-sunos*)
unix_dir=unix
;;
@@ -769,7 +773,7 @@ newlib_cflags="${newlib_cflags} -DCLOCK_PROVIDED -DMALLOC_PROVIDED -DEXIT_PROVID
syscall_dir=
;;
m68k-atari-elf)
- newlib_cflags="${newlib_cflags} -DHAVE_RENAME -DMISSING_SYSCALL_NAMES"
+ newlib_cflags="${newlib_cflags} -D__M68K_ATARI_ELF__ -DHAVE_RENAME -DMISSING_SYSCALL_NAMES"
syscall_dir=
;;
mcore-*-*)
diff --git a/newlib/libc/acinclude.m4 b/newlib/libc/acinclude.m4
index a1040386b..c70a4aebb 100644
--- a/newlib/libc/acinclude.m4
+++ b/newlib/libc/acinclude.m4
@@ -13,6 +13,7 @@ AM_CONDITIONAL(HAVE_UNIX_DIR, test x${unix_dir} != x)
dnl We always recur into sys and machine, and let them decide what to do.
m4_foreach_w([SYS_DIR], [
a29khif amdgcn arm
+ atari
d10v
epiphany
h8300hms h8500hms
diff --git a/newlib/libc/stdio/flags.c b/newlib/libc/stdio/flags.c
index d686fa046..7f3b15e2f 100644
--- a/newlib/libc/stdio/flags.c
+++ b/newlib/libc/stdio/flags.c
@@ -72,7 +72,7 @@ __sflags (struct _reent *ptr,
m |= O_BINARY;
#endif
break;
-#ifdef __CYGWIN__
+#if defined (__CYGWIN__) || defined (__M68K_ATARI_ELF__)
case 't':
m |= O_TEXT;
break;
diff --git a/newlib/libc/stdio/stdio.c b/newlib/libc/stdio/stdio.c
index 6347949db..e3abdb0ce 100644
--- a/newlib/libc/stdio/stdio.c
+++ b/newlib/libc/stdio/stdio.c
@@ -138,6 +138,10 @@ __stextmode (int fd)
extern int _cygwin_istext_for_stdio (int);
return _cygwin_istext_for_stdio (fd);
#else
+ #ifdef __M68K_ATARI_ELF__
+ extern int atari_istext_for_stdio (int);
+ return atari_istext_for_stdio (fd);
+ #endif
return 0;
#endif
}
diff --git a/newlib/libc/sys/Makefile.inc b/newlib/libc/sys/Makefile.inc
index 9f8758934..97ce59a36 100644
--- a/newlib/libc/sys/Makefile.inc
+++ b/newlib/libc/sys/Makefile.inc
@@ -7,6 +7,9 @@ endif
if HAVE_LIBC_SYS_ARM_DIR
include %D%/arm/Makefile.inc
endif
+if HAVE_LIBC_SYS_ATARI_DIR
+include %D%/atari/Makefile.inc
+endif
if HAVE_LIBC_SYS_D10V_DIR
include %D%/d10v/Makefile.inc
endif
diff --git a/newlib/libc/sys/atari/Makefile.inc b/newlib/libc/sys/atari/Makefile.inc
new file mode 100644
index 000000000..5ca8259ee
--- /dev/null
+++ b/newlib/libc/sys/atari/Makefile.inc
@@ -0,0 +1 @@
+libc_a_SOURCES += %D%/filemode.c
diff --git a/newlib/libc/sys/atari/filemode.c b/newlib/libc/sys/atari/filemode.c
new file mode 100644
index 000000000..ea8ff7455
--- /dev/null
+++ b/newlib/libc/sys/atari/filemode.c
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 2026 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#include <_ansi.h>
+#include <sys/reent.h>
+#include <stdio.h>
+#include <sys/fcntl.h>
+#include <sys/lock.h>
+#include <errno.h>
+#include "../../stdio/local.h"
+
+
+/*
+ Thread safety in case support for MultiTOS or MagiC is added in the future.
+*/
+#ifndef __SINGLE_THREAD__
+__LOCK_INIT(static, __setmode_mutex);
+#endif
+
+short setmode_fd;
+FILE* setmode_fp;
+
+int setmode_helper(struct _reent *ptr, FILE *f)
+{
+ /*
+ We call __sfileno instead of fileno, as the latter tries to lock the filepointer.
+ And the filepointer is already locked if we came from fopen etc.
+ */
+ if (setmode_fd == __sfileno(f))
+ {
+ setmode_fp = f;
+ }
+ return 0;
+}
+
+FILE* GetFilePointer(short fd)
+{
+#ifndef __SINGLE_THREAD__
+ __lock_acquire(__setmode_mutex);
+#endif
+ setmode_fd = fd;
+ setmode_fp = 0;
+ _fwalk_sglue (_GLOBAL_REENT, setmode_helper, &__sglue);
+ FILE* fp = setmode_fp;
+#ifndef __SINGLE_THREAD__
+ __lock_release(__setmode_mutex);
+#endif
+ return fp;
+}
+
+extern int atari_setmode(short f, int mode);
+extern int atari_istext_for_stdio(short fd);
+
+int _setmode (short fd, int mode)
+{
+ if (fd < 0)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+/*
+ We need to make sure that stdio is set up, as this function most likely will be called before any file operations.
+ The second parameter in the CHECK_INIT should be a file pointer, but paradoxically enough we cannot have one
+ before setting up stdio.
+ This is not a problem however as the file pointer never is used by the macro, so we set it to NULL.
+*/
+ CHECK_INIT(_REENT, NULL);
+
+ FILE* fp = GetFilePointer(fd);
+ if (fp == 0)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+ int oldmode = atari_setmode(fd, mode);
+
+ /*
+ Set or clear __SCLE for correct CRLF handling.
+ */
+ if (atari_istext_for_stdio(fd) != 0)
+ {
+ fp->_flags |= __SCLE;
+ }
+ else
+ {
+ fp->_flags &= ~__SCLE;
+ }
+
+ return oldmode;
+}
diff --git a/newlib/libc/sys/atari/include/io.h b/newlib/libc/sys/atari/include/io.h
new file mode 100644
index 000000000..027ff1626
--- /dev/null
+++ b/newlib/libc/sys/atari/include/io.h
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 2026 Mikael Hildenborg
+ SPDX-License-Identifier: BSD-2-Clause
+*/
+
+#ifndef _IO_H_
+#define _IO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ fmode functions is in libgloss atari-setmode.c
+*/
+extern int atari_set_fmode(int mode);
+extern int atari_get_fmode(int* mode);
+extern int _setmode(short f, int mode);
+
+#define _set_fmode(mode) atari_set_fmode((mode))
+#define _get_fmode(mode) atari_get_fmode((mode))
+#define set_fmode(mode) atari_set_fmode((mode))
+#define get_fmode(mode) atari_get_fmode((mode))
+#define setmode(f, mode) _setmode((f), (mode))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/newlib/libc/sys/atari/include/sys/fcntl.h b/newlib/libc/sys/atari/include/sys/fcntl.h
new file mode 100644
index 000000000..70c635c72
--- /dev/null
+++ b/newlib/libc/sys/atari/include/sys/fcntl.h
@@ -0,0 +1,11 @@
+#ifndef _SYS_FCNTL_H_
+#define _SYS_FCNTL_H_
+
+#include <sys/_default_fcntl.h>
+
+#define _FBINARY 0x10000
+#define _FTEXT 0x20000
+#define O_BINARY _FBINARY
+#define O_TEXT _FTEXT
+
+#endif /* _SYS_FCNTL_H_ */
diff --git a/newlib/libc/sys/atari/include/sys/stdio.h b/newlib/libc/sys/atari/include/sys/stdio.h
new file mode 100644
index 000000000..8f3616a8c
--- /dev/null
+++ b/newlib/libc/sys/atari/include/sys/stdio.h
@@ -0,0 +1,33 @@
+/*
+ This file is a copy of libc/include/sys/stdio.h
+ The only difference is the __SCLE define at the bottom.
+*/
+#ifndef _NEWLIB_STDIO_H
+#define _NEWLIB_STDIO_H
+
+#include <sys/lock.h>
+#include <sys/reent.h>
+
+/* Internal locking macros, used to protect stdio functions. In the
+ general case, expand to nothing. Use __SSTR flag in FILE _flags to
+ detect if FILE is private to sprintf/sscanf class of functions; if
+ set then do nothing as lock is not initialised. */
+#if !defined(_flockfile)
+#ifndef __SINGLE_THREAD__
+# define _flockfile(fp) (((fp)->_flags & __SSTR) ? 0 : __lock_acquire_recursive((fp)->_lock))
+#else
+# define _flockfile(fp) ((void) 0)
+#endif
+#endif
+
+#if !defined(_funlockfile)
+#ifndef __SINGLE_THREAD__
+# define _funlockfile(fp) (((fp)->_flags & __SSTR) ? 0 : __lock_release_recursive((fp)->_lock))
+#else
+# define _funlockfile(fp) ((void) 0)
+#endif
+#endif
+
+#define __SCLE 0x4000
+
+#endif /* _NEWLIB_STDIO_H */
--
2.43.0