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 n'th argument. Assumes \p n is valid.
28 static std::int64_t ArgumentLength(std::int32_t n) {
29   std::size_t length{std::strlen(executionEnvironment.argv[n])};
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     return 0;
42   }
43 
44   return ArgumentLength(n);
45 }
46 
47 static bool IsValidCharDescriptor(const Descriptor *value) {
48   return value && value->IsAllocated() &&
49       value->type() == TypeCode(TypeCategory::Character, 1) &&
50       value->rank() == 0;
51 }
52 
53 static void FillWithSpaces(const Descriptor *value) {
54   std::memset(value->OffsetElement(), ' ', value->ElementBytes());
55 }
56 
57 std::int32_t RTNAME(ArgumentValue)(
58     std::int32_t n, const Descriptor *value, const Descriptor *errmsg) {
59   if (IsValidCharDescriptor(value)) {
60     FillWithSpaces(value);
61   }
62 
63   if (n < 0 || n >= executionEnvironment.argc) {
64     return ToErrmsg(errmsg, StatInvalidArgumentNumber);
65   }
66 
67   if (IsValidCharDescriptor(value)) {
68     std::int64_t argLen{ArgumentLength(n)};
69     if (argLen <= 0) {
70       return ToErrmsg(errmsg, StatMissingArgument);
71     }
72 
73     std::int64_t toCopy{
74         std::min(argLen, static_cast<std::int64_t>(value->ElementBytes()))};
75     std::memcpy(value->OffsetElement(), executionEnvironment.argv[n], toCopy);
76 
77     if (argLen > toCopy) {
78       return ToErrmsg(errmsg, StatValueTooShort);
79     }
80   }
81 
82   return StatOk;
83 }
84 
85 static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
86   std::size_t s{d.ElementBytes() - 1};
87   while (*d.OffsetElement(s) == ' ') {
88     --s;
89   }
90   return s + 1;
91 }
92 
93 std::int64_t RTNAME(EnvVariableLength)(
94     const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
95   std::size_t nameLength{
96       trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()};
97   if (nameLength == 0) {
98     return 0;
99   }
100 
101   Terminator terminator{sourceFile, line};
102   const char *value{executionEnvironment.GetEnv(
103       name.OffsetElement(), nameLength, terminator)};
104   if (!value) {
105     return 0;
106   }
107   return std::strlen(value);
108 }
109 } // namespace Fortran::runtime
110