Skip to content

Commit

Permalink
compatible with some /usr/bin/env flags
Browse files Browse the repository at this point in the history
  • Loading branch information
liberize committed Dec 8, 2024
1 parent c34af29 commit 012aac4
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 60 deletions.
140 changes: 80 additions & 60 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#endif

enum ScriptFormat {
UNKNOWN,
SHELL,
PYTHON,
PERL,
Expand Down Expand Up @@ -106,6 +107,7 @@ int main(int argc, char* argv[]) {
#elif defined(MOUNT_SQUASHFS)
base_dir = mount_dir = mount_squashfs();
#endif

setenv(OBF("SSC_EXTRACT_DIR"), extract_dir.c_str(), 1);
setenv(OBF("SSC_MOUNT_DIR"), mount_dir.c_str(), 1);
setenv(OBF("SSC_EXECUTABLE_PATH"), exe_path.c_str(), 1);
Expand All @@ -114,21 +116,8 @@ int main(int argc, char* argv[]) {
std::string file_name = OBF(R"SSC(SCRIPT_FILE_NAME)SSC");
std::string shebang = OBF(R"SSC(SCRIPT_SHEBANG)SSC");

#ifdef __APPLE__
auto buf = read_data_sect("s");
if (buf.empty())
exit(1);
char* script_data = buf.data();
int script_len = buf.size();
#else
extern char _binary_s_start;
extern char _binary_s_end;
char* script_data = &_binary_s_start;
int script_len = &_binary_s_end - &_binary_s_start;
#endif

// detect script format by file name suffix
ScriptFormat format = SHELL;
ScriptFormat format = UNKNOWN;
std::string shell("sh");
auto pos = file_name.find_last_of(".");
if (pos != std::string::npos) {
Expand Down Expand Up @@ -158,65 +147,83 @@ int main(int argc, char* argv[]) {

std::vector<std::string> args;
// parse shebang
if (!shebang.empty()) {
if (shebang.empty()) {
switch (format) {
case SHELL: args.emplace_back(shell); break;
case PYTHON: args.emplace_back("python"); break;
case PERL: args.emplace_back("perl"); break;
case JAVASCRIPT: args.emplace_back("node"); break;
case RUBY: args.emplace_back("ruby"); break;
case PHP: args.emplace_back("php"); break;
case R: args.emplace_back("Rscript"); break;
case LUA: args.emplace_back("lua"); break;
default: break;
}
} else {
wordexp_t wrde;
if (wordexp(shebang.c_str() + 2, &wrde, 0) != 0) {
LOGE("failed to parse shebang!");
return 1;
}
for (size_t i = 0; i < wrde.we_wordc; i++) {
bool env = wrde.we_wordc && (!strcmp(wrde.we_wordv[0], "env") || !strcmp(wrde.we_wordv[0], "/usr/bin/env"));
for (size_t i = env; i < wrde.we_wordc; i++) {
auto s = wrde.we_wordv[i];
if (args.empty()) {
if (!strcmp(s, "env") || !strcmp(s, "/usr/bin/env")) {
continue;
}
auto p = s;
while (*p == '_' || isalnum(*p)) {
++p;
}
if (*p == '=') {
std::string name(s, p - s);
setenv(name.c_str(), ++p, 1);
continue;
if (!args.empty()) {
args.emplace_back(s);
continue;
}
if (env && s[0] == '-') {
if (!strcmp(s, "-S") || !strcmp(s, "--split-string")) {
// do nothing
} else if (!strcmp(s, "-i") || !strcmp(s, "-") || !strcmp(s, "--ignore-environment")) {
extern char **environ;
environ = (char**) calloc(1, sizeof(*environ));
} else if ((!strcmp(s, "-C") || !strcmp(s, "--chdir")) && i + 1 < wrde.we_wordc) {
chdir(wrde.we_wordv[++i]);
} else if (!strncmp(s, "--chdir=", 8)) {
chdir(s + 8);
} else if ((!strcmp(s, "-u") || !strcmp(s, "--unset")) && i + 1 < wrde.we_wordc) {
unsetenv(wrde.we_wordv[++i]);
} else if (!strncmp(s, "--unset=", 8)) {
unsetenv(s + 8);
}
continue;
}
auto p = s;
while (*p == '_' || isalnum(*p)) {
++p;
}
if (*p == '=') {
std::string name(s, p - s);
setenv(name.c_str(), ++p, 1);
continue;
}
args.emplace_back(s);
}
wordfree(&wrde);
if (args.empty()) {
LOGE("invalid shebang!");
return 1;
}

// detect script format by shebang
if (!args.empty()) {
if (str_ends_with(args[0], "sh")) {
format = SHELL;
shell = base_name(args[0]);
} else if (args[0].find("python") != std::string::npos || args[0].find("conda") != std::string::npos) {
format = PYTHON;
} else if (args[0].find("perl") != std::string::npos) {
format = PERL;
} else if (args[0].find("node") != std::string::npos) {
format = JAVASCRIPT;
} else if (args[0].find("ruby") != std::string::npos) {
format = RUBY;
} else if (args[0].find("php") != std::string::npos) {
format = PHP;
} else if (args[0].find("Rscript") != std::string::npos) {
format = R;
} else if (args[0].find("lua") != std::string::npos) {
format = LUA;
}
}
}
if (args.empty()) {
switch (format) {
case SHELL: args.emplace_back(shell); break;
case PYTHON: args.emplace_back("python"); break;
case PERL: args.emplace_back("perl"); break;
case JAVASCRIPT: args.emplace_back("node"); break;
case RUBY: args.emplace_back("ruby"); break;
case PHP: args.emplace_back("php"); break;
case R: args.emplace_back("Rscript"); break;
case LUA: args.emplace_back("lua"); break;
default: LOGE("unknown format!"); return 4;
if (str_ends_with(args[0], "sh")) {
format = SHELL;
shell = base_name(args[0]);
} else if (str_contains_word(args[0], "python") || str_contains_word(args[0], "conda")) {
format = PYTHON;
} else if (str_contains_word(args[0], "perl")) {
format = PERL;
} else if (str_contains_word(args[0], "node") || str_contains_word(args[0], "deno") || str_contains_word(args[0], "bun")) {
format = JAVASCRIPT;
} else if (str_contains_word(args[0], "ruby")) {
format = RUBY;
} else if (str_contains_word(args[0], "php")) {
format = PHP;
} else if (str_contains_word(args[0], "Rscript")) {
format = R;
} else if (str_contains_word(args[0], "lua")) {
format = LUA;
}
}
if (interpreter_path.empty()) {
Expand Down Expand Up @@ -367,6 +374,19 @@ int main(int argc, char* argv[]) {
write(fd, "\n", 1);
#endif

#ifdef __APPLE__
auto buf = read_data_sect("s");
if (buf.empty())
exit(1);
char* script_data = buf.data();
int script_len = buf.size();
#else
extern char _binary_s_start;
extern char _binary_s_end;
char* script_data = &_binary_s_start;
int script_len = &_binary_s_end - &_binary_s_start;
#endif

int n = SEGMENT;
n = std::max(std::min(n, script_len), 1);
int max_seg_len = (script_len + n - 1) / n;
Expand Down
12 changes: 12 additions & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ FORCE_INLINE bool str_ends_with(const std::string& s, const std::string& e) {
return s.size() >= e.size() && s.compare(s.size() - e.size(), e.size(), e) == 0;
}

FORCE_INLINE bool str_contains_word(const std::string& s, const std::string& w) {
auto p = s.find(w);
if (p == std::string::npos)
return false;
if (p && isalpha(s[p-1]))
return false;
p += w.size();
if (p < s.size() && isalpha(s[p]))
return false;
return true;
}

FORCE_INLINE std::string str_replace_all(std::string str, const std::string& from, const std::string& to) {
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
Expand Down

0 comments on commit 012aac4

Please sign in to comment.