Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions flang-rt/include/flang-rt/runtime/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,9 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from,
bool toIsContiguous, bool fromIsContiguous);
RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from);

// Determines if a character string is null-terminated.
RT_API_ATTRS bool IsNullTerminated(char *str, std::size_t length);

// Ensures that a character string is null-terminated, allocating a /p length +1
// size memory for null-terminator if necessary. Returns the original or a newly
// allocated null-terminated string (responsibility for deallocation is on the
Expand Down
25 changes: 24 additions & 1 deletion flang-rt/lib/runtime/misc-intrinsic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <cstdio>
#include <cstring>

#include "../../include/flang-rt/runtime/descriptor.h"

namespace Fortran::runtime {

static RT_API_ATTRS void TransferImpl(Descriptor &result,
Expand Down Expand Up @@ -60,13 +62,34 @@ void RTDEF(Rename)(const Descriptor &path1, const Descriptor &path2,
const Descriptor *status, const char *sourceFile, int line) {
Terminator terminator{sourceFile, line};
#if !defined(RT_DEVICE_COMPILATION)
// Get the raw strings (null-terminated)
char *pathSrc{EnsureNullTerminated(
path1.OffsetElement(), path1.ElementBytes(), terminator)};
char *pathDst{EnsureNullTerminated(
path2.OffsetElement(), path2.ElementBytes(), terminator)};
char *srcFilePath = pathSrc;
char *dstFilePath = pathDst;

// Trim trailing blanks (if string have not been null-terminated)
if (!IsNullTerminated(path1.OffsetElement(), path1.ElementBytes())) {
auto srcTrimPos{TrimTrailingSpaces(pathSrc, path1.ElementBytes())};
char *srcPathTrim{
static_cast<char *>(alloca((srcTrimPos + 1)))};
std::memcpy(srcPathTrim, pathSrc, srcTrimPos);
srcPathTrim[srcTrimPos] = '\0';
srcFilePath = srcPathTrim;
}
if (!IsNullTerminated(path2.OffsetElement(), path2.ElementBytes())) {
auto dstTrimPos{TrimTrailingSpaces(pathDst, path2.ElementBytes())};
char *dstPathTrim{
static_cast<char *>(alloca((dstTrimPos + 1)))};
std::memcpy(dstPathTrim, pathDst, dstTrimPos);
dstPathTrim[dstTrimPos] = '\0';
dstFilePath = dstPathTrim;
}
Copy link
Contributor

@jeanPerier jeanPerier Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Get the raw strings (null-terminated)
char *pathSrc{EnsureNullTerminated(
path1.OffsetElement(), path1.ElementBytes(), terminator)};
char *pathDst{EnsureNullTerminated(
path2.OffsetElement(), path2.ElementBytes(), terminator)};
char *srcFilePath = pathSrc;
char *dstFilePath = pathDst;
// Trim trailing blanks (if string have not been null-terminated)
if (!IsNullTerminated(path1.OffsetElement(), path1.ElementBytes())) {
auto srcTrimPos{TrimTrailingSpaces(pathSrc, path1.ElementBytes())};
char *srcPathTrim{
static_cast<char *>(alloca((srcTrimPos + 1)))};
std::memcpy(srcPathTrim, pathSrc, srcTrimPos);
srcPathTrim[srcTrimPos] = '\0';
srcFilePath = srcPathTrim;
}
if (!IsNullTerminated(path2.OffsetElement(), path2.ElementBytes())) {
auto dstTrimPos{TrimTrailingSpaces(pathDst, path2.ElementBytes())};
char *dstPathTrim{
static_cast<char *>(alloca((dstTrimPos + 1)))};
std::memcpy(dstPathTrim, pathDst, dstTrimPos);
dstPathTrim[dstTrimPos] = '\0';
dstFilePath = dstPathTrim;
}
auto srcFilePath{SaveDefaultCharacter(path1.OffsetElement(), TrimTrailingSpaces(path1.OffsetElement(), path1.ElementBytes()), terminator)};
auto dstFilePath{SaveDefaultCharacter(path2.OffsetElement(), TrimTrailingSpaces(path2.OffsetElement(), path2.ElementBytes()), terminator)};

(then use srcFilePath.get() and dstFilePath.get() in rename call below).

Unformatted/untested.

You should get the same behavior because CHAR(0) is not a blank, so trimming will have no effect when this is the last character and no special handling for this case is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! That is much better than what I had :-) and it seems to work for what I have tested.


// We simply call rename(2) from POSIX
int result{rename(pathSrc, pathDst)};
int result{rename(srcFilePath, dstFilePath)};
if (status) {
// When an error has happened,
int errorCode{0}; // Assume success
Expand Down
6 changes: 5 additions & 1 deletion flang-rt/lib/runtime/tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,13 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from) {
ShallowCopy(to, from, to.IsContiguous(), from.IsContiguous());
}

RT_API_ATTRS bool IsNullTerminated(char *str, std::size_t length) {
return !(runtime::memchr(str, '\0', length) == nullptr);
}

RT_API_ATTRS char *EnsureNullTerminated(
char *str, std::size_t length, Terminator &terminator) {
if (runtime::memchr(str, '\0', length) == nullptr) {
if (!IsNullTerminated(str, length)) {
char *newCmd{(char *)AllocateMemoryOrCrash(terminator, length + 1)};
runtime::memcpy(newCmd, str, length);
newCmd[length] = '\0';
Expand Down
Loading