Skip to content

Instantly share code, notes, and snippets.

@Chubek
Last active February 8, 2024 14:35
Show Gist options
  • Save Chubek/987e12ec0a2da7ba1ae01fccc6968578 to your computer and use it in GitHub Desktop.
Save Chubek/987e12ec0a2da7ba1ae01fccc6968578 to your computer and use it in GitHub Desktop.
FewlPP.rkt -> C Preprocessor that adds Fork-Exec-Wait Loops

FewPP.rkt is a C preprocessor, which has only one directive, but this directive is powerful enough: it adds a Fork-Exec-Wait loop function to the program text.

If you wish to know what FEWLs are, please read my other Gist -> here

This preprocessor has only one directive, as stated above, and accepts text only via STDIN and outputs only to STDOUT. You should feed it text annoted with #fewl directives, like this simple program:

(CAREFUL, WARNING, PAY ATTENTION, THIS IS NO JOKE!) -> Never pass it the rm command!

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#fewl (#n exec_ls) (#p [char* path]) (#f ls) (#a "-a" path)


int main() { exec_ls("/"); }

The arguments to the directive are in S-Expression syntax (except #p, as we'll see).

#n -> Name of the function

#p -> Function parameters (which you can use in #a)

#f -> The command program name (for example ls, echo or your own program or script)

#a -> The arguments

You need to separate multiple parameters with comman, like:

(#p [int a, char*b])

In #a, you need to enclose non-parameter arguments in quotes, as you can clealy see in the example.

Also, you need to either include unistd.h and stdlib.h and also sys/wait.h, or define pid_t, NULL and EXIT_FAILURE via the -D directive.

Enjoy!

#!/usr/bin/racket
#lang racket
(define directives '())
(define bypasses '())
(define declarations '())
(define definitions '())
(define (process-line line)
(cond
[(string-prefix? line "#fewl")
(set! directives (append directives (list (regexp-replace #rx"#fewl[ \\t]+" line ""))))]
[else
(set! bypasses (append bypasses (list line)))]))
(define (make-function-body command-file command-args)
(format "char* program = \"~a\";
char* arguments[] = { ~a, NULL, };
pid_t child_pid = 0;
int exit_status = 0;
if ((child_pid = fork()) == 0) {
execvp(program, arguments);
perror(\"execvp\");
exit(EXIT_FAILURE);
}
if (child_pid < 0) {
perror(\"fork\");
exit(EXIT_FAILURE);
}
if (waitpid(child_pid, &exit_status, 0) < 0) {
perror(\"waitpid\");
exit(EXIT_FAILURE);
}
return exit_status;
" command-file (string-join command-args ",")))
(define (process-directive directive)
(let*
([function-name-match
(regexp-match #rx#"\\(#n[ \t]+([^ \t]+)\\)" directive)]
[function-params-match
(regexp-match #rx#"\\(#p[ \t]+\\[(.*)\\]" directive)]
[command-file-match
(regexp-match #rx#"\\(#f[ \t]+([^ \t]+)\\)" directive)]
[command-args-match
(regexp-match #rx#"\\(#a[ \t]+(.*)\\)" directive)]
[function-name (if function-name-match
(first (rest function-name-match))
(error "Function name not given through #n"))]
[function-params (if function-params-match
(first (rest function-params-match))
(error "Function params not given through #p"))]
[command-file (if command-file-match
(first (rest command-file-match))
(error "Command file not passed via #f"))]
[command-args (if command-args-match
(first (rest command-args-match))
(error "Command args not passed via #a"))]
[command-file-string (bytes->string/utf-8 command-file)]
[command-args-list (regexp-split #rx"[ \t]+" (bytes->string/utf-8 command-args))]
[function-body (make-function-body command-file command-args-list)])
(set! declarations
(append declarations
(list
(format "int ~a(~a);" function-name function-params))))
(set! definitions
(append definitions
(list
(format "int ~a(~a) { ~a }" function-name function-params function-body))))))
(define (process-results)
(for-each process-directive directives)
(displayln "\n")
(displayln (string-join declarations ""))
(displayln "\n")
(displayln (string-join bypasses "\n"))
(displayln "\n")
(displayln (string-join definitions "")))
(define (process-input)
(let loop ()
(let ([line (read-line)])
(if (eof-object? line)
(process-results)
(begin
(unless (string=? line "")
(process-line line)
(void))
(loop))))))
(process-input)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment