Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(LIB_SOURCES
src/builtins.c
src/pipeline.c
src/glob.c
src/nepeta.c
)

set(UTIL_SOURCES
Expand All @@ -23,6 +24,7 @@ set(HEADERS
include/builtins.h
include/pipeline.h
include/glob_expand.h
include/nepeta.h
)

add_library(trigger_core STATIC ${LIB_SOURCES} ${UTIL_SOURCES})
Expand Down Expand Up @@ -67,8 +69,13 @@ target_link_libraries(test_glob trigger_core)
target_compile_options(test_glob PRIVATE -Wall -Wextra -Werror)
add_test(NAME GlobTests COMMAND test_glob)

add_executable(test_nepeta tests/test_nepeta.c)
target_link_libraries(test_nepeta trigger_core)
target_compile_options(test_nepeta PRIVATE -Wall -Wextra -Werror)
add_test(NAME NepetaTests COMMAND test_nepeta)

add_custom_target(run_tests
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
DEPENDS test_input test_builtins test_execute test_pipeline test_glob
DEPENDS test_input test_builtins test_execute test_pipeline test_glob test_nepeta
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
1 change: 1 addition & 0 deletions include/builtins.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ int trigger_pwd(char **args);
int trigger_echo(char **args);
int trigger_export(char **args);
int trigger_unset(char **args);
int trigger_nepeta(char **args);

int trigger_num_builtins(void);
const Builtin *find_builtin(const char *name);
Expand Down
16 changes: 16 additions & 0 deletions include/nepeta.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef NEPETA_H
#define NEPETA_H

#include <stdbool.h>
#include <stdio.h>

bool trigger_nepeta_is_enabled(void);
void trigger_nepeta_set_enabled(bool enabled);
void trigger_nepeta_init_from_env(void);
const char *trigger_nepeta_prompt(void);
void trigger_nepeta_print_banner(void);
char *trigger_nepeta_translate(const char *plain_text);
void trigger_nepeta_say(FILE *stream, const char *plain_text);
const char *trigger_nepeta_random_quote(void);

#endif
53 changes: 44 additions & 9 deletions src/builtins.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#include "builtins.h"
#include "nepeta.h"
#include "utils/utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static const Builtin builtins[] = {
{"cd", trigger_cd}, {"help", trigger_help}, {"exit", trigger_exit},
{"pwd", trigger_pwd}, {"echo", trigger_echo}, {"export", trigger_export},
{"unset", trigger_unset},
{"cd", trigger_cd}, {"help", trigger_help}, {"exit", trigger_exit},
{"pwd", trigger_pwd}, {"echo", trigger_echo}, {"export", trigger_export},
{"unset", trigger_unset}, {"nepeta", trigger_nepeta},
};

static const int builtin_count = sizeof(builtins) / sizeof(builtins[0]);
Expand All @@ -26,7 +27,7 @@ const Builtin *find_builtin(const char *name) {

int trigger_cd(char **args) {
if (args[1] == NULL) {
fprintf(stderr, "trigger: expected argument to \"cd\"\n");
trigger_nepeta_say(stderr, "trigger: expected argument to \"cd\"\n");
return 1;
}
if (chdir(args[1]) != 0) {
Expand All @@ -38,15 +39,15 @@ int trigger_cd(char **args) {
}

int trigger_help(char **args __attribute__((unused))) {
printf("Trigger: A simple shell written in C\n");
printf("Type program names and arguments, and hit enter.\n");
printf("The following are built in:\n");
trigger_nepeta_say(stdout, "Trigger: A simple shell written in C\n");
trigger_nepeta_say(stdout, "Type program names and arguments, and hit enter.\n");
trigger_nepeta_say(stdout, "The following are built in:\n");

for (int i = 0; i < builtin_count; i++) {
printf(" %s\n", builtins[i].name);
}

printf("Use the man command for information on other programs.\n");
trigger_nepeta_say(stdout, "Use the man command for information on other programs.\n");
return 0;
}

Expand All @@ -57,7 +58,9 @@ int trigger_exit(char **args) {
if (*endptr == '\0' && n >= 0 && n <= 255) {
return (int) n;
}
fprintf(stderr, "trigger: exit: %s: numeric argument required\n", args[1]);
char buf[256];
snprintf(buf, sizeof(buf), "trigger: exit: %s: numeric argument required\n", args[1]);
trigger_nepeta_say(stderr, buf);
return 2;
Comment thread
melogtm marked this conversation as resolved.
}
return EXIT_SUCCESS;
Expand Down Expand Up @@ -114,3 +117,35 @@ int trigger_unset(char **args) {
}
return 0;
}

int trigger_nepeta(char **args) {
if (args[1] == NULL) {
trigger_nepeta_set_enabled(!trigger_nepeta_is_enabled());
trigger_nepeta_say(stdout, "Nepeta mode toggled.\n");
return 0;
}

if (strcmp(args[1], "on") == 0) {
trigger_nepeta_set_enabled(true);
trigger_nepeta_say(stdout, "Nepeta mode enabled.\n");
return 0;
}

if (strcmp(args[1], "off") == 0) {
trigger_nepeta_set_enabled(false);
trigger_nepeta_say(stdout, "Nepeta mode disabled.\n");
return 0;
}

if (strcmp(args[1], "status") == 0) {
if (trigger_nepeta_is_enabled()) {
trigger_nepeta_say(stdout, "Nepeta mode is enabled.\n");
} else {
trigger_nepeta_say(stdout, "Nepeta mode is disabled.\n");
}
return 0;
}

trigger_nepeta_say(stderr, "usage: nepeta [on|off|status]\n");
return 1;
}
6 changes: 5 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "execute.h"
#include "input.h"
#include "nepeta.h"
#include "utils/utils.h"
#include <signal.h>
#include <stdbool.h>
Expand All @@ -10,7 +11,7 @@ void trigger_loop(void) {
int last_status = 0;

for (;;) {
printf("> ");
printf("%s", trigger_nepeta_prompt());
fflush(stdout);
char *line = trigger_read_line();
TokenList *tl = parse_line_with_quotes(line);
Expand Down Expand Up @@ -38,6 +39,9 @@ int main(void) {
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);

trigger_nepeta_init_from_env();
trigger_nepeta_print_banner();

trigger_loop();

return EXIT_SUCCESS;
Expand Down
166 changes: 166 additions & 0 deletions src/nepeta.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#include "nepeta.h"
#include "utils/utils.h"
#include <ctype.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

static bool nepeta_enabled = false;

bool trigger_nepeta_is_enabled(void) { return nepeta_enabled; }

void trigger_nepeta_set_enabled(bool enabled) { nepeta_enabled = enabled; }

void trigger_nepeta_init_from_env(void) {
const char *val = getenv("TRIGGER_NEPETA");
if (val != NULL && val[0] != '\0' && strcmp(val, "0") != 0) {
nepeta_enabled = true;
}
}
Comment thread
melogtm marked this conversation as resolved.

const char *trigger_nepeta_prompt(void) {
if (nepeta_enabled) {
return ":33 < ";
}
return "> ";
}

typedef struct {
const char *from;
const char *to;
} PunEntry;

static const PunEntry pun_dict[] = {
{"for", "fur"}, {"perfect", "purrfect"}, {"great", "clawesome"}, {"awesome", "pawsome"},
{"friend", "furriend"}, {"now", "meow"}, {"help", "halp"},
};

static const size_t pun_count = sizeof(pun_dict) / sizeof(pun_dict[0]);

static int alpha_run_len(const char *s) {
int n = 0;
while (s[n] != '\0' && isalpha((unsigned char) s[n])) {
n++;
}
return n;
}

static bool alpha_run_equals(const char *s, int slen, const char *dict_word) {
size_t dwlen = strlen(dict_word);
if ((size_t) slen != dwlen) {
return false;
}
for (size_t i = 0; i < dwlen; i++) {
if (tolower((unsigned char) s[i]) != tolower((unsigned char) dict_word[i])) {
return false;
}
}
return true;
}

static const char *find_pun(const char *s, int slen) {
for (size_t i = 0; i < pun_count; i++) {
if (alpha_run_equals(s, slen, pun_dict[i].from)) {
return pun_dict[i].to;
}
}
return NULL;
}

static void append_char(char **buf, size_t *cap, size_t *len, char c) {
if (*len + 1 >= *cap) {
*cap = *cap == 0 ? 64 : *cap * 2;
*buf = xrealloc(*buf, *cap);
}
(*buf)[*len] = c;
(*len)++;
}

static void append_str(char **buf, size_t *cap, size_t *len, const char *s) {
for (const char *p = s; *p != '\0'; p++) {
append_char(buf, cap, len, *p);
}
}

char *trigger_nepeta_translate(const char *plain_text) {
if (plain_text == NULL) {
char *empty = xmalloc(1);
empty[0] = '\0';
return empty;
}

char *result = NULL;
size_t cap = 0;
size_t len = 0;

const char *p = plain_text;

while (*p != '\0') {
if (isalpha((unsigned char) *p)) {
int run = alpha_run_len(p);
const char *pun = find_pun(p, run);

if (pun != NULL) {
char first = p[0];
if (isupper((unsigned char) first)) {
append_char(&result, &cap, &len, (char) toupper((unsigned char) pun[0]));
append_str(&result, &cap, &len, pun + 1);
} else {
append_char(&result, &cap, &len, (char) tolower((unsigned char) pun[0]));
append_str(&result, &cap, &len, pun + 1);
}
} else {
for (int i = 0; i < run; i++) {
append_char(&result, &cap, &len, p[i]);
}
}
p += run;
} else {
append_char(&result, &cap, &len, *p);
p++;
}
}

append_char(&result, &cap, &len, '\0');
return result;
}

void trigger_nepeta_say(FILE *stream, const char *plain_text) {
if (!nepeta_enabled) {
fputs(plain_text, stream);
return;
}

char *translated = trigger_nepeta_translate(plain_text);
fprintf(stream, ":33 < %s", translated);
free(translated);
}

static const char *quotes[] = {
":33 < *ac scratches at the terminal prompt*", ":33 < purractice makes purrfect! hehe",
":33 < *pounces on the enter key* rawr!", ":33 < this shell is the cat's pajamas!",
":33 < *curls up on the keyboard* warm~", ":33 < fur the glory of the hunt!",
":33 < *chases the cursor with big kitty eyes*", ":33 < all your base are belong to Nepeta!",
":33 < *batbatbat* at the command line", ":33 < meow is the time fur action!",
};

static const size_t quote_count = sizeof(quotes) / sizeof(quotes[0]);

const char *trigger_nepeta_random_quote(void) {
static int seeded = 0;
if (!seeded) {
srand((unsigned) time(NULL));
seeded = 1;
}
return quotes[rand() % quote_count];
}

void trigger_nepeta_print_banner(void) {
if (!nepeta_enabled) {
return;
}

printf(":33 < *Nepeta Leijon has entered the terminal* ");
printf("purrfect mode activated, %s, fur great justice!\n", trigger_nepeta_random_quote());
}
Loading
Loading