[flang] [clang] [clang-tools-extra] [llvm] [flang] GETLOG runtime and extension implementation: get login username (PR #74628)

Yi Wu via cfe-commits cfe-commits at lists.llvm.org
Fri Dec 8 12:18:12 PST 2023


https://github.com/yi-wu-arm updated https://github.com/llvm/llvm-project/pull/74628

>From 0e98aa7ca15b05b91813eaeeb6ae1305e5f5384d Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Mon, 6 Nov 2023 19:49:13 +0000
Subject: [PATCH 01/26] GETLOG runtime and extension implementation: get login
 username

Get login username, ussage:
CHARACTER(32) :: login
CALL getlog(login)
WRITE(*,*) login
---
 flang/docs/Intrinsics.md                      |  2 +-
 .../Optimizer/Builder/Runtime/RTBuilder.h     |  8 ++++
 flang/include/flang/Runtime/command.h         |  6 +++
 flang/include/flang/Runtime/extensions.h      |  2 +
 flang/runtime/command.cpp                     | 40 +++++++++++++++++++
 flang/runtime/extensions.cpp                  |  6 +++
 flang/unittests/Runtime/CommandTest.cpp       | 15 ++++++-
 7 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index ab0a940e53e553..cfe5dcd141e982 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -751,7 +751,7 @@ This phase currently supports all the intrinsic procedures listed above but the
 | Object characteristic inquiry functions | ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, IS_CONTIGUOUS, PRESENT, RANK, SAME_TYPE, STORAGE_SIZE |
 | Type inquiry intrinsic functions | BIT_SIZE, DIGITS, EPSILON, HUGE, KIND, MAXEXPONENT, MINEXPONENT, NEW_LINE, PRECISION, RADIX, RANGE, TINY|
 | Non-standard intrinsic functions | AND, OR, XOR, SHIFT, ZEXT, IZEXT, COSD, SIND, TAND, ACOSD, ASIND, ATAND, ATAN2D, COMPL, EQV, NEQV, INT8, JINT, JNINT, KNINT, QCMPLX, DREAL, DFLOAT, QEXT, QFLOAT, QREAL, DNUM, NUM, JNUM, KNUM, QNUM, RNUM, RAN, RANF, ILEN, SIZEOF, MCLOCK, SECNDS, COTAN, IBCHNG, ISHA, ISHC, ISHL, IXOR, IARG, IARGC, NARGS, NUMARG, BADDRESS, IADDR, CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, MALLOC |
-| Intrinsic subroutines |MVBITS (elemental), CPU_TIME, DATE_AND_TIME, EVENT_QUERY, EXECUTE_COMMAND_LINE, GET_COMMAND, GET_COMMAND_ARGUMENT, GET_ENVIRONMENT_VARIABLE, MOVE_ALLOC, RANDOM_INIT, RANDOM_NUMBER, RANDOM_SEED, SYSTEM_CLOCK |
+| Intrinsic subroutines |MVBITS (elemental), CPU_TIME, DATE_AND_TIME, EVENT_QUERY, EXECUTE_COMMAND_LINE, GET_COMMAND, GET_COMMAND_ARGUMENT, GET_ENVIRONMENT_VARIABLE, GETLOG, MOVE_ALLOC, RANDOM_INIT, RANDOM_NUMBER, RANDOM_SEED, SYSTEM_CLOCK |
 | Atomic intrinsic subroutines | ATOMIC_ADD |
 | Collective intrinsic subroutines | CO_REDUCE |
 
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
index b2774263e7a31a..830df7ad006b54 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
@@ -62,6 +62,14 @@ using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *);
 /// standard type `i32` when `sizeof(int)` is 4.
 template <typename T>
 static constexpr TypeBuilderFunc getModel();
+
+template <>
+constexpr TypeBuilderFunc getModel<unsigned int>() {
+  return [](mlir::MLIRContext *context) -> mlir::Type {
+    return mlir::IntegerType::get(context, 8 * sizeof(unsigned int));
+  };
+}
+
 template <>
 constexpr TypeBuilderFunc getModel<short int>() {
   return [](mlir::MLIRContext *context) -> mlir::Type {
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index ec628939054547..1c212ef61697cd 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -47,6 +47,12 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
     bool trim_name = true, const Descriptor *errmsg = nullptr,
     const char *sourceFile = nullptr, int line = 0);
 }
+
+// Try to get the name of current user
+// Returns a STATUS as described in the standard.
+std::int32_t RTNAME(GetLog)(
+    const Descriptor *argument = nullptr, const Descriptor *errmsg = nullptr);
+
 } // namespace Fortran::runtime
 
 #endif // FORTRAN_RUNTIME_COMMAND_H_
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index ad592814e5acb7..d199d5e387b864 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -28,5 +28,7 @@ std::int32_t FORTRAN_PROCEDURE_NAME(iargc)();
 void FORTRAN_PROCEDURE_NAME(getarg)(
     std::int32_t &n, std::int8_t *arg, std::int64_t length);
 
+void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *name, std::int64_t length);
+
 } // extern "C"
 #endif // FORTRAN_RUNTIME_EXTENSIONS_H_
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index b81a0791c5e571..6b2f313e227a19 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -15,6 +15,30 @@
 #include <cstdlib>
 #include <limits>
 
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <windows.h>
+
+#include <Lmcons.h> // UNLEN=256
+
+inline char *getlogin() {
+  char *username = NULL;
+  DWORD size = UNLEN + 1; // Constant for the maximum username length
+  username = (char *)malloc(size);
+
+  if (GetUserName(username, &size)) {
+    // Username retrieved successfully
+    return username;
+  } else {
+    free(username);
+    return NULL;
+  }
+}
+#else
+#include <unistd.h>
+#endif
+
 namespace Fortran::runtime {
 std::int32_t RTNAME(ArgumentCount)() {
   int argc{executionEnvironment.argc};
@@ -222,6 +246,22 @@ std::int32_t RTNAME(GetCommand)(const Descriptor *value,
   return stat;
 }
 
+std::int32_t RTNAME(GetLog)(const Descriptor *value, const Descriptor *errmsg) {
+  FillWithSpaces(*value);
+
+  const char *arg = getlogin();
+  std::int64_t argLen{StringLength(arg)};
+  if (argLen <= 0) {
+    return ToErrmsg(errmsg, StatMissingArgument);
+  }
+
+  if (value) {
+    return CopyToDescriptor(*value, arg, argLen, errmsg);
+  }
+
+  return StatOk;
+}
+
 static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
   std::size_t s{d.ElementBytes() - 1};
   while (*d.OffsetElement(s) == ' ') {
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index b8e9b6eae13205..47b269d1e5b42a 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -37,5 +37,11 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
   (void)RTNAME(GetCommandArgument)(
       n, &value, nullptr, nullptr, __FILE__, __LINE__);
 }
+
+void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
+  Descriptor value{*Descriptor::Create(1, length, arg, 0)};
+  (void)RTNAME(GetLog)(&value, nullptr);
+}
+
 } // namespace Fortran::runtime
 } // extern "C"
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index c3571c9684e4b0..e33dd7b0469ebf 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -225,6 +225,12 @@ TEST_F(ZeroArguments, GetCommandArgument) {
   CheckMissingArgumentValue(1);
 }
 
+TEST_F(ZeroArguments, GetLog) {
+  CheckMissingArgumentValue(-1);
+  CheckArgumentValue(commandOnlyArgv[0], 0);
+  CheckMissingArgumentValue(1);
+}
+
 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
 
 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
@@ -242,6 +248,13 @@ TEST_F(OneArgument, GetCommandArgument) {
   CheckMissingArgumentValue(2);
 }
 
+TEST_F(OneArgument, GetLog) {
+  CheckMissingArgumentValue(-1);
+  CheckArgumentValue(oneArgArgv[0], 0);
+  CheckArgumentValue(oneArgArgv[1], 1);
+  CheckMissingArgumentValue(2);
+}
+
 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
 
 static const char *severalArgsArgv[]{
@@ -284,7 +297,7 @@ TEST_F(SeveralArguments, ArgValueTooShort) {
   ASSERT_NE(tooShort, nullptr);
   EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);
   CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
-
+  
   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
   ASSERT_NE(length, nullptr);
   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};

>From ce001f38e45511d0c4381a65a95c0e76220e45c9 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 7 Nov 2023 15:09:23 +0000
Subject: [PATCH 02/26] add include string.h, wchar.h, link library, and format

---
 flang/runtime/command.cpp               | 25 ++++++++++++++++---------
 flang/unittests/Runtime/CommandTest.cpp |  2 +-
 2 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 6b2f313e227a19..c5171a1ef36f28 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -14,26 +14,33 @@
 #include "flang/Runtime/descriptor.h"
 #include <cstdlib>
 #include <limits>
+#include <string.h>
 
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
 #define NOMINMAX
 #include <windows.h>
 
-#include <Lmcons.h> // UNLEN=256
+#include <lmcons.h> // UNLEN=256
+#include <wchar.h> // wchar_t cast to LPWSTR
+#pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
 
