Index: flang/runtime/command.cpp =================================================================== --- flang/runtime/command.cpp +++ flang/runtime/command.cpp @@ -51,16 +51,16 @@ value->rank() == 0; } -static void FillWithSpaces(const Descriptor *value) { - std::memset(value->OffsetElement(), ' ', value->ElementBytes()); +static void FillWithSpaces(const Descriptor &value, std::size_t offset = 0) { + std::memset(value.OffsetElement(offset), ' ', value.ElementBytes() - offset); } static std::int32_t CopyToDescriptor(const Descriptor &value, - const char *rawValue, std::int64_t rawValueLength, - const Descriptor *errmsg) { - std::int64_t toCopy{std::min( - rawValueLength, static_cast(value.ElementBytes()))}; - std::memcpy(value.OffsetElement(), rawValue, toCopy); + const char *rawValue, std::int64_t rawValueLength, const Descriptor *errmsg, + std::size_t offset = 0) { + std::int64_t toCopy{std::min(rawValueLength, + static_cast(value.ElementBytes() - offset))}; + std::memcpy(value.OffsetElement(offset), rawValue, toCopy); if (rawValueLength > toCopy) { return ToErrmsg(errmsg, StatValueTooShort); @@ -69,10 +69,33 @@ return StatOk; } +static std::int32_t CheckAndCopyToDescriptor(const Descriptor *value, + const char *rawValue, const Descriptor *errmsg, std::size_t &offset) { + bool haveValue{IsValidCharDescriptor(value)}; + + std::int64_t len{StringLength(rawValue)}; + if (len <= 0) { + if (haveValue) { + FillWithSpaces(*value); + } + return ToErrmsg(errmsg, StatMissingArgument); + } + + if (haveValue) { + std::int32_t stat{CopyToDescriptor(*value, rawValue, len, errmsg, offset)}; + if (stat != StatOk) { + return stat; + } + offset += len; + } + + return StatOk; +} + std::int32_t RTNAME(ArgumentValue)( std::int32_t n, const Descriptor *value, const Descriptor *errmsg) { if (IsValidCharDescriptor(value)) { - FillWithSpaces(value); + FillWithSpaces(*value); } if (n < 0 || n >= executionEnvironment.argc) { @@ -92,6 +115,57 @@ return StatOk; } +std::int32_t RTNAME(CommandValue)( + const Descriptor *value, const Descriptor *errmsg) { + bool haveValue{IsValidCharDescriptor(value)}; + + std::size_t offset{0}; + + // value = argv[0] + std::int32_t stat{CheckAndCopyToDescriptor( + value, executionEnvironment.argv[0], errmsg, offset)}; + if (stat != StatOk) { + return stat; + } + + // value += " " + argv[1:n] + for (std::int32_t i{1}; i < executionEnvironment.argc; ++i) { + if (haveValue) { + stat = CopyToDescriptor(*value, " ", 1, errmsg, offset); + if (stat != StatOk) { + return stat; + } + offset++; + } + + stat = CheckAndCopyToDescriptor( + value, executionEnvironment.argv[i], errmsg, offset); + if (stat != StatOk) { + return stat; + } + } + + // value += spaces for padding + if (haveValue) { + FillWithSpaces(*value, offset); + } + + return StatOk; +} + +std::int64_t RTNAME(CommandLength)() { + std::int64_t length{0}; + for (int i = 0; i < executionEnvironment.argc; i++) { + std::int64_t argLen{StringLength(executionEnvironment.argv[i])}; + if (argLen <= 0) { + return 0; + } + length += argLen; + } + // Also add the spaces between arguments. + return length + executionEnvironment.argc - 1; +} + static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) { std::size_t s{d.ElementBytes() - 1}; while (*d.OffsetElement(s) == ' ') { @@ -118,7 +192,7 @@ const Descriptor *value, bool trim_name, const Descriptor *errmsg, const char *sourceFile, int line) { if (IsValidCharDescriptor(value)) { - FillWithSpaces(value); + FillWithSpaces(*value); } const char *rawValue{GetEnvVariableValue(name, trim_name, sourceFile, line)}; Index: flang/unittests/Runtime/CommandTest.cpp =================================================================== --- flang/unittests/Runtime/CommandTest.cpp +++ flang/unittests/Runtime/CommandTest.cpp @@ -86,6 +86,20 @@ expectedValue); } + void CheckCommandValue(const char *args[], int n) const { + SCOPED_TRACE("Checking command:"); + ASSERT_GE(n, 1); + std::string expectedValue{args[0]}; + for (int i = 1; i < n; i++) { + expectedValue += " " + std::string{args[i]}; + } + CheckValue( + [&](const Descriptor *value, const Descriptor *errmsg) { + return RTNAME(CommandValue)(value, errmsg); + }, + expectedValue.c_str()); + } + void CheckEnvVarValue( const char *expectedValue, const char *name, bool trimName = true) const { SCOPED_TRACE(name); @@ -131,6 +145,23 @@ CheckDescriptorEqStr(err.get(), paddedErrStr); } } + + void CheckMissingCommandValue(const char *errStr = nullptr) const { + OwningPtr value{CreateEmptyCharDescriptor()}; + ASSERT_NE(value, nullptr); + + OwningPtr err{errStr ? CreateEmptyCharDescriptor() : nullptr}; + + EXPECT_GT(RTNAME(CommandValue)(value.get(), err.get()), 0); + + std::string spaces(value->ElementBytes(), ' '); + CheckDescriptorEqStr(value.get(), spaces); + + if (errStr) { + std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes())); + CheckDescriptorEqStr(err.get(), paddedErrStr); + } + } }; static const char *commandOnlyArgv[]{"aProgram"}; @@ -151,6 +182,10 @@ CheckArgumentValue(commandOnlyArgv[0], 0); } +TEST_F(ZeroArguments, CommandLength) { EXPECT_EQ(8, RTNAME(CommandLength())); } + +TEST_F(ZeroArguments, CommandValue) { CheckCommandValue(commandOnlyArgv, 1); } + static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"}; class OneArgument : public CommandFixture { protected: @@ -171,6 +206,10 @@ CheckArgumentValue(oneArgArgv[1], 1); } +TEST_F(OneArgument, CommandLength) { EXPECT_EQ(29, RTNAME(CommandLength)()); } + +TEST_F(OneArgument, CommandValue) { CheckCommandValue(oneArgArgv, 2); } + static const char *severalArgsArgv[]{ "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"}; class SeveralArguments : public CommandFixture { @@ -215,7 +254,7 @@ CheckMissingArgumentValue(5); } -TEST_F(SeveralArguments, ValueTooShort) { +TEST_F(SeveralArguments, ArgValueTooShort) { OwningPtr tooShort{CreateEmptyCharDescriptor<15>()}; ASSERT_NE(tooShort, nullptr); EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1); @@ -231,12 +270,70 @@ CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); } -TEST_F(SeveralArguments, ErrMsgTooShort) { +TEST_F(SeveralArguments, ArgErrMsgTooShort) { OwningPtr errMsg{CreateEmptyCharDescriptor<3>()}; EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0); CheckDescriptorEqStr(errMsg.get(), "Inv"); } +TEST_F(SeveralArguments, CommandLength) { + EXPECT_EQ(0, RTNAME(CommandLength)()); +} + +TEST_F(SeveralArguments, CommandValue) { + CheckMissingCommandValue(); + CheckMissingCommandValue("Missing argument"); +} + +TEST_F(SeveralArguments, CommandErrMsgTooShort) { + OwningPtr value{CreateEmptyCharDescriptor()}; + OwningPtr errMsg{CreateEmptyCharDescriptor<3>()}; + + EXPECT_GT(RTNAME(CommandValue)(value.get(), errMsg.get()), 0); + + std::string spaces(value->ElementBytes(), ' '); + CheckDescriptorEqStr(value.get(), spaces); + CheckDescriptorEqStr(errMsg.get(), "Mis"); +} + +TEST_F(SeveralArguments, CommandValueCanTakeNull) { + EXPECT_GT(RTNAME(CommandValue)(nullptr, nullptr), 0); +} + +static const char *onlyValidArgsArgv[]{ + "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"}; +class OnlyValidArguments : public CommandFixture { +protected: + OnlyValidArguments() + : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv), + onlyValidArgsArgv) {} +}; + +TEST_F(OnlyValidArguments, CommandValue) { + CheckCommandValue(onlyValidArgsArgv, 4); +} + +TEST_F(OnlyValidArguments, CommandValueTooShort) { + OwningPtr tooShort{CreateEmptyCharDescriptor<50>()}; + ASSERT_NE(tooShort, nullptr); + EXPECT_EQ(RTNAME(CommandValue)(tooShort.get(), nullptr), -1); + CheckDescriptorEqStr( + tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe"); + + OwningPtr errMsg{CreateEmptyCharDescriptor()}; + ASSERT_NE(errMsg, nullptr); + + EXPECT_EQ(-1, RTNAME(CommandValue)(tooShort.get(), errMsg.get())); + + std::string expectedErrMsg{ + GetPaddedStr("Value too short", errMsg->ElementBytes())}; + CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); +} + +TEST_F(OnlyValidArguments, CommandValueCanTakeNull) { + EXPECT_EQ(0, RTNAME(CommandValue)(nullptr, nullptr)); +} + class EnvironmentVariables : public CommandFixture { protected: EnvironmentVariables() : CommandFixture(0, nullptr) {