Skip to content

Instantly share code, notes, and snippets.

@abrasumente233
Last active April 11, 2023 09:21
Show Gist options
  • Save abrasumente233/5f60c9467750130558d3aa2b09483a42 to your computer and use it in GitHub Desktop.
Save abrasumente233/5f60c9467750130558d3aa2b09483a42 to your computer and use it in GitHub Desktop.
com
/* $ clang++ -o # @ -O2 -Wall
*
* compile anything
* original version by Tom Duff: http://www.iq0.com/duffgram/com.html
* here's my own version, with the purpose to learn exec() syscall.
*
* - 44
* 2021.11.24
*/
// @TODO: don't force C-style comments at start
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void no_compile_command() {
printf("no compile command found.\n");
printf("add your compile command to the first line of your program like:\n");
printf(" // $ clang @ -o #\n");
printf("where:\n");
printf(" $ is the start of the command\n");
printf(" @ is the source filename\n");
printf(" # is the source filename without extension\n");
exit(1);
}
void help(const char *prog) {
printf("usage: %s [-qg] <source> [more arguments]...\n", prog);
printf(" -q: don't echo the compile command\n");
printf(" -g: generate compile_commands.json\n");
}
int main(int argc, char *argv[]) {
if (argc < 2) {
help(argv[0]);
exit(1);
}
int arg_index = 1;
bool quiet_mode = false;
bool generate_compile_commands_json = false;
while (argv[arg_index][0] == '-') {
if (argv[arg_index][0] == '-') {
if (argv[arg_index][1] == 'q') {
quiet_mode = true;
} else if (argv[arg_index][1] == 'g') {
generate_compile_commands_json = true;
} else {
help(argv[0]);
exit(1);
}
}
arg_index++;
}
const char *filename = argv[arg_index];
int source_fd = open(filename, O_RDONLY);
if (source_fd == -1) {
printf("unable to open file: %s\n", argv[arg_index]);
exit(1);
}
// get the first line
char buffer[8096];
char *sp = buffer;
int rc;
while ((rc = read(source_fd, sp, 1) != 0)) {
if (rc == -1) {
printf("read error\n");
exit(1);
}
if (*sp == '\0') {
no_compile_command();
} else if (*sp == '\n') {
*sp = '\0';
break;
} else if (*sp == '@') {
strcpy(sp, argv[arg_index]);
sp += strlen(sp);
} else if (*sp == '#') {
strcpy(sp, argv[arg_index]);
for (int i = strlen(sp); i >= 0; i--) {
if (sp[i] == '.') {
sp[i] = '\0';
sp += i;
break;
}
if (i == 0) {
printf("file has no extension.\n");
exit(1);
}
}
} else {
sp++;
}
}
close(source_fd);
arg_index++;
// concat additional arguments to buffer
for (int i = arg_index; i < argc; i++) {
sp += sprintf(sp, " %s", argv[i]);
}
// find the command in the first line
// by skipping // or /*, plus a $,
// which is the start of the command.
sp = buffer;
while (isspace(*sp))
sp++;
if (sp[0] != '/' || (sp[1] != '*' && sp[1] != '/')) {
no_compile_command();
}
sp += 2; // skip comment mark
while (isspace(*sp))
sp++;
if (sp[0] != '$') {
no_compile_command();
}
sp += 1; // skip $
while (isspace(*sp))
sp++;
char *command = sp;
if (!quiet_mode) {
printf("%s\n", command);
}
if (generate_compile_commands_json) {
int cc = open("compile_commands.json", O_WRONLY | O_CREAT | O_TRUNC, 0655);
if (cc == -1) {
printf("unable to open file: compile_commands.json\n");
exit(1);
}
char *abs_path = realpath(filename, NULL);
if (!abs_path) {
printf("realpath: error\n");
exit(1);
}
char buf[8096];
sprintf(buf,
"[\n"
" {\n"
" \"command\": \"%s\",\n"
" \"directory\": \".\",\n"
" \"file\": \"%s\"\n"
" }\n"
"]\n",
command, abs_path);
if (write(cc, buf, strlen(buf)) == -1) {
printf("unable to write file: compile_commands.json\n");
exit(1);
}
close(cc);
free(abs_path);
}
if (execl("/bin/sh", "sh", "-c", command, NULL) == -1) {
printf("exec error\n");
exit(1);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment