1 //===-- runtime/command.cpp -----------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "flang/Runtime/command.h"
10 #include "environment.h"
11 #include "stat.h"
12 #include "terminator.h"
13 #include "flang/Runtime/descriptor.h"
14 #include <cstdlib>
15 #include <limits>
16 
17 namespace Fortran::runtime {
18 std::int32_t RTNAME(ArgumentCount)() {
19   int argc{executionEnvironment.argc};
20   if (argc > 1) {
21     // C counts the command name as one of the arguments, but Fortran doesn't.
22     return argc - 1;
23   }
24   return 0;
25 }
26 
27 // Returns the length of the \p string. Assumes \p string is valid.
28 static std::int64_t StringLength(const char *string) {
29   std::size_t length{std::strlen(string)};
30   if constexpr (sizeof(std::size_t) <= sizeof(std::int64_t)) {
31     return static_cast<std::int64_t>(length);
32   } else {
33     std::size_t max{std::numeric_limits<std::int64_t>::max()};
34     return length > max ? 0 // Just fail.
35                         : static_cast<std::int64_t>(length);
36   }
37 }
38 
39 std::int64_t RTNAME(ArgumentLength)(std::int32_t n) {
40   if (n < 0 || n >= executionEnvironment.argc ||
41       !executionEnvironment.argv[n]) {
42     return 0;
43   }
44 
45   return StringLength(executionEnvironment.argv[n]);
46 }
47 
48 static bool IsValidCharDescriptor(const Descriptor *value) {
49   return value && value->IsAllocated() &&
50       value->type() == TypeCode(TypeCategory::Character, 1) &&
51       value->rank() == 0;
52 }
53 
54 static void FillWithSpaces(const Descriptor *value) {
55   std::memset(value->OffsetElement(), ' ', value->ElementBytes());
56 }
57 
58 static std::int32_t CopyToDescriptor(const Descriptor &value,
59     const char *rawValue, std::int64_t rawValueLength,
60     const Descriptor *errmsg) {
61   std::int64_t toCopy{std::min(
62       rawValueLength, static_cast<std::int64_t>(value.ElementBytes()))};
63   std::memcpy(value.OffsetElement(), rawValue, toCopy);
64 
65   if (rawValueLength > toCopy) {
66     return ToErrmsg(errmsg, StatValueTooShort);
67   }
68 
69   return StatOk;
70 }
71 
72 std::int32_t RTNAME(ArgumentValue)(
73     std::int32_t n, const Descriptor *value, const Descriptor *errmsg) {
74   if (IsValidCharDescriptor(value)) {
75     FillWithSpaces(value);
76   }
77 
78   if (n < 0 || n >= executionEnvironment.argc) {
79     return ToErrmsg(errmsg, StatInvalidArgumentNumber);
80   }
81 
82   if (IsValidCharDescriptor(value)) {
83     const char *arg{executionEnvironment.argv[n]};
84     std::int64_t argLen{StringLength(arg)};
85     if (argLen <= 0) {
86       return ToErrmsg(errmsg, StatMissingArgument);
87     }
88 
89     return CopyToDescriptor(*value, arg, argLen, errmsg);
90   }
91 
92   return StatOk;
93 }
94 
95 static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
96   std::size_t s{d.ElementBytes() - 1};
97   while (*d.OffsetElement(s) == ' ') {
98     --s;
99   }
100   return s + 1;
101 }
102 
103 static const char *GetEnvVariableValue(
104     const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
105   std::size_t nameLength{
106       trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()};
107   if (nameLength == 0) {
108     return nullptr;
109   }
110 
111   Terminator terminator{sourceFile, line};
112   const char *value{executionEnvironment.GetEnv(
113       name.OffsetElement(), nameLength, terminator)};
114   return value;
115 }
116 
117 std::int32_t RTNAME(EnvVariableValue)(const Descriptor &name,
118     const Descriptor *value, bool trim_name, const Descriptor *errmsg,
119     const char *sourceFile, int line) {
120   if (IsValidCharDescriptor(value)) {
121     FillWithSpaces(value);
122   }
123 
124   const char *rawValue{GetEnvVariableValue(name, trim_name, sourceFile, line)};
125   if (!rawValue) {
126     return ToErrmsg(errmsg, StatMissingEnvVariable);
127   }
128 
129   if (IsValidCharDescriptor(value)) {
130     return CopyToDescriptor(*value, rawValue, StringLength(rawValue), errmsg);
131   }
132 
133   return StatOk;
134 }
135 
136 std::int64_t RTNAME(EnvVariableLength)(
137     const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
138   const char *value{GetEnvVariableValue(name, trim_name, sourceFile, line)};
139   if (!value) {
140     return 0;
141   }
142   return StringLength(value);
143 }
144 } // namespace Fortran::runtime
145