summaryrefslogtreecommitdiff
path: root/nm.c
blob: 455fd0e09124c620214727c5c6efafea656430c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* noshmore
 * an extended version of nosh,
 * the minimal POSIX C shell.
 * -> generally: adds some functionality.
 *
 * von czjstmax, <jstmaxlol at disroot dot org>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#include <wordexp.h>
#include <sys/wait.h>
#include <readline/readline.h>
#include <readline/history.h>

void handlecc(int sig);
void FreeAll();

char *line = NULL;
wordexp_t p;

int main(void)
{
    signal(SIGINT, handlecc);
    char *USER = getenv("USER");
    if (!USER)
        USER = "god";

    while (true) {
        char prompt[strlen(USER) + strlen("$ ") + 1];
        snprintf(prompt, sizeof(prompt), "%s$ ", USER);

        line = readline(prompt);

        if (!line)
            break;
        if (*line)
            add_history(line);

        if (wordexp(line, &p, 0) != 0) {
            fprintf(stderr, "nsh+! parse error\n");
            free(line);
            continue;
        }

        // built-ins
        if (p.we_wordc == 0) {
            FreeAll();
            continue;
        }
        if (strcmp(p.we_wordv[0], ":q") == 0 || strcmp(p.we_wordv[0], "exit") == 0) {
            FreeAll();
            return 0;
        }
        else if (strcmp(p.we_wordv[0], "echo") == 0) {
            for (size_t i = 1; i < p.we_wordc; i++)
                printf("%s ", p.we_wordv[i]);
            if (p.we_wordc >= 2) {
                if (strcmp(p.we_wordv[1], "-c") != 0)
                    printf("\n");
                else continue;
            }
        }
        else if (strcmp(p.we_wordv[0], "cd") == 0) {
            if (p.we_wordc <= 1) {
                printf("nsh+! cd: no arguments were given.\n");
                FreeAll();
                continue;
            }
            if (chdir(p.we_wordv[1]))
                perror("nsh+! ERROR: failed to change directory");
        }
        else if (strcmp(p.we_wordv[0], "clear") == 0) {
            printf("\033[2J\033[H\033[3J");
            fflush(stdout);
        }
        else if (strcmp(p.we_wordv[0], "help") == 0) {
            printf(
                "noshmore\n"
                "an extended version of nosh, the minimal POSIX C shell.\n"
                "\n"
                "built-ins:\n"
                "echo  -> outputs a string of text, doesn't need \" enclosure.\n"
                "         supports '-c' for output without '\\n'"
                "clear -> clears screen (and scrollback)\n"
                "cd    -> change working directory\n"
                "help  -> prints this screen\n"
                "\ngithub repo: github.com/jstmaxlol/noshmore\n"
            );
        }
        else {
            pid_t pid = fork();
            char **argv = p.we_wordv; // pray with me now
            if (pid == 0) {
                execvp(p.we_wordv[0], argv);
                perror("nsh+! ERROR: failed to execvp()");
                FreeAll();
                _exit(1);
            }
            else if (pid > 0) {
                int status;
                waitpid(pid, &status, 0);
            }
            else {
                fprintf(stderr, "nsh+! ERROR: couldn't execute %s\n", p.we_wordv[0]);
            }
        }
    }
}

void handlecc(int sig)
{
    (void)sig;
    rl_replace_line("", 0);
    printf("\n");
    rl_on_new_line();
    rl_redisplay();
}

void FreeAll()
{
    wordfree(&p);
    free(line);
    line = NULL;
}