summaryrefslogtreecommitdiff
path: root/indra/llcommon/scriptcommand.cpp
blob: 79afbc2063b7eda51d80e1809b54f53e4e0acbf2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
 * @file   scriptcommand.cpp
 * @author Nat Goodspeed
 * @date   2024-09-16
 * @brief  Implementation for scriptcommand.
 *
 * $LicenseInfo:firstyear=2024&license=viewerlgpl$
 * Copyright (c) 2024, Linden Research, Inc.
 * $/LicenseInfo$
 */

// Precompiled header
#include "linden_common.h"
// associated header
#include "scriptcommand.h"
// STL headers
// std headers
#include <sstream>
// external library headers
// other Linden headers
#include "fsyspath.h"
#include "llerror.h"
#include "llsdutil.h"
#include "llstring.h"
#include "stringize.h"

ScriptCommand::ScriptCommand(const std::string& command, const LLSD& path,
                             const std::string& base)
{
    fsyspath basepath(base);
    // Use LLStringUtil::getTokens() to parse the script command line
    args = LLStringUtil::getTokens(command,
                                   " \t\r\n", // drop_delims
                                   "",        // no keep_delims
                                   "\"'",     // either kind of quotes
                                   "\\");     // backslash escape
    // search for args[0] on paths
    if (search(args[0], path, basepath))
    {
        // The first token is in fact the script filename. Now that we've
        // found the script file, we've consumed that token. The rest are
        // command-line arguments.
        args.erase(args.begin());
        return;
    }

    // Parsing the command line produced a script file path we can't find.
    // Maybe that's because there are spaces in the original pathname that
    // were neither quoted nor escaped? See if we can find the whole original
    // command line string.
    if (search(command, path, basepath))
    {
        // Here we found script, using the whole input command line as its
        // pathname. Discard any parts of it we mistook for command-line
        // arguments.
        args.clear();
        return;
    }

    // Couldn't find the script either way. Is it because we can't even check
    // existence?
    if (! mError.empty())
    {
        return;
    }

    // No, existence check works, we just can't find the script.
    std::ostringstream msgstream;
    msgstream << "Can't find script file " << std::quoted(args[0]);
    if (command != args[0])
    {
        msgstream << " or " << std::quoted(command);
    }
    if (path.size() > 0)
    {
        msgstream << " on " << path;
    }
    if (! base.empty())
    {
        msgstream << " relative to " << base;
    }
    mError = msgstream.str();
    LL_WARNS("Lua") << mError << LL_ENDL;
}

bool ScriptCommand::search(const fsyspath& script, const LLSD& paths, const fsyspath& base)
{
    for (const auto& path : llsd::inArray(paths))
    {
        // If a path is already absolute, (otherpath / path) preserves it.
        // Explicitly instantiate fsyspath for every string conversion to
        // properly convert UTF-8 filename strings on Windows.
        fsyspath absscript{ base / fsyspath(path.asString()) / script };
        bool exists;
        try
        {
            exists = std::filesystem::exists(absscript);
        }
        catch (const std::filesystem::filesystem_error& exc)
        {
            mError = stringize("Can't check existence: ", exc.what());
            LL_WARNS("Lua") << mError << LL_ENDL;
            return false;
        }
        if (exists)
        {
            this->script = absscript.string();
            return true;
        }
    }
    return false;
}

std::string ScriptCommand::error() const
{
    return mError;
}