diff --git a/README.md b/README.md
index b66cb82..ee499c2 100644
--- a/README.md
+++ b/README.md
@@ -16,9 +16,23 @@ These commands can be placed anywhere within the script like regular commands.
## Other features
+### Output generated code
+
+Output the generated shell code to stdout with either:
+- `-o` option
+- shebang other than lxsh
+
+> Redirect stdout to a file to create a script file.
+> The resulting script is not dependent on lxsh
+
### Live execution
-Execute an extended shell script directly with the `-e` option.
+Directly execute an extended shell script with either
+- `-e` option
+- shebang is lxsh
+
+> Direct execution introduces direct dependency on lxsh and overhead,
+> therefore it should be avoided outside of development use
### Minimize code
@@ -30,5 +44,6 @@ lxsh should currently fully support POSIX syntax.
A POSIX shell script should give a working output.
Some specific features are missing:
-- link commands inside arithmetics (`$(())`) are not resolved
+- link commands in subshells inside arithmetics are not resolved
+- arithmetics cannot be minimized
- link commands placed on the same line as keywords `if`, `then`, `elif`, `else`, `for`, `while`, `do` or `done` are not resolved
diff --git a/include/util.hpp b/include/util.hpp
index f4f6362..8f7dd27 100644
--- a/include/util.hpp
+++ b/include/util.hpp
@@ -34,7 +34,7 @@ std::string delete_brackets(std::string const& in);
std::string pwd();
-void _exec(std::string const& bin, std::vector const& args);
+int _exec(std::string const& bin, std::vector const& args);
std::string stringReplace(std::string subject, const std::string& search, const std::string& replace);
diff --git a/src/main.cpp b/src/main.cpp
index cb43f9b..cb83856 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -12,6 +12,48 @@
#include "parse.hpp"
#include "options.hpp"
+int execute(block& sh, std::vector& args)
+{
+ std::string data=sh.generate();
+
+ std::string filename=ztd::exec("basename", args[0]).first;
+ filename.pop_back();
+
+ // generate path
+ std::string tmpdir = (getenv("TMPDIR") != NULL) ? getenv("TMPDIR") : "/tmp" ;
+ std::string dirpath = tmpdir + "/lxsh_" + ztd::sh("tr -dc '[:alnum:]' < /dev/urandom | head -c10");
+ std::string filepath = dirpath+'/'+filename;
+
+ // create dir
+ if(ztd::exec("mkdir", "-p", dirpath).second)
+ {
+ throw std::runtime_error("Failed to create directory '"+dirpath+'\'');
+ }
+
+ // create stream
+ std::ofstream stream(filepath);
+ if(!stream)
+ {
+ ztd::exec("rm", "-rf", dirpath);
+ throw std::runtime_error("Failed to write to '"+filepath+'\'');
+ }
+
+ // output
+ stream << data;
+ stream.close();
+ if(ztd::exec("chmod", "+x", filepath).second != 0)
+ {
+ ztd::exec("rm", "-rf", dirpath);
+ throw std::runtime_error("Failed to make '"+filepath+"' executable");
+ }
+
+ // exec
+ int retval=_exec(filepath, args);
+ ztd::exec("rm", "-rf", dirpath);
+
+ return retval;
+}
+
int main(int argc, char* argv[])
{
auto args=options.process(argc, argv, false, true);
@@ -59,37 +101,36 @@ int main(int argc, char* argv[])
}
}
+
g_origin=file;
add_include(file);
try
{
block sh(parse(import_file(file)));
+ std::string curbin, binshebang;
+ curbin=ztd::exec("basename", argv[0]).first;
+ binshebang=ztd::exec("basename", sh.shebang).first;
+ if(binshebang==curbin)
+ sh.shebang="#!/bin/sh";
if(options['e'])
{
- std::string data=sh.generate();
- // generate path
- std::string tmpdir = (getenv("TMPDIR") != NULL) ? getenv("TMPDIR") : "/tmp" ;
- std::string filepath = tmpdir + "/lxsh_exec_" + ztd::sh("tr -dc '[:alnum:]' < /dev/urandom | head -c10");
- // create stream
- std::ofstream stream(filepath);
- if(!stream)
- throw std::runtime_error("Failed to write to file '"+filepath+'\'');
-
- // output
- stream << data;
- stream.close();
- auto p = ztd::exec("chmod", "+x", filepath);
- if(p.second != 0)
- return p.second;
-
- args.erase(args.begin());
- _exec(filepath, args);
-
+ return execute(sh, args);
+ }
+ else if(options['o'])
+ {
+ std::cout << sh.generate();
}
else
{
- std::cout << sh.generate();
+ if(binshebang == curbin)
+ {
+ return execute(sh, args);
+ }
+ else
+ {
+ std::cout << sh.generate();
+ }
}
}
catch(ztd::format_error& e)
diff --git a/src/options.cpp b/src/options.cpp
index 33b09d9..a48e218 100644
--- a/src/options.cpp
+++ b/src/options.cpp
@@ -9,7 +9,8 @@ ztd::option_set gen_options()
ztd::option_set ret;
ret.add(ztd::option('h', "help", false, "Display this help message"));
ret.add(ztd::option('m', "minimize", false, "Minimize code"));
- ret.add(ztd::option('e', "exec", false, "Directly exec instead of outputting"));
+ ret.add(ztd::option('e', "exec", false, "Directly execute script"));
+ ret.add(ztd::option('o', "output", false, "Output result script to stdout"));
ret.add(ztd::option("help-commands", false, "Print help for linker commands"));
return ret;
}
diff --git a/src/util.cpp b/src/util.cpp
index 76197a7..88d9a48 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -1,6 +1,10 @@
#include "util.hpp"
#include
+#include
+#include
+
+#include
#include
@@ -69,14 +73,40 @@ std::string pwd()
return std::string(buf);
}
-void _exec(std::string const& bin, std::vector const& args)
+int _exec(std::string const& bin, std::vector const& args)
{
std::vector rargs;
- rargs.push_back((char*) bin.c_str());
for(auto it=args.begin(); it!=args.end(); it++)
rargs.push_back((char*) it->c_str());
rargs.push_back(NULL);
- execvp(bin.c_str(), rargs.data());
+
+ pid_t pid;
+
+ // forking
+ if((pid = fork()) == -1)
+ {
+ perror("fork");
+ exit(1);
+ }
+ // child process
+ if(pid == 0)
+ {
+ setpgid(pid, pid); //Needed so negative PIDs can kill children of /bin/sh
+ execvp(bin.c_str(), rargs.data());
+ exit(1); // exec didn't work
+ }
+
+ int stat;
+ // wait for end and get return value
+ while (waitpid(pid, &stat, 0) == -1)
+ {
+ if (errno != EINTR)
+ {
+ stat = -1;
+ break;
+ }
+ }
+ return stat;
}
std::string stringReplace(std::string subject, const std::string& search, const std::string& replace)
@@ -134,4 +164,3 @@ void printErrorIndex(const char* in, const int index, const std::string& message
}
}
}
-