-inline char *getlogin() {
-  char *username = NULL;
-  DWORD size = UNLEN + 1; // Constant for the maximum username length
-  username = (char *)malloc(size);
+static inline char *getlogin() {
+  static char username[UNLEN + 1];
+  wchar_t w_username[UNLEN + 1];
+  DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
 
-  if (GetUserName(username, &size)) {
-    // Username retrieved successfully
-    return username;
+  if (GetUserName(w_username, &namelen)) {
+    // Convert the wchar_t string to a regular C string
+    if (wcstombs(username, w_username, UNLEN + 1) == -1) {
+      // Conversion failed
+      return NULL;
+    }
+    return (username[0] == 0 ? NULL : username);
   } else {
-    free(username);
     return NULL;
   }
+  return nullptr;
 }
 #else
 #include <unistd.h>
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index e33dd7b0469ebf..686dc273142bad 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -297,7 +297,7 @@ TEST_F(SeveralArguments, ArgValueTooShort) {
   ASSERT_NE(tooShort, nullptr);
   EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);
   CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
-  
+
   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
   ASSERT_NE(length, nullptr);
   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};

>From 0de53646977c88ebc7bdb799325e5d92269a2f62 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Fri, 10 Nov 2023 10:44:12 +0000
Subject: [PATCH 03/26] use getlogin_r instead of getlogin, check for
 POSIX-compliant

accourding to https://linux.die.net/man/3/getlogin_r,
`_REENTRANT || _POSIX_C_SOURCE >= 199506L` checks the existance of getlogin_r
---
 flang/runtime/command.cpp | 44 ++++++++++++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 12 deletions(-)

diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index c5171a1ef36f28..aa7c777d767cbc 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -24,26 +24,30 @@
 #include <lmcons.h> // UNLEN=256
 #include <wchar.h> // wchar_t cast to LPWSTR
 #pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
+#define LOGIN_NAME_MAX UNLEN
 
-static inline char *getlogin() {
-  static char username[UNLEN + 1];
+inline int getlogin_r(char *buf, size_t bufSize) {
   wchar_t w_username[UNLEN + 1];
   DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
 
   if (GetUserName(w_username, &namelen)) {
     // Convert the wchar_t string to a regular C string
-    if (wcstombs(username, w_username, UNLEN + 1) == -1) {
+    if (wcstombs(buf, w_username, UNLEN + 1) == -1) {
       // Conversion failed
-      return NULL;
+      return -1;
     }
-    return (username[0] == 0 ? NULL : username);
+    return (buf[0] == 0 ? -1 : 0);
   } else {
-    return NULL;
+    return -1;
   }
-  return nullptr;
+  return -1;
 }
-#else
+#elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
+// System is posix-compliant and has getlogin_r
 #include <unistd.h>
+#else
+// System is not posix-compliant
+inline int getlogin_r(char *buf, size_t bufsize) { return -1; }
 #endif
 
 namespace Fortran::runtime {
@@ -253,17 +257,33 @@ std::int32_t RTNAME(GetCommand)(const Descriptor *value,
   return stat;
 }
 
+// Trim space from right/end
+char *RTrim(char *str) {
+  int i = strlen(str);
+  while (' ' == str[--i]) {
+    str[i] = 0;
+  }
+  return str;
+}
+
 std::int32_t RTNAME(GetLog)(const Descriptor *value, const Descriptor *errmsg) {
   FillWithSpaces(*value);
 
-  const char *arg = getlogin();
-  std::int64_t argLen{StringLength(arg)};
-  if (argLen <= 0) {
+  std::array<char, LOGIN_NAME_MAX + 1> str;
+  int err = getlogin_r(str.data(), str.size());
+  if (err != 0) {
+    return ToErrmsg(errmsg, StatMissingArgument);
+  }
+
+  RTrim(str.data());
+  std::int64_t strLen{StringLength(str.data())};
+
+  if (strLen <= 0) {
     return ToErrmsg(errmsg, StatMissingArgument);
   }
 
   if (value) {
-    return CopyToDescriptor(*value, arg, argLen, errmsg);
+    return CopyToDescriptor(*value, str.data(), strLen, errmsg);
   }
 
   return StatOk;

>From 72727baf225979fcf3108be7a6d936aaed20b97c Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Fri, 10 Nov 2023 15:06:08 +0000
Subject: [PATCH 04/26] move implementation to extension.cpp, add assert for
 failure, and format

---
 .../Optimizer/Builder/Runtime/RTBuilder.h     |  8 ---
 flang/include/flang/Runtime/command.h         |  6 --
 flang/runtime/command.cpp                     | 67 -------------------
 flang/runtime/extensions.cpp                  | 47 ++++++++++++-
 4 files changed, 45 insertions(+), 83 deletions(-)

diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
index 830df7ad006b54..b2774263e7a31a 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
@@ -62,14 +62,6 @@ using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *);
 /// standard type `i32` when `sizeof(int)` is 4.
 template <typename T>
 static constexpr TypeBuilderFunc getModel();
-
-template <>
-constexpr TypeBuilderFunc getModel<unsigned int>() {
-  return [](mlir::MLIRContext *context) -> mlir::Type {
-    return mlir::IntegerType::get(context, 8 * sizeof(unsigned int));
-  };
-}
-
 template <>
 constexpr TypeBuilderFunc getModel<short int>() {
   return [](mlir::MLIRContext *context) -> mlir::Type {
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index 1c212ef61697cd..ec628939054547 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -47,12 +47,6 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
     bool trim_name = true, const Descriptor *errmsg = nullptr,
     const char *sourceFile = nullptr, int line = 0);
 }
-
-// Try to get the name of current user
-// Returns a STATUS as described in the standard.
-std::int32_t RTNAME(GetLog)(
-    const Descriptor *argument = nullptr, const Descriptor *errmsg = nullptr);
-
 } // namespace Fortran::runtime
 
 #endif // FORTRAN_RUNTIME_COMMAND_H_
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index aa7c777d767cbc..b81a0791c5e571 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -14,41 +14,6 @@
 #include "flang/Runtime/descriptor.h"
 #include <cstdlib>
 #include <limits>
-#include <string.h>
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#define NOMINMAX
-#include <windows.h>
-
-#include <lmcons.h> // UNLEN=256
-#include <wchar.h> // wchar_t cast to LPWSTR
-#pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
-#define LOGIN_NAME_MAX UNLEN
-
-inline int getlogin_r(char *buf, size_t bufSize) {
-  wchar_t w_username[UNLEN + 1];
-  DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
-
-  if (GetUserName(w_username, &namelen)) {
-    // Convert the wchar_t string to a regular C string
-    if (wcstombs(buf, w_username, UNLEN + 1) == -1) {
-      // Conversion failed
-      return -1;
-    }
-    return (buf[0] == 0 ? -1 : 0);
-  } else {
-    return -1;
-  }
-  return -1;
-}
-#elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
-// System is posix-compliant and has getlogin_r
-#include <unistd.h>
-#else
-// System is not posix-compliant
-inline int getlogin_r(char *buf, size_t bufsize) { return -1; }
-#endif
 
 namespace Fortran::runtime {
 std::int32_t RTNAME(ArgumentCount)() {
@@ -257,38 +222,6 @@ std::int32_t RTNAME(GetCommand)(const Descriptor *value,
   return stat;
 }
 
-// Trim space from right/end
-char *RTrim(char *str) {
-  int i = strlen(str);
-  while (' ' == str[--i]) {
-    str[i] = 0;
-  }
-  return str;
-}
-
-std::int32_t RTNAME(GetLog)(const Descriptor *value, const Descriptor *errmsg) {
-  FillWithSpaces(*value);
-
-  std::array<char, LOGIN_NAME_MAX + 1> str;
-  int err = getlogin_r(str.data(), str.size());
-  if (err != 0) {
-    return ToErrmsg(errmsg, StatMissingArgument);
-  }
-
-  RTrim(str.data());
-  std::int64_t strLen{StringLength(str.data())};
-
-  if (strLen <= 0) {
-    return ToErrmsg(errmsg, StatMissingArgument);
-  }
-
-  if (value) {
-    return CopyToDescriptor(*value, str.data(), strLen, errmsg);
-  }
-
-  return StatOk;
-}
-
 static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
   std::size_t s{d.ElementBytes() - 1};
   while (*d.OffsetElement(s) == ' ') {
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 47b269d1e5b42a..a699196699ab8f 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -13,6 +13,41 @@
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
+#include <string.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <windows.h>
+
+#include <lmcons.h> // UNLEN=256
+#include <wchar.h> // wchar_t cast to LPWSTR
+#pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
+#define LOGIN_NAME_MAX UNLEN
+
+inline int getlogin_r(char *buf, size_t bufSize) {
+  wchar_t w_username[UNLEN + 1];
+  DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
+
+  if (GetUserName(w_username, &namelen)) {
+    // Convert the wchar_t string to a regular C string
+    if (wcstombs(buf, w_username, UNLEN + 1) == -1) {
+      // Conversion failed
+      return -1;
+    }
+    return (buf[0] == 0 ? -1 : 0);
+  } else {
+    return -1;
+  }
+  return -1;
+}
+#elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
+// System is posix-compliant and has getlogin_r
+#include <unistd.h>
+#else
+// System is not posix-compliant
+inline int getlogin_r(char *buf, size_t bufsize) { return -1; }
+#endif
 
 extern "C" {
 
@@ -39,8 +74,16 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  Descriptor value{*Descriptor::Create(1, length, arg, 0)};
-  (void)RTNAME(GetLog)(&value, nullptr);
+  std::array<char, LOGIN_NAME_MAX + 1> str;
+  int error = getlogin_r(str.data(), str.size());
+  assert(error == 0 && "getlogin_r returned an error");
+
+  // Trim space from right/end
+  int i = str.size();
+  while (' ' == str[--i]) {
+    str[i] = 0;
+  }
+  strncpy(reinterpret_cast<char *>(arg), str.data(), length);
 }
 
 } // namespace Fortran::runtime

>From 17cf43659f6c9e07ae58f54499706a0055cc5c71 Mon Sep 17 00:00:00 2001
From: Yi Wu <yiwu02 at wdev-yiwu02.arm.com>
Date: Mon, 13 Nov 2023 16:03:19 +0000
Subject: [PATCH 05/26] Use a more secure version for transfoom wchar_t to char

---
 flang/runtime/extensions.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index a699196699ab8f..aa14fb952ceb21 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -21,17 +21,19 @@
 #include <windows.h>
 
 #include <lmcons.h> // UNLEN=256
+#include <stdlib.h> // wcstombs_s
 #include <wchar.h> // wchar_t cast to LPWSTR
 #pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
 #define LOGIN_NAME_MAX UNLEN
 
 inline int getlogin_r(char *buf, size_t bufSize) {
   wchar_t w_username[UNLEN + 1];
-  DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
+  DWORD nameLen = UNLEN + 1;
 
-  if (GetUserName(w_username, &namelen)) {
-    // Convert the wchar_t string to a regular C string
-    if (wcstombs(buf, w_username, UNLEN + 1) == -1) {
+  if (GetUserNameW(w_username, &nameLen)) {
+    // Convert the wchar_t string to a regular C string using wcstombs_s
+    if (wcstombs_s(nullptr, buf, sizeof(w_username), w_username, _TRUNCATE) !=
+        0) {
       // Conversion failed
       return -1;
     }
@@ -41,6 +43,7 @@ inline int getlogin_r(char *buf, size_t bufSize) {
   }
   return -1;
 }
+
 #elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
 // System is posix-compliant and has getlogin_r
 #include <unistd.h>

>From a1b1b66fab3add35f33e6f56618b3c28e5cfdbf8 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 15 Nov 2023 11:06:01 +0000
Subject: [PATCH 06/26] use cstring not string.h, add terminator, fill buffer
 with space

take copyBufferAndPad() out of anonymous namespace,
so it can be called in other places.
---
 flang/include/flang/Runtime/time-intrinsic.h | 11 ++++++++
 flang/runtime/extensions.cpp                 | 29 +++++++++++---------
 flang/runtime/time-intrinsic.cpp             | 26 ++++++++++--------
 3 files changed, 42 insertions(+), 24 deletions(-)

diff --git a/flang/include/flang/Runtime/time-intrinsic.h b/flang/include/flang/Runtime/time-intrinsic.h
index 650c02436ee49e..a0c863712131b5 100644
--- a/flang/include/flang/Runtime/time-intrinsic.h
+++ b/flang/include/flang/Runtime/time-intrinsic.h
@@ -9,6 +9,17 @@
 // Defines the API between compiled code and the implementations of time-related
 // intrinsic subroutines in the runtime library.
 
+// time-intrinsic.h
+#ifndef TIME_INTRINSIC_H
+#define TIME_INTRINSIC_H
+
+#include <cstddef>
+
+void copyBufferAndPad(
+    char *dest, std::size_t destChars, char *buffer, std::size_t len);
+
+#endif // TIME_INTRINSIC_H
+
 #ifndef FORTRAN_RUNTIME_TIME_INTRINSIC_H_
 #define FORTRAN_RUNTIME_TIME_INTRINSIC_H_
 
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index aa14fb952ceb21..ea53d97d980053 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -10,18 +10,20 @@
 // extensions that will eventually be implemented in Fortran.
 
 #include "flang/Runtime/extensions.h"
+#include "terminator.h"
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
-#include <string.h>
+#include "flang/Runtime/time-intrinsic.h" // copyBufferAndPad
+#include <cstring>
 
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
 #define NOMINMAX
 #include <windows.h>
 
+#include <cstdlib> // wcstombs_s
 #include <lmcons.h> // UNLEN=256
-#include <stdlib.h> // wcstombs_s
 #include <wchar.h> // wchar_t cast to LPWSTR
 #pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
 #define LOGIN_NAME_MAX UNLEN
@@ -30,10 +32,9 @@ inline int getlogin_r(char *buf, size_t bufSize) {
   wchar_t w_username[UNLEN + 1];
   DWORD nameLen = UNLEN + 1;
 
-  if (GetUserNameW(w_username, &nameLen)) {
+  if (GetUserName(w_username, &nameLen)) {
     // Convert the wchar_t string to a regular C string using wcstombs_s
-    if (wcstombs_s(nullptr, buf, sizeof(w_username), w_username, _TRUNCATE) !=
-        0) {
+    if (wcstombs_s(nullptr, buf, bufSize, w_username, _TRUNCATE) != 0) {
       // Conversion failed
       return -1;
     }
@@ -49,7 +50,11 @@ inline int getlogin_r(char *buf, size_t bufSize) {
 #include <unistd.h>
 #else
 // System is not posix-compliant
-inline int getlogin_r(char *buf, size_t bufsize) { return -1; }
+inline int getlogin_r(char *buf, size_t bufSize) {
+  std::memset(buf, ' ', bufSize - 1);
+  buf[bufSize - 1] = '\0';
+  return 0;
+}
 #endif
 
 extern "C" {
@@ -78,15 +83,13 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   std::array<char, LOGIN_NAME_MAX + 1> str;
+
   int error = getlogin_r(str.data(), str.size());
-  assert(error == 0 && "getlogin_r returned an error");
+  Terminator terminator{__FILE__, __LINE__};
+  RUNTIME_CHECK(terminator, error == 0);
 
-  // Trim space from right/end
-  int i = str.size();
-  while (' ' == str[--i]) {
-    str[i] = 0;
-  }
-  strncpy(reinterpret_cast<char *>(arg), str.data(), length);
+  copyBufferAndPad(
+      reinterpret_cast<char *>(arg), length, str.data(), str.size());
 }
 
 } // namespace Fortran::runtime
diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp
index 68d63253139f18..6a42734d787393 100644
--- a/flang/runtime/time-intrinsic.cpp
+++ b/flang/runtime/time-intrinsic.cpp
@@ -39,6 +39,17 @@
 // overload will have a dummy parameter whose type indicates whether or not it
 // should be preferred. Any other parameters required for SFINAE should have
 // default values provided.
+
+// outside anonymous namespace, function reused
+void copyBufferAndPad(
+    char *dest, std::size_t destChars, char *buffer, std::size_t len) {
+  auto copyLen{std::min(len, destChars)};
+  std::memcpy(dest, buffer, copyLen);
+  for (auto i{copyLen}; i < destChars; ++i) {
+    dest[i] = ' ';
+  }
+}
+
 namespace {
 // Types for the dummy parameter indicating the priority of a given overload.
 // We will invoke our helper with an integer literal argument, so the overload
@@ -279,29 +290,22 @@ static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
 
   static constexpr std::size_t buffSize{16};
   char buffer[buffSize];
-  auto copyBufferAndPad{
-      [&](char *dest, std::size_t destChars, std::size_t len) {
-        auto copyLen{std::min(len, destChars)};
-        std::memcpy(dest, buffer, copyLen);
-        for (auto i{copyLen}; i < destChars; ++i) {
-          dest[i] = ' ';
-        }
-      }};
+
   if (date) {
     auto len = std::strftime(buffer, buffSize, "%Y%m%d", &localTime);
-    copyBufferAndPad(date, dateChars, len);
+    copyBufferAndPad(date, dateChars, buffer, len);
   }
   if (time) {
     auto len{std::snprintf(buffer, buffSize, "%02d%02d%02d.%03jd",
         localTime.tm_hour, localTime.tm_min, localTime.tm_sec, ms)};
-    copyBufferAndPad(time, timeChars, len);
+    copyBufferAndPad(time, timeChars, buffer, len);
   }
   if (zone) {
     // Note: this may leave the buffer empty on many platforms. Classic flang
     // has a much more complex way of doing this (see __io_timezone in classic
     // flang).
     auto len{std::strftime(buffer, buffSize, "%z", &localTime)};
-    copyBufferAndPad(zone, zoneChars, len);
+    copyBufferAndPad(zone, zoneChars, buffer, len);
   }
   if (values) {
     auto typeCode{values->type().GetCategoryAndKind()};

>From fb910d1e9587a1558b62ccd54ab0be064e0429e9 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 22 Nov 2023 13:40:38 +0000
Subject: [PATCH 07/26] brace initialize array

---
 flang/runtime/extensions.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index ea53d97d980053..23daa3eeb9d6c5 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -29,7 +29,7 @@
 #define LOGIN_NAME_MAX UNLEN
 
 inline int getlogin_r(char *buf, size_t bufSize) {
-  wchar_t w_username[UNLEN + 1];
+  wchar_t w_username[UNLEN + 1] = {};
   DWORD nameLen = UNLEN + 1;
 
   if (GetUserName(w_username, &nameLen)) {
@@ -82,7 +82,7 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  std::array<char, LOGIN_NAME_MAX + 1> str;
+  std::array<char, LOGIN_NAME_MAX + 1> str = {};
 
   int error = getlogin_r(str.data(), str.size());
   Terminator terminator{__FILE__, __LINE__};

>From f066c81f943a266afbdcdd1bbd3123c27b690114 Mon Sep 17 00:00:00 2001
From: Yi Wu <43659785+yi-wu-arm at users.noreply.github.com>
Date: Wed, 22 Nov 2023 14:04:46 +0000
Subject: [PATCH 08/26] Update flang/runtime/extensions.cpp: brace
 initialization

Co-authored-by: Kiran Chandramohan <kiranchandramohan at gmail.com>
---
 flang/runtime/extensions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 23daa3eeb9d6c5..e630abea799bdf 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -30,7 +30,7 @@
 
 inline int getlogin_r(char *buf, size_t bufSize) {
   wchar_t w_username[UNLEN + 1] = {};
-  DWORD nameLen = UNLEN + 1;
+  DWORD nameLen{UNLEN + 1};
 
   if (GetUserName(w_username, &nameLen)) {
     // Convert the wchar_t string to a regular C string using wcstombs_s

>From 8997af86097897765f8b1a5aba84ab357d07a5bc Mon Sep 17 00:00:00 2001
From: Yi Wu <43659785+yi-wu-arm at users.noreply.github.com>
Date: Wed, 22 Nov 2023 14:06:08 +0000
Subject: [PATCH 09/26] Update flang/runtime/extensions.cpp: brace
 initialization

Co-authored-by: Kiran Chandramohan <kiranchandramohan at gmail.com>
---
 flang/runtime/extensions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index e630abea799bdf..646ad120fcdfa1 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -84,7 +84,7 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   std::array<char, LOGIN_NAME_MAX + 1> str = {};
 
-  int error = getlogin_r(str.data(), str.size());
+  int error{getlogin_r(str.data(), str.size())};
   Terminator terminator{__FILE__, __LINE__};
   RUNTIME_CHECK(terminator, error == 0);
 

>From bad673a37d15418dd5119b639388e4600bbba197 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 22 Nov 2023 15:07:15 +0000
Subject: [PATCH 10/26] Revert "brace initialize array"

This reverts commit fb910d1e9587a1558b62ccd54ab0be064e0429e9.
---
 flang/runtime/extensions.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 646ad120fcdfa1..36f76a2f0e7b1e 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -29,7 +29,7 @@
 #define LOGIN_NAME_MAX UNLEN
 
 inline int getlogin_r(char *buf, size_t bufSize) {
-  wchar_t w_username[UNLEN + 1] = {};
+  wchar_t w_username[UNLEN + 1];
   DWORD nameLen{UNLEN + 1};
 
   if (GetUserName(w_username, &nameLen)) {
@@ -82,7 +82,7 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  std::array<char, LOGIN_NAME_MAX + 1> str = {};
+  std::array<char, LOGIN_NAME_MAX + 1> str;
 
   int error{getlogin_r(str.data(), str.size())};
   Terminator terminator{__FILE__, __LINE__};

>From b55bb5f23064eb342fb860e7e23ba49a84947b4c Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Mon, 27 Nov 2023 17:24:18 +0000
Subject: [PATCH 11/26] final nits

---
 flang/include/flang/Runtime/time-intrinsic.h |  4 ++--
 flang/runtime/extensions.cpp                 |  4 ++--
 flang/runtime/time-intrinsic.cpp             | 10 +++++-----
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/flang/include/flang/Runtime/time-intrinsic.h b/flang/include/flang/Runtime/time-intrinsic.h
index a0c863712131b5..6f1e0f390abeb1 100644
--- a/flang/include/flang/Runtime/time-intrinsic.h
+++ b/flang/include/flang/Runtime/time-intrinsic.h
@@ -15,8 +15,8 @@
 
 #include <cstddef>
 
-void copyBufferAndPad(
-    char *dest, std::size_t destChars, char *buffer, std::size_t len);
+void CopyBufferAndPad(
+    char *dest, std::size_t destChars, const char *buffer, std::size_t len);
 
 #endif // TIME_INTRINSIC_H
 
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 36f76a2f0e7b1e..1a3d2c8f745932 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -14,7 +14,7 @@
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
-#include "flang/Runtime/time-intrinsic.h" // copyBufferAndPad
+#include "flang/Runtime/time-intrinsic.h" // CopyBufferAndPad
 #include <cstring>
 
 #ifdef _WIN32
@@ -88,7 +88,7 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   Terminator terminator{__FILE__, __LINE__};
   RUNTIME_CHECK(terminator, error == 0);
 
-  copyBufferAndPad(
+  CopyBufferAndPad(
       reinterpret_cast<char *>(arg), length, str.data(), str.size());
 }
 
diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp
index 6a42734d787393..6d234a58ff8a43 100644
--- a/flang/runtime/time-intrinsic.cpp
+++ b/flang/runtime/time-intrinsic.cpp
@@ -41,8 +41,8 @@
 // default values provided.
 
 // outside anonymous namespace, function reused
-void copyBufferAndPad(
-    char *dest, std::size_t destChars, char *buffer, std::size_t len) {
+void CopyBufferAndPad(
+    char *dest, std::size_t destChars, const char *buffer, std::size_t len) {
   auto copyLen{std::min(len, destChars)};
   std::memcpy(dest, buffer, copyLen);
   for (auto i{copyLen}; i < destChars; ++i) {
@@ -293,19 +293,19 @@ static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
 
   if (date) {
     auto len = std::strftime(buffer, buffSize, "%Y%m%d", &localTime);
-    copyBufferAndPad(date, dateChars, buffer, len);
+    CopyBufferAndPad(date, dateChars, buffer, len);
   }
   if (time) {
     auto len{std::snprintf(buffer, buffSize, "%02d%02d%02d.%03jd",
         localTime.tm_hour, localTime.tm_min, localTime.tm_sec, ms)};
-    copyBufferAndPad(time, timeChars, buffer, len);
+    CopyBufferAndPad(time, timeChars, buffer, len);
   }
   if (zone) {
     // Note: this may leave the buffer empty on many platforms. Classic flang
     // has a much more complex way of doing this (see __io_timezone in classic
     // flang).
     auto len{std::strftime(buffer, buffSize, "%z", &localTime)};
-    copyBufferAndPad(zone, zoneChars, buffer, len);
+    CopyBufferAndPad(zone, zoneChars, buffer, len);
   }
   if (values) {
     auto typeCode{values->type().GetCategoryAndKind()};

>From d1470b0fe34f025519552ddc682f742ae821a2f9 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 5 Dec 2023 13:14:58 +0000
Subject: [PATCH 12/26] Revert "use cstring not string.h, add terminator, fill
 buffer with space"

This reverts commit a1b1b66fab3add35f33e6f56618b3c28e5cfdbf8.
---
 flang/include/flang/Runtime/time-intrinsic.h | 11 ---------
 flang/runtime/extensions.cpp                 |  9 +++----
 flang/runtime/time-intrinsic.cpp             | 26 +++++++++-----------
 3 files changed, 15 insertions(+), 31 deletions(-)

diff --git a/flang/include/flang/Runtime/time-intrinsic.h b/flang/include/flang/Runtime/time-intrinsic.h
index 6f1e0f390abeb1..650c02436ee49e 100644
--- a/flang/include/flang/Runtime/time-intrinsic.h
+++ b/flang/include/flang/Runtime/time-intrinsic.h
@@ -9,17 +9,6 @@
 // Defines the API between compiled code and the implementations of time-related
 // intrinsic subroutines in the runtime library.
 
-// time-intrinsic.h
-#ifndef TIME_INTRINSIC_H
-#define TIME_INTRINSIC_H
-
-#include <cstddef>
-
-void CopyBufferAndPad(
-    char *dest, std::size_t destChars, const char *buffer, std::size_t len);
-
-#endif // TIME_INTRINSIC_H
-
 #ifndef FORTRAN_RUNTIME_TIME_INTRINSIC_H_
 #define FORTRAN_RUNTIME_TIME_INTRINSIC_H_
 
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 1a3d2c8f745932..7e0f6494779f1c 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -14,7 +14,6 @@
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
-#include "flang/Runtime/time-intrinsic.h" // CopyBufferAndPad
 #include <cstring>
 
 #ifdef _WIN32
@@ -82,14 +81,14 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  std::array<char, LOGIN_NAME_MAX + 1> str;
+  int charLen{LOGIN_NAME_MAX + 1};
+  char str[charLen];
 
-  int error{getlogin_r(str.data(), str.size())};
+  int error{getlogin_r(*str, charLen)};
   Terminator terminator{__FILE__, __LINE__};
   RUNTIME_CHECK(terminator, error == 0);
 
-  CopyBufferAndPad(
-      reinterpret_cast<char *>(arg), length, str.data(), str.size());
+  CopyBufferAndPad(reinterpret_cast<char *>(arg), length, *str, charLen);
 }
 
 } // namespace Fortran::runtime
diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp
index 6d234a58ff8a43..68d63253139f18 100644
--- a/flang/runtime/time-intrinsic.cpp
+++ b/flang/runtime/time-intrinsic.cpp
@@ -39,17 +39,6 @@
 // overload will have a dummy parameter whose type indicates whether or not it
 // should be preferred. Any other parameters required for SFINAE should have
 // default values provided.
-
-// outside anonymous namespace, function reused
-void CopyBufferAndPad(
-    char *dest, std::size_t destChars, const char *buffer, std::size_t len) {
-  auto copyLen{std::min(len, destChars)};
-  std::memcpy(dest, buffer, copyLen);
-  for (auto i{copyLen}; i < destChars; ++i) {
-    dest[i] = ' ';
-  }
-}
-
 namespace {
 // Types for the dummy parameter indicating the priority of a given overload.
 // We will invoke our helper with an integer literal argument, so the overload
@@ -290,22 +279,29 @@ static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
 
   static constexpr std::size_t buffSize{16};
   char buffer[buffSize];
-
+  auto copyBufferAndPad{
+      [&](char *dest, std::size_t destChars, std::size_t len) {
+        auto copyLen{std::min(len, destChars)};
+        std::memcpy(dest, buffer, copyLen);
+        for (auto i{copyLen}; i < destChars; ++i) {
+          dest[i] = ' ';
+        }
+      }};
   if (date) {
     auto len = std::strftime(buffer, buffSize, "%Y%m%d", &localTime);
-    CopyBufferAndPad(date, dateChars, buffer, len);
+    copyBufferAndPad(date, dateChars, len);
   }
   if (time) {
     auto len{std::snprintf(buffer, buffSize, "%02d%02d%02d.%03jd",
         localTime.tm_hour, localTime.tm_min, localTime.tm_sec, ms)};
-    CopyBufferAndPad(time, timeChars, buffer, len);
+    copyBufferAndPad(time, timeChars, len);
   }
   if (zone) {
     // Note: this may leave the buffer empty on many platforms. Classic flang
     // has a much more complex way of doing this (see __io_timezone in classic
     // flang).
     auto len{std::strftime(buffer, buffSize, "%z", &localTime)};
-    CopyBufferAndPad(zone, zoneChars, buffer, len);
+    copyBufferAndPad(zone, zoneChars, len);
   }
   if (values) {
     auto typeCode{values->type().GetCategoryAndKind()};

>From 85d42d49042ee003ee0cb1a946be02c102d530a1 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 5 Dec 2023 16:39:57 +0000
Subject: [PATCH 13/26] move and use CopyAndPad from character.h

The compiler must have access to the implementations
of templated functions at the points where they're instantiated.
---
 flang/include/flang/Runtime/character.h | 30 +++++++++++++++++++++++++
 flang/runtime/character.cpp             | 22 +-----------------
 flang/runtime/extensions.cpp            | 11 ++++-----
 3 files changed, 37 insertions(+), 26 deletions(-)

diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h
index 24f26920bdd2cc..ec1fb027511643 100644
--- a/flang/include/flang/Runtime/character.h
+++ b/flang/include/flang/Runtime/character.h
@@ -6,6 +6,36 @@
 //
 //===----------------------------------------------------------------------===//
 
+// character.h
+#ifndef CHARACTER_H
+#define CHARACTER_H
+
+#include <cstddef>
+#include <algorithm>
+
+template <typename TO, typename FROM>
+void CopyAndPad(
+    TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
+  if constexpr (sizeof(TO) != sizeof(FROM)) {
+    std::size_t copyChars{std::min(toChars, fromChars)};
+    for (std::size_t j{0}; j < copyChars; ++j) {
+      to[j] = from[j];
+    }
+    for (std::size_t j{copyChars}; j < toChars; ++j) {
+      to[j] = static_cast<TO>(' ');
+    }
+  } else if (toChars <= fromChars) {
+    std::memcpy(to, from, toChars * sizeof(TO));
+  } else {
+    std::memcpy(to, from, fromChars * sizeof(TO));
+    for (std::size_t j{fromChars}; j < toChars; ++j) {
+      to[j] = static_cast<TO>(' ');
+    }
+  }
+}
+
+#endif // CHARACTER_H
+
 // Defines API between compiled code and the CHARACTER
 // support functions in the runtime library.
 
diff --git a/flang/runtime/character.cpp b/flang/runtime/character.cpp
index dd522f19a7ede5..2a62a60874e74d 100644
--- a/flang/runtime/character.cpp
+++ b/flang/runtime/character.cpp
@@ -11,6 +11,7 @@
 #include "tools.h"
 #include "flang/Common/bit-population-count.h"
 #include "flang/Common/uint128.h"
+#include "flang/Runtime/character.h"
 #include "flang/Runtime/cpp-type.h"
 #include "flang/Runtime/descriptor.h"
 #include <algorithm>
@@ -461,27 +462,6 @@ static void GeneralCharFuncKind(Descriptor &result, const Descriptor &string,
   }
 }
 
-template <typename TO, typename FROM>
-static void CopyAndPad(
-    TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
-  if constexpr (sizeof(TO) != sizeof(FROM)) {
-    std::size_t copyChars{std::min(toChars, fromChars)};
-    for (std::size_t j{0}; j < copyChars; ++j) {
-      to[j] = from[j];
-    }
-    for (std::size_t j{copyChars}; j < toChars; ++j) {
-      to[j] = static_cast<TO>(' ');
-    }
-  } else if (toChars <= fromChars) {
-    std::memcpy(to, from, toChars * sizeof(TO));
-  } else {
-    std::memcpy(to, from, fromChars * sizeof(TO));
-    for (std::size_t j{fromChars}; j < toChars; ++j) {
-      to[j] = static_cast<TO>(' ');
-    }
-  }
-}
-
 template <typename CHAR, bool ISMIN>
 static void MaxMinHelper(Descriptor &accumulator, const Descriptor &x,
     const Terminator &terminator) {
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 7e0f6494779f1c..ee34eef99d4db3 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -11,10 +11,10 @@
 
 #include "flang/Runtime/extensions.h"
 #include "terminator.h"
+#include "flang/Runtime/character.h"
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
-#include <cstring>
 
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
@@ -81,14 +81,15 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  int charLen{LOGIN_NAME_MAX + 1};
-  char str[charLen];
+  const int nameMaxLen = LOGIN_NAME_MAX + 1;
+  char str[nameMaxLen];
 
-  int error{getlogin_r(*str, charLen)};
+  int error{getlogin_r(str, nameMaxLen)};
   Terminator terminator{__FILE__, __LINE__};
   RUNTIME_CHECK(terminator, error == 0);
 
-  CopyBufferAndPad(reinterpret_cast<char *>(arg), length, *str, charLen);
+  // find first \0 in string then pad from there
+  CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
 }
 
 } // namespace Fortran::runtime

>From 99117e491ba0fd81dd7790bde6de91d0906b5a3b Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 10:40:37 +0000
Subject: [PATCH 14/26] better tests and use min length for memcpy

---
 flang/include/flang/Runtime/character.h   |  5 ++-
 flang/include/flang/Runtime/extensions.h  |  1 +
 flang/runtime/extensions.cpp              |  2 +-
 flang/unittests/Runtime/CMakeLists.txt    |  1 +
 flang/unittests/Runtime/CommandTest.cpp   | 14 +------
 flang/unittests/Runtime/ExtensionTest.cpp | 48 +++++++++++++++++++++++
 6 files changed, 55 insertions(+), 16 deletions(-)
 create mode 100644 flang/unittests/Runtime/ExtensionTest.cpp

diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h
index ec1fb027511643..52cd48f9637e69 100644
--- a/flang/include/flang/Runtime/character.h
+++ b/flang/include/flang/Runtime/character.h
@@ -10,8 +10,9 @@
 #ifndef CHARACTER_H
 #define CHARACTER_H
 
-#include <cstddef>
 #include <algorithm>
+#include <cstddef>
+#include <cstring>
 
 template <typename TO, typename FROM>
 void CopyAndPad(
@@ -27,7 +28,7 @@ void CopyAndPad(
   } else if (toChars <= fromChars) {
     std::memcpy(to, from, toChars * sizeof(TO));
   } else {
-    std::memcpy(to, from, fromChars * sizeof(TO));
+    std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
     for (std::size_t j{fromChars}; j < toChars; ++j) {
       to[j] = static_cast<TO>(' ');
     }
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index d199d5e387b864..4f78e92ae677bb 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -28,6 +28,7 @@ std::int32_t FORTRAN_PROCEDURE_NAME(iargc)();
 void FORTRAN_PROCEDURE_NAME(getarg)(
     std::int32_t &n, std::int8_t *arg, std::int64_t length);
 
+// GNU extension subroutine GETLOG(C).
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *name, std::int64_t length);
 
 } // extern "C"
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index ee34eef99d4db3..21d2ece06865aa 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -81,7 +81,7 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  const int nameMaxLen = LOGIN_NAME_MAX + 1;
+  const int nameMaxLen{LOGIN_NAME_MAX + 1};
   char str[nameMaxLen];
 
   int error{getlogin_r(str, nameMaxLen)};
diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt
index 23f02aa751246b..a25472409ac943 100644
--- a/flang/unittests/Runtime/CMakeLists.txt
+++ b/flang/unittests/Runtime/CMakeLists.txt
@@ -7,6 +7,7 @@ add_flang_unittest(FlangRuntimeTests
   Complex.cpp
   CrashHandlerFixture.cpp
   Derived.cpp
+  ExtensionTest.cpp
   ExternalIOTest.cpp
   Format.cpp
   Inquiry.cpp
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 61209541cdb1e5..d003eacd177e89 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -10,6 +10,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "flang/Runtime/descriptor.h"
+#include "flang/Runtime/extensions.h"
 #include "flang/Runtime/main.h"
 #include <cstdlib>
 
@@ -225,12 +226,6 @@ TEST_F(ZeroArguments, GetCommandArgument) {
   CheckMissingArgumentValue(1);
 }
 
-TEST_F(ZeroArguments, GetLog) {
-  CheckMissingArgumentValue(-1);
-  CheckArgumentValue(commandOnlyArgv[0], 0);
-  CheckMissingArgumentValue(1);
-}
-
 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
 
 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
@@ -248,13 +243,6 @@ TEST_F(OneArgument, GetCommandArgument) {
   CheckMissingArgumentValue(2);
 }
 
-TEST_F(OneArgument, GetLog) {
-  CheckMissingArgumentValue(-1);
-  CheckArgumentValue(oneArgArgv[0], 0);
-  CheckArgumentValue(oneArgArgv[1], 1);
-  CheckMissingArgumentValue(2);
-}
-
 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
 
 static const char *severalArgsArgv[]{
diff --git a/flang/unittests/Runtime/ExtensionTest.cpp b/flang/unittests/Runtime/ExtensionTest.cpp
new file mode 100644
index 00000000000000..d1147f18c21591
--- /dev/null
+++ b/flang/unittests/Runtime/ExtensionTest.cpp
@@ -0,0 +1,48 @@
+//===-- flang/unittests/Runtime/ExtensionTest.cpp
+//---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CrashHandlerFixture.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "flang/Runtime/descriptor.h"
+#include "flang/Runtime/extensions.h"
+#include "flang/Runtime/main.h"
+#include <cstdlib>
+
+#ifdef _WIN32
+#include <lmcons.h> // UNLEN=256
+#define LOGIN_NAME_MAX UNLEN
+#else
+#include <limits.h>
+#endif
+
+using namespace Fortran::runtime;
+
+struct ExtensionTests : CrashHandlerFixture {};
+
+TEST_F(ExtensionTests, GetlogGetName) {
+  const int charLen{3};
+  char input[charLen]{"\0\0"};
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::int8_t *>(input), charLen);
+
+  EXPECT_NE(input[0], '\0');
+}
+
+TEST_F(ExtensionTests, GetlogPadSpace) {
+  const int charLen{LOGIN_NAME_MAX +
+      2}; // guarantee 1 char longer than max, last char should be pad space
+  char input[charLen];
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::int8_t *>(input), charLen);
+
+  EXPECT_EQ(input[charLen - 1], ' ');
+}
\ No newline at end of file

>From ea2ea3b3a473e0d0092ad8865c49f7e627a03c64 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 10:57:28 +0000
Subject: [PATCH 15/26] doc change: move getlog to Library subroutine row

---
 flang/docs/Intrinsics.md | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index fd16ed3bcd6b69..4a6e68d708efbb 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -657,6 +657,11 @@ CALL CO_REDUCE
 CALL CO_SUM
 ```
 
+### Library subroutine 
+```
+CALL GETLOG(USRNAME)
+```
+
 ## Non-standard intrinsics
 ### PGI
 ```
@@ -751,9 +756,10 @@ This phase currently supports all the intrinsic procedures listed above but the
 | Object characteristic inquiry functions | ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, IS_CONTIGUOUS, PRESENT, RANK, SAME_TYPE, STORAGE_SIZE |
 | Type inquiry intrinsic functions | BIT_SIZE, DIGITS, EPSILON, HUGE, KIND, MAXEXPONENT, MINEXPONENT, NEW_LINE, PRECISION, RADIX, RANGE, TINY|
 | Non-standard intrinsic functions | AND, OR, XOR, SHIFT, ZEXT, IZEXT, COSD, SIND, TAND, ACOSD, ASIND, ATAND, ATAN2D, COMPL, EQV, NEQV, INT8, JINT, JNINT, KNINT, QCMPLX, DREAL, DFLOAT, QEXT, QFLOAT, QREAL, DNUM, NUM, JNUM, KNUM, QNUM, RNUM, RAN, RANF, ILEN, SIZEOF, MCLOCK, SECNDS, COTAN, IBCHNG, ISHA, ISHC, ISHL, IXOR, IARG, IARGC, NARGS, GETPID, NUMARG, BADDRESS, IADDR, CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, MALLOC |
-| Intrinsic subroutines |MVBITS (elemental), CPU_TIME, DATE_AND_TIME, EVENT_QUERY, EXECUTE_COMMAND_LINE, GET_COMMAND, GET_COMMAND_ARGUMENT, GET_ENVIRONMENT_VARIABLE, GETLOG, MOVE_ALLOC, RANDOM_INIT, RANDOM_NUMBER, RANDOM_SEED, SYSTEM_CLOCK |
+| Intrinsic subroutines |MVBITS (elemental), CPU_TIME, DATE_AND_TIME, EVENT_QUERY, EXECUTE_COMMAND_LINE, GET_COMMAND, GET_COMMAND_ARGUMENT, GET_ENVIRONMENT_VARIABLE, MOVE_ALLOC, RANDOM_INIT, RANDOM_NUMBER, RANDOM_SEED, SYSTEM_CLOCK |
 | Atomic intrinsic subroutines | ATOMIC_ADD |
 | Collective intrinsic subroutines | CO_REDUCE |
+| Library subroutines | GETLOG|
 
 
 ### Intrinsic Function Folding

>From 67d404585846786455310b55b44b2d1ac41caf7c Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 12:42:37 +0000
Subject: [PATCH 16/26] small fixes to tests

---
 flang/runtime/extensions.cpp              | 5 ++++-
 flang/unittests/Runtime/ExtensionTest.cpp | 9 +++++----
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 21d2ece06865aa..2457731ba7b92c 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -80,13 +80,16 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
       n, &value, nullptr, nullptr, __FILE__, __LINE__);
 }
 
+// CALL GETLOG(USRNAME)
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   const int nameMaxLen{LOGIN_NAME_MAX + 1};
   char str[nameMaxLen];
 
   int error{getlogin_r(str, nameMaxLen)};
   Terminator terminator{__FILE__, __LINE__};
-  RUNTIME_CHECK(terminator, error == 0);
+  if (error != 0) {
+    terminator.Crash("getlogin_r fail with a nonzero value: %d.\n", error);
+  }
 
   // find first \0 in string then pad from there
   CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
diff --git a/flang/unittests/Runtime/ExtensionTest.cpp b/flang/unittests/Runtime/ExtensionTest.cpp
index d1147f18c21591..a74da4e7005ba3 100644
--- a/flang/unittests/Runtime/ExtensionTest.cpp
+++ b/flang/unittests/Runtime/ExtensionTest.cpp
@@ -16,10 +16,10 @@
 #include <cstdlib>
 
 #ifdef _WIN32
-#include <lmcons.h> // UNLEN=256
+#include <lmcons.h> // UNLEN
 #define LOGIN_NAME_MAX UNLEN
 #else
-#include <limits.h>
+#include <limits.h> // LOGIN_NAME_MAX
 #endif
 
 using namespace Fortran::runtime;
@@ -37,8 +37,9 @@ TEST_F(ExtensionTests, GetlogGetName) {
 }
 
 TEST_F(ExtensionTests, GetlogPadSpace) {
-  const int charLen{LOGIN_NAME_MAX +
-      2}; // guarantee 1 char longer than max, last char should be pad space
+  // guarantee 1 char longer than max, last char should be pad with space
+  const int charLen{LOGIN_NAME_MAX + 2};
+
   char input[charLen];
 
   FORTRAN_PROCEDURE_NAME(getlog)

>From 3ac03a67ea06185f8cdbe1f0cbb8c88405f3492b Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 13:11:23 +0000
Subject: [PATCH 17/26] use env to set and test user name

---
 flang/unittests/Runtime/CMakeLists.txt    |  1 -
 flang/unittests/Runtime/CommandTest.cpp   | 31 ++++++++++++++
 flang/unittests/Runtime/ExtensionTest.cpp | 49 -----------------------
 3 files changed, 31 insertions(+), 50 deletions(-)
 delete mode 100644 flang/unittests/Runtime/ExtensionTest.cpp

diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt
index a25472409ac943..23f02aa751246b 100644
--- a/flang/unittests/Runtime/CMakeLists.txt
+++ b/flang/unittests/Runtime/CMakeLists.txt
@@ -7,7 +7,6 @@ add_flang_unittest(FlangRuntimeTests
   Complex.cpp
   CrashHandlerFixture.cpp
   Derived.cpp
-  ExtensionTest.cpp
   ExternalIOTest.cpp
   Format.cpp
   Inquiry.cpp
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index d003eacd177e89..6287e6a40c681c 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -398,6 +398,7 @@ class EnvironmentVariables : public CommandFixture {
 protected:
   EnvironmentVariables() : CommandFixture(0, nullptr) {
     SetEnv("NAME", "VALUE");
+    SetEnv("LOGNAME", "loginName");
     SetEnv("EMPTY", "");
   }
 
@@ -495,3 +496,33 @@ TEST_F(EnvironmentVariables, ErrMsgTooShort) {
       1);
   CheckDescriptorEqStr(errMsg.get(), "Mis");
 }
+
+TEST_F(EnvironmentVariables, GetlogGetName) {
+  const int charLen{11};
+  char input[charLen]{"XXXXXXXXX"};
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::int8_t *>(input), charLen);
+
+  EXPECT_NE(input, "loginName");
+}
+
+TEST_F(EnvironmentVariables, GetlogBufferShort) {
+  const int charLen{7};
+  char input[charLen]{"XXXXXX"};
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::int8_t *>(input), charLen);
+
+  EXPECT_NE(input, "loginN");
+}
+
+TEST_F(EnvironmentVariables, GetlogPadSpace) {
+  const int charLen{12};
+  char input[charLen]{"XXXXXXXXXX"};
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::int8_t *>(input), charLen);
+
+  EXPECT_NE(input, "loginName ");
+}
diff --git a/flang/unittests/Runtime/ExtensionTest.cpp b/flang/unittests/Runtime/ExtensionTest.cpp
deleted file mode 100644
index a74da4e7005ba3..00000000000000
--- a/flang/unittests/Runtime/ExtensionTest.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-//===-- flang/unittests/Runtime/ExtensionTest.cpp
-//---------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "CrashHandlerFixture.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "flang/Runtime/descriptor.h"
-#include "flang/Runtime/extensions.h"
-#include "flang/Runtime/main.h"
-#include <cstdlib>
-
-#ifdef _WIN32
-#include <lmcons.h> // UNLEN
-#define LOGIN_NAME_MAX UNLEN
-#else
-#include <limits.h> // LOGIN_NAME_MAX
-#endif
-
-using namespace Fortran::runtime;
-
-struct ExtensionTests : CrashHandlerFixture {};
-
-TEST_F(ExtensionTests, GetlogGetName) {
-  const int charLen{3};
-  char input[charLen]{"\0\0"};
-
-  FORTRAN_PROCEDURE_NAME(getlog)
-  (reinterpret_cast<std::int8_t *>(input), charLen);
-
-  EXPECT_NE(input[0], '\0');
-}
-
-TEST_F(ExtensionTests, GetlogPadSpace) {
-  // guarantee 1 char longer than max, last char should be pad with space
-  const int charLen{LOGIN_NAME_MAX + 2};
-
-  char input[charLen];
-
-  FORTRAN_PROCEDURE_NAME(getlog)
-  (reinterpret_cast<std::int8_t *>(input), charLen);
-
-  EXPECT_EQ(input[charLen - 1], ' ');
-}
\ No newline at end of file

>From 266a2695f14bdc292bd1f4249c1255520aa1c9f3 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 15:28:41 +0000
Subject: [PATCH 18/26] get logname from environment variable LOGNAME not from
 getlog_r, and restore CopyAndPad

Suggested: https://man.archlinux.org/man/getlogin_r.3.en
For most purposes, it is more useful to use the environment variable LOGNAME to find out who the user is. This is more flexible precisely because the user can set LOGNAME arbitrarily.

POSIX specification:
LOGNAME
The system shall initialize this variable at the time of login to be the user's login name.

Note that at this point, this implementation might not work on Windows
---
 flang/include/flang/Runtime/character.h | 31 ------------
 flang/runtime/character.cpp             | 21 +++++++++
 flang/runtime/extensions.cpp            | 63 +++++--------------------
 flang/unittests/Runtime/CommandTest.cpp | 13 +++--
 4 files changed, 44 insertions(+), 84 deletions(-)

diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h
index 52cd48f9637e69..24f26920bdd2cc 100644
--- a/flang/include/flang/Runtime/character.h
+++ b/flang/include/flang/Runtime/character.h
@@ -6,37 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// character.h
-#ifndef CHARACTER_H
-#define CHARACTER_H
-
-#include <algorithm>
-#include <cstddef>
-#include <cstring>
-
-template <typename TO, typename FROM>
-void CopyAndPad(
-    TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
-  if constexpr (sizeof(TO) != sizeof(FROM)) {
-    std::size_t copyChars{std::min(toChars, fromChars)};
-    for (std::size_t j{0}; j < copyChars; ++j) {
-      to[j] = from[j];
-    }
-    for (std::size_t j{copyChars}; j < toChars; ++j) {
-      to[j] = static_cast<TO>(' ');
-    }
-  } else if (toChars <= fromChars) {
-    std::memcpy(to, from, toChars * sizeof(TO));
-  } else {
-    std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
-    for (std::size_t j{fromChars}; j < toChars; ++j) {
-      to[j] = static_cast<TO>(' ');
-    }
-  }
-}
-
-#endif // CHARACTER_H
-
 // Defines API between compiled code and the CHARACTER
 // support functions in the runtime library.
 
diff --git a/flang/runtime/character.cpp b/flang/runtime/character.cpp
index 2a62a60874e74d..99528ce837f467 100644
--- a/flang/runtime/character.cpp
+++ b/flang/runtime/character.cpp
@@ -462,6 +462,27 @@ static void GeneralCharFuncKind(Descriptor &result, const Descriptor &string,
   }
 }
 
+template <typename TO, typename FROM>
+static void CopyAndPad(
+    TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
+  if constexpr (sizeof(TO) != sizeof(FROM)) {
+    std::size_t copyChars{std::min(toChars, fromChars)};
+    for (std::size_t j{0}; j < copyChars; ++j) {
+      to[j] = from[j];
+    }
+    for (std::size_t j{copyChars}; j < toChars; ++j) {
+      to[j] = static_cast<TO>(' ');
+    }
+  } else if (toChars <= fromChars) {
+    std::memcpy(to, from, toChars * sizeof(TO));
+  } else {
+    std::memcpy(to, from, fromChars * sizeof(TO));
+    for (std::size_t j{fromChars}; j < toChars; ++j) {
+      to[j] = static_cast<TO>(' ');
+    }
+  }
+}
+
 template <typename CHAR, bool ISMIN>
 static void MaxMinHelper(Descriptor &accumulator, const Descriptor &x,
     const Terminator &terminator) {
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 2457731ba7b92c..e2b290c262a117 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -10,52 +10,10 @@
 // extensions that will eventually be implemented in Fortran.
 
 #include "flang/Runtime/extensions.h"
-#include "terminator.h"
-#include "flang/Runtime/character.h"
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
 
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#define NOMINMAX
-#include <windows.h>
-
-#include <cstdlib> // wcstombs_s
-#include <lmcons.h> // UNLEN=256
-#include <wchar.h> // wchar_t cast to LPWSTR
-#pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
-#define LOGIN_NAME_MAX UNLEN
-
-inline int getlogin_r(char *buf, size_t bufSize) {
-  wchar_t w_username[UNLEN + 1];
-  DWORD nameLen{UNLEN + 1};
-
-  if (GetUserName(w_username, &nameLen)) {
-    // Convert the wchar_t string to a regular C string using wcstombs_s
-    if (wcstombs_s(nullptr, buf, bufSize, w_username, _TRUNCATE) != 0) {
-      // Conversion failed
-      return -1;
-    }
-    return (buf[0] == 0 ? -1 : 0);
-  } else {
-    return -1;
-  }
-  return -1;
-}
-
-#elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
-// System is posix-compliant and has getlogin_r
-#include <unistd.h>
-#else
-// System is not posix-compliant
-inline int getlogin_r(char *buf, size_t bufSize) {
-  std::memset(buf, ' ', bufSize - 1);
-  buf[bufSize - 1] = '\0';
-  return 0;
-}
-#endif
-
 extern "C" {
 
 namespace Fortran::runtime {
@@ -82,17 +40,22 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 
 // CALL GETLOG(USRNAME)
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  const int nameMaxLen{LOGIN_NAME_MAX + 1};
-  char str[nameMaxLen];
+  //Get environment variable LOGNAME
+  const int charLen = 8;
+  char envName[charLen] = "LOGNAME";
+  std::size_t n{std::strlen(envName)};
+  Descriptor name{*Descriptor::Create(1, n, envName, 0)};
+  std::memcpy(name.OffsetElement(), envName, n);
+
+  Descriptor value{*Descriptor::Create(1, length, arg, 0)};
+
+  RTNAME(GetEnvVariable)
+  (name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
 
-  int error{getlogin_r(str, nameMaxLen)};
-  Terminator terminator{__FILE__, __LINE__};
-  if (error != 0) {
-    terminator.Crash("getlogin_r fail with a nonzero value: %d.\n", error);
-  }
+  std::memcpy(reinterpret_cast<char *>(arg), value.OffsetElement(), length);
 
   // find first \0 in string then pad from there
-  CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
+  // CopyAndPad(reinterpret_cast<char *>(arg), value.OffsetElement(), length, value.ElementBytes());
 }
 
 } // namespace Fortran::runtime
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 6287e6a40c681c..f0958f58474db8 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -71,6 +71,13 @@ class CommandFixture : public ::testing::Test {
         << std::string{value->OffsetElement(), value->ElementBytes()};
   }
 
+  void CheckCharEqStr(const char *value, const std::string &expected) const {
+    ASSERT_NE(value, nullptr);
+    EXPECT_EQ(std::strncmp(value, expected.c_str(), expected.size()), 0)
+        << "expected: " << expected << "\n"
+        << "value: " << value;
+  }
+
   template <typename INT_T = std::int64_t>
   void CheckDescriptorEqInt(
       const Descriptor *value, const INT_T expected) const {
@@ -504,7 +511,7 @@ TEST_F(EnvironmentVariables, GetlogGetName) {
   FORTRAN_PROCEDURE_NAME(getlog)
   (reinterpret_cast<std::int8_t *>(input), charLen);
 
-  EXPECT_NE(input, "loginName");
+  CheckCharEqStr(input, "loginName");
 }
 
 TEST_F(EnvironmentVariables, GetlogBufferShort) {
@@ -514,7 +521,7 @@ TEST_F(EnvironmentVariables, GetlogBufferShort) {
   FORTRAN_PROCEDURE_NAME(getlog)
   (reinterpret_cast<std::int8_t *>(input), charLen);
 
-  EXPECT_NE(input, "loginN");
+  CheckCharEqStr(input, "loginN");
 }
 
 TEST_F(EnvironmentVariables, GetlogPadSpace) {
@@ -524,5 +531,5 @@ TEST_F(EnvironmentVariables, GetlogPadSpace) {
   FORTRAN_PROCEDURE_NAME(getlog)
   (reinterpret_cast<std::int8_t *>(input), charLen);
 
-  EXPECT_NE(input, "loginName ");
+  CheckCharEqStr(input, "loginName ");
 }

>From 40ff3730c06c92a60d30b41b3e797860598bd814 Mon Sep 17 00:00:00 2001
From: Yi Wu <yiwu02 at wdev-yiwu02.arm.com>
Date: Wed, 6 Dec 2023 17:08:18 +0000
Subject: [PATCH 19/26] windows use USERNAME environment variable

---
 flang/runtime/extensions.cpp            | 14 ++++++--------
 flang/unittests/Runtime/CommandTest.cpp |  4 ++++
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index e2b290c262a117..893d3a7de04245 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -40,22 +40,20 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 
 // CALL GETLOG(USRNAME)
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  //Get environment variable LOGNAME
+  // get username from environment variable
+#ifdef _WIN32
+  const int charLen = 9;
+  char envName[charLen] = "USERNAME";
+#else
   const int charLen = 8;
   char envName[charLen] = "LOGNAME";
+#endif
   std::size_t n{std::strlen(envName)};
   Descriptor name{*Descriptor::Create(1, n, envName, 0)};
-  std::memcpy(name.OffsetElement(), envName, n);
-
   Descriptor value{*Descriptor::Create(1, length, arg, 0)};
 
   RTNAME(GetEnvVariable)
   (name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
-
-  std::memcpy(reinterpret_cast<char *>(arg), value.OffsetElement(), length);
-
-  // find first \0 in string then pad from there
-  // CopyAndPad(reinterpret_cast<char *>(arg), value.OffsetElement(), length, value.ElementBytes());
 }
 
 } // namespace Fortran::runtime
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index f0958f58474db8..0163dada41b3fc 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -405,7 +405,11 @@ class EnvironmentVariables : public CommandFixture {
 protected:
   EnvironmentVariables() : CommandFixture(0, nullptr) {
     SetEnv("NAME", "VALUE");
+#ifdef _WIN32
+    SetEnv("USERNAME", "loginName");
+#else
     SetEnv("LOGNAME", "loginName");
+#endif
     SetEnv("EMPTY", "");
   }
 

>From 2cdaa2d6fbb040b9777ca03447c37ccc94a000df Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 17:54:36 +0000
Subject: [PATCH 20/26] Revert "get logname from environment variable LOGNAME
 not from getlog_r, and restore CopyAndPad"

This reverts commit 266a2695f14bdc292bd1f4249c1255520aa1c9f3.
---
 flang/include/flang/Runtime/character.h | 31 ++++++++++++
 flang/runtime/character.cpp             | 21 --------
 flang/runtime/extensions.cpp            | 65 ++++++++++++++++++++-----
 flang/unittests/Runtime/CommandTest.cpp | 13 ++---
 4 files changed, 86 insertions(+), 44 deletions(-)

diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h
index 24f26920bdd2cc..52cd48f9637e69 100644
--- a/flang/include/flang/Runtime/character.h
+++ b/flang/include/flang/Runtime/character.h
@@ -6,6 +6,37 @@
 //
 //===----------------------------------------------------------------------===//
 
+// character.h
+#ifndef CHARACTER_H
+#define CHARACTER_H
+
+#include <algorithm>
+#include <cstddef>
+#include <cstring>
+
+template <typename TO, typename FROM>
+void CopyAndPad(
+    TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
+  if constexpr (sizeof(TO) != sizeof(FROM)) {
+    std::size_t copyChars{std::min(toChars, fromChars)};
+    for (std::size_t j{0}; j < copyChars; ++j) {
+      to[j] = from[j];
+    }
+    for (std::size_t j{copyChars}; j < toChars; ++j) {
+      to[j] = static_cast<TO>(' ');
+    }
+  } else if (toChars <= fromChars) {
+    std::memcpy(to, from, toChars * sizeof(TO));
+  } else {
+    std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
+    for (std::size_t j{fromChars}; j < toChars; ++j) {
+      to[j] = static_cast<TO>(' ');
+    }
+  }
+}
+
+#endif // CHARACTER_H
+
 // Defines API between compiled code and the CHARACTER
 // support functions in the runtime library.
 
diff --git a/flang/runtime/character.cpp b/flang/runtime/character.cpp
index 99528ce837f467..2a62a60874e74d 100644
--- a/flang/runtime/character.cpp
+++ b/flang/runtime/character.cpp
@@ -462,27 +462,6 @@ static void GeneralCharFuncKind(Descriptor &result, const Descriptor &string,
   }
 }
 
-template <typename TO, typename FROM>
-static void CopyAndPad(
-    TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
-  if constexpr (sizeof(TO) != sizeof(FROM)) {
-    std::size_t copyChars{std::min(toChars, fromChars)};
-    for (std::size_t j{0}; j < copyChars; ++j) {
-      to[j] = from[j];
-    }
-    for (std::size_t j{copyChars}; j < toChars; ++j) {
-      to[j] = static_cast<TO>(' ');
-    }
-  } else if (toChars <= fromChars) {
-    std::memcpy(to, from, toChars * sizeof(TO));
-  } else {
-    std::memcpy(to, from, fromChars * sizeof(TO));
-    for (std::size_t j{fromChars}; j < toChars; ++j) {
-      to[j] = static_cast<TO>(' ');
-    }
-  }
-}
-
 template <typename CHAR, bool ISMIN>
 static void MaxMinHelper(Descriptor &accumulator, const Descriptor &x,
     const Terminator &terminator) {
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 893d3a7de04245..2457731ba7b92c 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -10,10 +10,52 @@
 // extensions that will eventually be implemented in Fortran.
 
 #include "flang/Runtime/extensions.h"
+#include "terminator.h"
+#include "flang/Runtime/character.h"
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
 
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <windows.h>
+
+#include <cstdlib> // wcstombs_s
+#include <lmcons.h> // UNLEN=256
+#include <wchar.h> // wchar_t cast to LPWSTR
+#pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
+#define LOGIN_NAME_MAX UNLEN
+
+inline int getlogin_r(char *buf, size_t bufSize) {
+  wchar_t w_username[UNLEN + 1];
+  DWORD nameLen{UNLEN + 1};
+
+  if (GetUserName(w_username, &nameLen)) {
+    // Convert the wchar_t string to a regular C string using wcstombs_s
+    if (wcstombs_s(nullptr, buf, bufSize, w_username, _TRUNCATE) != 0) {
+      // Conversion failed
+      return -1;
+    }
+    return (buf[0] == 0 ? -1 : 0);
+  } else {
+    return -1;
+  }
+  return -1;
+}
+
+#elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
+// System is posix-compliant and has getlogin_r
+#include <unistd.h>
+#else
+// System is not posix-compliant
+inline int getlogin_r(char *buf, size_t bufSize) {
+  std::memset(buf, ' ', bufSize - 1);
+  buf[bufSize - 1] = '\0';
+  return 0;
+}
+#endif
+
 extern "C" {
 
 namespace Fortran::runtime {
@@ -40,20 +82,17 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 
 // CALL GETLOG(USRNAME)
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  // get username from environment variable
-#ifdef _WIN32
-  const int charLen = 9;
-  char envName[charLen] = "USERNAME";
-#else
-  const int charLen = 8;
-  char envName[charLen] = "LOGNAME";
-#endif
-  std::size_t n{std::strlen(envName)};
-  Descriptor name{*Descriptor::Create(1, n, envName, 0)};
-  Descriptor value{*Descriptor::Create(1, length, arg, 0)};
+  const int nameMaxLen{LOGIN_NAME_MAX + 1};
+  char str[nameMaxLen];
+
+  int error{getlogin_r(str, nameMaxLen)};
+  Terminator terminator{__FILE__, __LINE__};
+  if (error != 0) {
+    terminator.Crash("getlogin_r fail with a nonzero value: %d.\n", error);
+  }
 
-  RTNAME(GetEnvVariable)
-  (name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
+  // find first \0 in string then pad from there
+  CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
 }
 
 } // namespace Fortran::runtime
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 0163dada41b3fc..471a83143bde18 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -71,13 +71,6 @@ class CommandFixture : public ::testing::Test {
         << std::string{value->OffsetElement(), value->ElementBytes()};
   }
 
-  void CheckCharEqStr(const char *value, const std::string &expected) const {
-    ASSERT_NE(value, nullptr);
-    EXPECT_EQ(std::strncmp(value, expected.c_str(), expected.size()), 0)
-        << "expected: " << expected << "\n"
-        << "value: " << value;
-  }
-
   template <typename INT_T = std::int64_t>
   void CheckDescriptorEqInt(
       const Descriptor *value, const INT_T expected) const {
@@ -515,7 +508,7 @@ TEST_F(EnvironmentVariables, GetlogGetName) {
   FORTRAN_PROCEDURE_NAME(getlog)
   (reinterpret_cast<std::int8_t *>(input), charLen);
 
-  CheckCharEqStr(input, "loginName");
+  EXPECT_NE(input, "loginName");
 }
 
 TEST_F(EnvironmentVariables, GetlogBufferShort) {
@@ -525,7 +518,7 @@ TEST_F(EnvironmentVariables, GetlogBufferShort) {
   FORTRAN_PROCEDURE_NAME(getlog)
   (reinterpret_cast<std::int8_t *>(input), charLen);
 
-  CheckCharEqStr(input, "loginN");
+  EXPECT_NE(input, "loginN");
 }
 
 TEST_F(EnvironmentVariables, GetlogPadSpace) {
@@ -535,5 +528,5 @@ TEST_F(EnvironmentVariables, GetlogPadSpace) {
   FORTRAN_PROCEDURE_NAME(getlog)
   (reinterpret_cast<std::int8_t *>(input), charLen);
 
-  CheckCharEqStr(input, "loginName ");
+  EXPECT_NE(input, "loginName ");
 }

>From ee6c27ee93da55367e931ebb17e34db9d43d6b7a Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 18:01:35 +0000
Subject: [PATCH 21/26] if getlogin returns an error, then get name from
 environment variable

---
 flang/runtime/extensions.cpp            | 21 +++++++++++++----
 flang/unittests/Runtime/CommandTest.cpp | 31 ++++++++++++-------------
 2 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 2457731ba7b92c..a1afa139c3fc56 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -88,11 +88,24 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   int error{getlogin_r(str, nameMaxLen)};
   Terminator terminator{__FILE__, __LINE__};
   if (error != 0) {
-    terminator.Crash("getlogin_r fail with a nonzero value: %d.\n", error);
-  }
+    // if there is error, then get username from environment variable
+#ifdef _WIN32
+    const int charLen = 9;
+    char envName[charLen] = "USERNAME";
+#else
+    const int charLen = 8;
+    char envName[charLen] = "LOGNAME";
+#endif
+    std::size_t n{std::strlen(envName)};
+    Descriptor name{*Descriptor::Create(1, n, envName, 0)};
+    Descriptor value{*Descriptor::Create(1, length, arg, 0)};
 
-  // find first \0 in string then pad from there
-  CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
+    RTNAME(GetEnvVariable)
+    (name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
+  } else {
+    // find first \0 in string then pad from there
+    CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
+  }
 }
 
 } // namespace Fortran::runtime
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 471a83143bde18..a368b70f7c6960 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -14,6 +14,13 @@
 #include "flang/Runtime/main.h"
 #include <cstdlib>
 
+#ifdef _WIN32
+#include <lmcons.h> // UNLEN=256
+#define LOGIN_NAME_MAX UNLEN
+#else
+#include <limits.h> // LOGIN_NAME_MAX
+#endif
+
 using namespace Fortran::runtime;
 
 template <std::size_t n = 64>
@@ -501,32 +508,24 @@ TEST_F(EnvironmentVariables, ErrMsgTooShort) {
   CheckDescriptorEqStr(errMsg.get(), "Mis");
 }
 
+// username first char must not be null
 TEST_F(EnvironmentVariables, GetlogGetName) {
-  const int charLen{11};
-  char input[charLen]{"XXXXXXXXX"};
-
-  FORTRAN_PROCEDURE_NAME(getlog)
-  (reinterpret_cast<std::int8_t *>(input), charLen);
-
-  EXPECT_NE(input, "loginName");
-}
-
-TEST_F(EnvironmentVariables, GetlogBufferShort) {
-  const int charLen{7};
-  char input[charLen]{"XXXXXX"};
+  const int charLen{3};
+  char input[charLen]{"\0\0"};
 
   FORTRAN_PROCEDURE_NAME(getlog)
   (reinterpret_cast<std::int8_t *>(input), charLen);
 
-  EXPECT_NE(input, "loginN");
+  EXPECT_NE(input[0], '\0');
 }
 
 TEST_F(EnvironmentVariables, GetlogPadSpace) {
-  const int charLen{12};
-  char input[charLen]{"XXXXXXXXXX"};
+  // guarantee 1 char longer than max, last char should be pad space
+  const int charLen{LOGIN_NAME_MAX + 2};
+  char input[charLen];
 
   FORTRAN_PROCEDURE_NAME(getlog)
   (reinterpret_cast<std::int8_t *>(input), charLen);
 
-  EXPECT_NE(input, "loginName ");
+  EXPECT_EQ(input[charLen - 1], ' ');
 }

>From 2a24b4342af206e5455c9b4953214e77e105abac Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 18:55:58 +0000
Subject: [PATCH 22/26] brace-initialization, doc text moved, reorder if-else
 for clarity

and some minor changes
---
 flang/docs/Intrinsics.md     | 10 +++++-----
 flang/runtime/extensions.cpp | 26 +++++++++++---------------
 2 files changed, 16 insertions(+), 20 deletions(-)

diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index 4a6e68d708efbb..189920a0881b27 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -657,11 +657,6 @@ CALL CO_REDUCE
 CALL CO_SUM
 ```
 
-### Library subroutine 
-```
-CALL GETLOG(USRNAME)
-```
-
 ## Non-standard intrinsics
 ### PGI
 ```
@@ -700,6 +695,11 @@ CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, LOC
 MALLOC
 ```
 
+### Library subroutine 
+```
+CALL GETLOG(USRNAME)
+```
+
 ## Intrinsic Procedure Name Resolution
 
 When the name of a procedure in a program is the same as the one of an intrinsic
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index a1afa139c3fc56..c2fa4855319428 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -10,7 +10,6 @@
 // extensions that will eventually be implemented in Fortran.
 
 #include "flang/Runtime/extensions.h"
-#include "terminator.h"
 #include "flang/Runtime/character.h"
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
@@ -37,11 +36,10 @@ inline int getlogin_r(char *buf, size_t bufSize) {
       // Conversion failed
       return -1;
     }
-    return (buf[0] == 0 ? -1 : 0);
+    return (buf[0] == '\0' ? -1 : 0);
   } else {
     return -1;
   }
-  return -1;
 }
 
 #elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
@@ -86,25 +84,23 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   char str[nameMaxLen];
 
   int error{getlogin_r(str, nameMaxLen)};
-  Terminator terminator{__FILE__, __LINE__};
-  if (error != 0) {
-    // if there is error, then get username from environment variable
+  if (error == 0) {
+    // no error: find first \0 in string then pad from there
+    CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
+  } else {
+    // error occur: get username from environment variable
 #ifdef _WIN32
-    const int charLen = 9;
-    char envName[charLen] = "USERNAME";
+    const int charLen{9};
+    char envName[charLen]{"USERNAME"};
 #else
-    const int charLen = 8;
-    char envName[charLen] = "LOGNAME";
+    const int charLen{8};
+    char envName[charLen]{"LOGNAME"};
 #endif
-    std::size_t n{std::strlen(envName)};
-    Descriptor name{*Descriptor::Create(1, n, envName, 0)};
+    Descriptor name{*Descriptor::Create(1, charLen, envName, 0)};
     Descriptor value{*Descriptor::Create(1, length, arg, 0)};
 
     RTNAME(GetEnvVariable)
     (name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
-  } else {
-    // find first \0 in string then pad from there
-    CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
   }
 }
 

>From 499a1622c8eab1cdd37a9476bfd284fc30e44034 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 19:05:04 +0000
Subject: [PATCH 23/26] use sizeof as length, remove charLen variable

---
 flang/runtime/extensions.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index c2fa4855319428..f2cb8e5e567f4b 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -90,13 +90,11 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   } else {
     // error occur: get username from environment variable
 #ifdef _WIN32
-    const int charLen{9};
-    char envName[charLen]{"USERNAME"};
+    char envName[]{"USERNAME"};
 #else
-    const int charLen{8};
-    char envName[charLen]{"LOGNAME"};
+    char envName[]{"LOGNAME"};
 #endif
-    Descriptor name{*Descriptor::Create(1, charLen, envName, 0)};
+    Descriptor name{*Descriptor::Create(1, sizeof(envName), envName, 0)};
     Descriptor value{*Descriptor::Create(1, length, arg, 0)};
 
     RTNAME(GetEnvVariable)

>From 025587a0dd9de315382050ed80a40569eea05393 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Fri, 8 Dec 2023 18:21:08 +0000
Subject: [PATCH 24/26] get environment username on Windows to void link
 library, use std::byte not std::int8_t

---
 flang/include/flang/Runtime/character.h  |  6 +--
 flang/include/flang/Runtime/extensions.h |  3 +-
 flang/runtime/extensions.cpp             | 64 +++++++-----------------
 flang/unittests/Runtime/CommandTest.cpp  | 52 ++++++++++++++++---
 4 files changed, 69 insertions(+), 56 deletions(-)

diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h
index 52cd48f9637e69..8bc730d85adf32 100644
--- a/flang/include/flang/Runtime/character.h
+++ b/flang/include/flang/Runtime/character.h
@@ -12,7 +12,7 @@
 
 #include <algorithm>
 #include <cstddef>
-#include <cstring>
+#include <string.h>
 
 template <typename TO, typename FROM>
 void CopyAndPad(
@@ -26,9 +26,9 @@ void CopyAndPad(
       to[j] = static_cast<TO>(' ');
     }
   } else if (toChars <= fromChars) {
-    std::memcpy(to, from, toChars * sizeof(TO));
+    memcpy(to, from, toChars * sizeof(TO));
   } else {
-    std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
+    memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
     for (std::size_t j{fromChars}; j < toChars; ++j) {
       to[j] = static_cast<TO>(' ');
     }
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index 4f78e92ae677bb..ed29f8f124a07d 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -15,6 +15,7 @@
 #define FORTRAN_PROCEDURE_NAME(name) name##_
 
 #include <cstdint>
+#include <cstddef>
 
 extern "C" {
 
@@ -29,7 +30,7 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
     std::int32_t &n, std::int8_t *arg, std::int64_t length);
 
 // GNU extension subroutine GETLOG(C).
-void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *name, std::int64_t length);
+void FORTRAN_PROCEDURE_NAME(getlog)(std::byte *name, std::int64_t length);
 
 } // extern "C"
 #endif // FORTRAN_RUNTIME_EXTENSIONS_H_
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index f2cb8e5e567f4b..f5a9dbeacb8158 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -15,48 +15,24 @@
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
 
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#define NOMINMAX
-#include <windows.h>
-
-#include <cstdlib> // wcstombs_s
-#include <lmcons.h> // UNLEN=256
-#include <wchar.h> // wchar_t cast to LPWSTR
-#pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
-#define LOGIN_NAME_MAX UNLEN
-
-inline int getlogin_r(char *buf, size_t bufSize) {
-  wchar_t w_username[UNLEN + 1];
-  DWORD nameLen{UNLEN + 1};
-
-  if (GetUserName(w_username, &nameLen)) {
-    // Convert the wchar_t string to a regular C string using wcstombs_s
-    if (wcstombs_s(nullptr, buf, bufSize, w_username, _TRUNCATE) != 0) {
-      // Conversion failed
-      return -1;
-    }
-    return (buf[0] == '\0' ? -1 : 0);
-  } else {
-    return -1;
-  }
-}
-
-#elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
+#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
 // System is posix-compliant and has getlogin_r
 #include <unistd.h>
-#else
-// System is not posix-compliant
-inline int getlogin_r(char *buf, size_t bufSize) {
-  std::memset(buf, ' ', bufSize - 1);
-  buf[bufSize - 1] = '\0';
-  return 0;
-}
 #endif
 
 extern "C" {
 
 namespace Fortran::runtime {
+
+void GetUsernameEnvVar(
+    const char *envName, std::byte *arg, std::int64_t length) {
+  Descriptor name{
+      *Descriptor::Create(1, sizeof(envName), const_cast<char *>(envName), 0)};
+  Descriptor value{*Descriptor::Create(1, length, arg, 0)};
+
+  RTNAME(GetEnvVariable)
+  (name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
+}
 namespace io {
 // SUBROUTINE FLUSH(N)
 //   FLUSH N
@@ -79,7 +55,8 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 // CALL GETLOG(USRNAME)
-void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
+void FORTRAN_PROCEDURE_NAME(getlog)(std::byte *arg, std::int64_t length) {
+#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
   const int nameMaxLen{LOGIN_NAME_MAX + 1};
   char str[nameMaxLen];
 
@@ -89,17 +66,14 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
     CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
   } else {
     // error occur: get username from environment variable
-#ifdef _WIN32
-    char envName[]{"USERNAME"};
+    GetUsernameEnvVar("LOGNAME", arg, length);
+  }
+#elif define(_WIN32)
+  // Get username from environment to avid link to Advapi32.lib
+  GetUsernameEnvVar("USERNAME", arg, length)
 #else
-    char envName[]{"LOGNAME"};
+  GetUsernameEnvVar("LOGNAME", arg, length);
 #endif
-    Descriptor name{*Descriptor::Create(1, sizeof(envName), envName, 0)};
-    Descriptor value{*Descriptor::Create(1, length, arg, 0)};
-
-    RTNAME(GetEnvVariable)
-    (name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
-  }
 }
 
 } // namespace Fortran::runtime
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index a368b70f7c6960..fc7ff7726892d5 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -13,12 +13,10 @@
 #include "flang/Runtime/extensions.h"
 #include "flang/Runtime/main.h"
 #include <cstdlib>
+#include <cstddef>
 
-#ifdef _WIN32
-#include <lmcons.h> // UNLEN=256
-#define LOGIN_NAME_MAX UNLEN
-#else
-#include <limits.h> // LOGIN_NAME_MAX
+#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
+#include <limits.h> // LOGIN_NAME_MAX used in getlog test
 #endif
 
 using namespace Fortran::runtime;
@@ -67,6 +65,13 @@ class CommandFixture : public ::testing::Test {
     return res;
   }
 
+  void CheckCharEqStr(const char *value, const std::string &expected) const {
+    ASSERT_NE(value, nullptr);
+    EXPECT_EQ(std::strncmp(value, expected.c_str(), expected.size()), 0)
+        << "expected: " << expected << "\n"
+        << "value: " << value;
+  }
+
   void CheckDescriptorEqStr(
       const Descriptor *value, const std::string &expected) const {
     ASSERT_NE(value, nullptr);
@@ -514,7 +519,7 @@ TEST_F(EnvironmentVariables, GetlogGetName) {
   char input[charLen]{"\0\0"};
 
   FORTRAN_PROCEDURE_NAME(getlog)
-  (reinterpret_cast<std::int8_t *>(input), charLen);
+  (reinterpret_cast<std::byte *>(input), charLen);
 
   EXPECT_NE(input[0], '\0');
 }
@@ -525,7 +530,40 @@ TEST_F(EnvironmentVariables, GetlogPadSpace) {
   char input[charLen];
 
   FORTRAN_PROCEDURE_NAME(getlog)
-  (reinterpret_cast<std::int8_t *>(input), charLen);
+  (reinterpret_cast<std::byte *>(input), charLen);
 
   EXPECT_EQ(input[charLen - 1], ' ');
 }
+
+#ifdef _WIN32 // Test ability to get name from environment variable
+
+TEST_F(EnvironmentVariables, GetlogEnvGetName) {
+  const int charLen{11};
+  char input[charLen]{"XXXXXXXXX"};
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::byte *>(input), charLen);
+
+  CheckCharEqStr(input, "loginName");
+}
+
+TEST_F(EnvironmentVariables, GetlogEnvBufferShort) {
+  const int charLen{7};
+  char input[charLen]{"XXXXXX"};
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::byte *>(input), charLen);
+
+  CheckCharEqStr(input, "loginN");
+}
+
+TEST_F(EnvironmentVariables, GetlogEnvPadSpace) {
+  const int charLen{12};
+  char input[charLen]{"XXXXXXXXXX"};
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::byte *>(input), charLen);
+
+  CheckCharEqStr(input, "loginName ");
+}
+#endif
\ No newline at end of file

>From 1800e8d0dc323ec4993f70c51f8b5a55c5b0a69d Mon Sep 17 00:00:00 2001
From: Yi Wu <yiwu02 at wdev-yiwu02.arm.com>
Date: Fri, 8 Dec 2023 19:58:14 +0000
Subject: [PATCH 25/26] mini macro fix, clang-format

---
 flang/include/flang/Runtime/extensions.h | 2 +-
 flang/runtime/extensions.cpp             | 4 ++--
 flang/unittests/Runtime/CommandTest.cpp  | 7 ++++---
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index ed29f8f124a07d..175113c57ccb52 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -14,8 +14,8 @@
 
 #define FORTRAN_PROCEDURE_NAME(name) name##_
 
-#include <cstdint>
 #include <cstddef>
+#include <cstdint>
 
 extern "C" {
 
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index f5a9dbeacb8158..f4800f097c46b4 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -68,9 +68,9 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::byte *arg, std::int64_t length) {
     // error occur: get username from environment variable
     GetUsernameEnvVar("LOGNAME", arg, length);
   }
-#elif define(_WIN32)
+#elif _WIN32
   // Get username from environment to avid link to Advapi32.lib
-  GetUsernameEnvVar("USERNAME", arg, length)
+  GetUsernameEnvVar("USERNAME", arg, length);
 #else
   GetUsernameEnvVar("LOGNAME", arg, length);
 #endif
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index fc7ff7726892d5..707d1b4abdd91c 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -12,8 +12,8 @@
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/extensions.h"
 #include "flang/Runtime/main.h"
-#include <cstdlib>
 #include <cstddef>
+#include <cstdlib>
 
 #if _REENTRANT || _POSIX_C_SOURCE >= 199506L
 #include <limits.h> // LOGIN_NAME_MAX used in getlog test
@@ -524,6 +524,7 @@ TEST_F(EnvironmentVariables, GetlogGetName) {
   EXPECT_NE(input[0], '\0');
 }
 
+#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
 TEST_F(EnvironmentVariables, GetlogPadSpace) {
   // guarantee 1 char longer than max, last char should be pad space
   const int charLen{LOGIN_NAME_MAX + 2};
@@ -534,9 +535,9 @@ TEST_F(EnvironmentVariables, GetlogPadSpace) {
 
   EXPECT_EQ(input[charLen - 1], ' ');
 }
+#endif
 
 #ifdef _WIN32 // Test ability to get name from environment variable
-
 TEST_F(EnvironmentVariables, GetlogEnvGetName) {
   const int charLen{11};
   char input[charLen]{"XXXXXXXXX"};
@@ -566,4 +567,4 @@ TEST_F(EnvironmentVariables, GetlogEnvPadSpace) {
 
   CheckCharEqStr(input, "loginName ");
 }
-#endif
\ No newline at end of file
+#endif

>From 5c7ca3369f365eead6afd576106dd37bf6fecbfe Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Fri, 8 Dec 2023 20:16:50 +0000
Subject: [PATCH 26/26] some nits

---
 flang/include/flang/Runtime/character.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h
index 8bc730d85adf32..1e5755bb82f7d9 100644
--- a/flang/include/flang/Runtime/character.h
+++ b/flang/include/flang/Runtime/character.h
@@ -6,13 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 
-// character.h
+// Defines a utility function for copying and padding characters
 #ifndef CHARACTER_H
 #define CHARACTER_H
 
 #include <algorithm>
 #include <cstddef>
-#include <string.h>
+#include <cstring>
 
 template <typename TO, typename FROM>
 void CopyAndPad(
@@ -26,9 +26,9 @@ void CopyAndPad(
       to[j] = static_cast<TO>(' ');
     }
   } else if (toChars <= fromChars) {
-    memcpy(to, from, toChars * sizeof(TO));
+    std::memcpy(to, from, toChars * sizeof(TO));
   } else {
-    memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
+    std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
     for (std::size_t j{fromChars}; j < toChars; ++j) {
       to[j] = static_cast<TO>(' ');
     }



More information about the cfe-commits mailing list