Skip to content

Commit 0296165

Browse files
committed
Add simple C launcher
Only works on Unix because it depends on the sh launcher script
1 parent 7366acf commit 0296165

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

jruby.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include <errno.h>
2+
#include <libgen.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
#include <unistd.h>
7+
8+
/*
9+
* This program finds JRUBY_HOME and executes the JRuby launcher script
10+
* contained within it.
11+
*/
12+
13+
static const char script_name[] = "jruby.sh";
14+
15+
16+
char *which(const char *const executable) {
17+
const size_t exe_length = strlen(executable);
18+
char *exe_path = NULL;
19+
size_t exe_path_size = 0;
20+
21+
// Iterate through PATH to find executable
22+
char *dirs = getenv("PATH");
23+
if (dirs == NULL) {
24+
return NULL;
25+
}
26+
size_t dirs_length = strlen(dirs);
27+
// Temporarily replace null terminator with colon
28+
dirs[dirs_length] = ':';
29+
30+
size_t dir_head = 0;
31+
size_t i = 0;
32+
do {
33+
if (dirs[i] == ':') {
34+
// Declare convenient path variables
35+
char *const dir = dirs + dir_head;
36+
const size_t dir_length = i - dir_head;
37+
const size_t new_path_length = dir_length + exe_length + 1;
38+
39+
// Allocate enough space for concatenated path
40+
if (exe_path_size < new_path_length + 1) {
41+
// Leave space for null terminator
42+
exe_path = realloc(exe_path, new_path_length + 1);
43+
exe_path_size = new_path_length + 1;
44+
}
45+
46+
// Concatenate path and executable
47+
memcpy(exe_path, dir, dir_length);
48+
exe_path[dir_length] = '/';
49+
memcpy(exe_path + dir_length + 1, executable, exe_length);
50+
exe_path[new_path_length] = '\0';
51+
52+
// Check if we can execute
53+
if (0 == access(exe_path, R_OK | X_OK)) {
54+
goto success;
55+
}
56+
57+
dir_head = i + 1;
58+
}
59+
} while (dirs[i++]);
60+
61+
// Lookup has failed, free if necessary and return NULL
62+
if (exe_path != NULL) {
63+
free(exe_path);
64+
exe_path = NULL;
65+
}
66+
success:
67+
// Restore null terminator
68+
dirs[dirs_length] = '\0';
69+
70+
return exe_path;
71+
}
72+
73+
74+
int main(int argc, char *argv[]) {
75+
if (argc == 0 || argv[0][0] == '\0') {
76+
fputs("Error: No executable provided!", stderr);
77+
return 2;
78+
}
79+
80+
// Find ourselves
81+
char *original_self = argv[0];
82+
char *self_path = realpath(original_self, NULL);
83+
84+
if (self_path == NULL) {
85+
// Iterate through PATH to find script
86+
self_path = which(argv[0]);
87+
88+
if (self_path == NULL) {
89+
fprintf(stderr, "Error: Could not find %s executable\n", script_name);
90+
return 1;
91+
}
92+
93+
// Juggle malloc'd paths
94+
char *real_path = realpath(self_path, NULL);
95+
free(self_path);
96+
self_path = real_path;
97+
}
98+
99+
// Find our parent directory
100+
char *script_dir = dirname(self_path);
101+
if (self_path != script_dir) {
102+
// Free malloc'd self_path if dirname returned statically allocated string
103+
free(self_path);
104+
}
105+
size_t script_dir_length = strlen(script_dir);
106+
107+
// Allocate space for complete script path
108+
size_t script_path_length = strlen(script_name) + script_dir_length + 1;
109+
// Leave space for null terminator
110+
char *script_path = malloc(script_path_length + 1);
111+
112+
// Concatenate script dir and script name
113+
memcpy(script_path, script_dir, script_dir_length);
114+
script_path[script_dir_length] = '/';
115+
memcpy(script_path + script_dir_length + 1, script_name, strlen(script_name));
116+
script_path[script_path_length] = '\0';
117+
118+
// Reuse argv for script command line
119+
argv[0] = script_path;
120+
int ret = execv(argv[0], argv);
121+
122+
if (ret < 0) {
123+
fprintf(stderr, "%s: %s: %s\n", original_self, strerror(errno), script_path);
124+
}
125+
126+
free(self_path);
127+
free(script_path);
128+
return EXIT_FAILURE;
129+
}

0 commit comments

Comments
 (0)