newlib - m68k-atari-elf: Correct handling of CRLF in stdio.

Message ID 1778749732533.7.70230@webmail-backend-production-7d55ff49-ggsdp
State New
Headers
Series newlib - m68k-atari-elf: Correct handling of CRLF in stdio. |

Commit Message

micael@hildenborg.com May 14, 2026, 9:08 a.m. UTC
  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

--
2.43.0
  

Comments

micael@hildenborg.com June 13, 2026, 1:18 p.m. UTC | #1
Bump.



Den torsdag 14 maj 2026 kl. 11:08:52 +02:00, skrev <micael@hildenborg.com>:

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

Patch

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