Showing preview only (396K chars total). Download the full file or copy to clipboard to get everything.
Repository: emersion/mrsh
Branch: master
Commit: 4c81598721bc
Files: 127
Total size: 367.3 KB
Directory structure:
gitextract_mub_c0wg/
├── .builds/
│ ├── alpine.yml
│ ├── archlinux.yml
│ └── freebsd.yml
├── .editorconfig
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── arithm.c
├── array.c
├── ast.c
├── ast_print.c
├── buffer.c
├── builtin/
│ ├── alias.c
│ ├── bg.c
│ ├── break.c
│ ├── builtin.c
│ ├── cd.c
│ ├── colon.c
│ ├── command.c
│ ├── dot.c
│ ├── eval.c
│ ├── exec.c
│ ├── exit.c
│ ├── export.c
│ ├── false.c
│ ├── fg.c
│ ├── getopts.c
│ ├── hash.c
│ ├── jobs.c
│ ├── pwd.c
│ ├── read.c
│ ├── return.c
│ ├── set.c
│ ├── shift.c
│ ├── times.c
│ ├── trap.c
│ ├── true.c
│ ├── type.c
│ ├── ulimit.c
│ ├── umask.c
│ ├── unalias.c
│ ├── unset.c
│ ├── unspecified.c
│ └── wait.c
├── configure
├── example/
│ ├── highlight.c
│ └── meson.build
├── frontend/
│ ├── basic.c
│ └── readline.c
├── getopt.c
├── hashtable.c
├── include/
│ ├── ast.h
│ ├── builtin.h
│ ├── frontend.h
│ ├── mrsh/
│ │ ├── arithm.h
│ │ ├── array.h
│ │ ├── ast.h
│ │ ├── buffer.h
│ │ ├── builtin.h
│ │ ├── entry.h
│ │ ├── hashtable.h
│ │ ├── parser.h
│ │ └── shell.h
│ ├── mrsh_getopt.h
│ ├── parser.h
│ └── shell/
│ ├── job.h
│ ├── path.h
│ ├── process.h
│ ├── redir.h
│ ├── shell.h
│ ├── task.h
│ ├── trap.h
│ └── word.h
├── libmrsh.darwin.sym
├── libmrsh.gnu.sym
├── main.c
├── meson.build
├── meson_options.txt
├── mkpc
├── parser/
│ ├── arithm.c
│ ├── parser.c
│ ├── program.c
│ └── word.c
├── shell/
│ ├── arithm.c
│ ├── entry.c
│ ├── job.c
│ ├── path.c
│ ├── process.c
│ ├── redir.c
│ ├── shell.c
│ ├── task/
│ │ ├── pipeline.c
│ │ ├── simple_command.c
│ │ ├── task.c
│ │ └── word.c
│ ├── trap.c
│ └── word.c
└── test/
├── args.sh
├── arithm.sh
├── async.sh
├── case.sh
├── command.sh
├── conformance/
│ ├── 2.2-quoted-characters.sh
│ ├── 2.2-quoted-characters.stdout
│ ├── 2.2.2-nested-single-quotes.fail.sh
│ ├── 2.2.3-alias-expansion.fail.sh
│ ├── 2.2.3-backquote-nonterminated-dquote.undefined.sh
│ ├── 2.2.3-backquote-nonterminated-squote.undefined.sh
│ ├── 2.2.3-dquote-nonterminated-backquote.undefined.sh
│ ├── README
│ ├── harness.sh
│ └── meson.build
├── for.sh
├── function.sh
├── harness.sh
├── if.sh
├── loop.sh
├── meson.build
├── pipeline.sh
├── read.sh
├── readonly.sh
├── redir.sh
├── return.sh
├── subshell.sh
├── syntax.sh
├── ulimit.sh
└── word.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .builds/alpine.yml
================================================
image: alpine/edge
packages:
- gcc
- clang
- meson
- dash
sources:
- https://git.sr.ht/~emersion/mrsh
tasks:
- setup: |
cd mrsh
CC=gcc meson build-gcc -Dreference-shell=dash
CC=clang meson build-clang -Dreference-shell=dash
- build-gcc: |
cd mrsh
ninja -C build-gcc
- build-clang: |
cd mrsh
ninja -C build-clang
- test-gcc: |
cd mrsh
ninja -C build-gcc test
- test-clang: |
cd mrsh
ninja -C build-clang test
triggers:
- action: email
condition: failure
to: "<contact@emersion.fr>"
================================================
FILE: .builds/archlinux.yml
================================================
image: archlinux
packages:
- gcc
- meson
- readline
sources:
- https://git.sr.ht/~emersion/mrsh
tasks:
- setup: |
cd mrsh
meson build -Db_sanitize=address,undefined -Dauto_features=enabled
- build: |
cd mrsh
ninja -C build
- test: |
cd mrsh
ninja -C build test
- build-release: |
cd mrsh
meson configure build --buildtype release
ninja -C build
- build-minimal: |
cd mrsh
meson configure build -Dauto_features=disabled
ninja -C build
triggers:
- action: email
condition: failure
to: "<contact@emersion.fr>"
================================================
FILE: .builds/freebsd.yml
================================================
image: freebsd/latest
packages:
- meson
- libedit
- pkgconf
sources:
- https://git.sr.ht/~emersion/mrsh
tasks:
- setup: |
cd mrsh
meson build -Dauto_features=enabled -Dreadline-provider=editline
- build: |
cd mrsh
ninja -C build
- test: |
cd mrsh
ninja -C build test
triggers:
- action: email
condition: failure
to: "<contact@emersion.fr>"
================================================
FILE: .editorconfig
================================================
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = tab
trim_trailing_whitespace = true
indent_size = 4
[*.yml]
indent_size = 2
indent_style = space
================================================
FILE: .gitignore
================================================
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
/build
/build-*
/.build
/highlight
/mrsh
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 emersion
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
.POSIX:
.SUFFIXES:
OUTDIR=.build
include $(OUTDIR)/config.mk
INCLUDE=-Iinclude
public_includes=\
include/mrsh/arithm.h \
include/mrsh/array.h \
include/mrsh/ast.h \
include/mrsh/buffer.h \
include/mrsh/builtin.h \
include/mrsh/entry.h \
include/mrsh/hashtable.h \
include/mrsh/parser.h \
include/mrsh/shell.h
tests=\
test/args.sh \
test/arithm.sh \
test/async.sh \
test/case.sh \
test/command.sh \
test/for.sh \
test/function.sh \
test/if.sh \
test/loop.sh \
test/pipeline.sh \
test/read.sh \
test/readonly.sh \
test/redir.sh \
test/return.sh \
test/subshell.sh \
test/syntax.sh \
test/ulimit.sh \
test/word.sh
include $(OUTDIR)/cppcache
.SUFFIXES: .c .o
.c.o:
@mkdir -p $$(dirname "$@")
@printf 'CC\t$@\n'
@touch $(OUTDIR)/cppcache
@grep $< $(OUTDIR)/cppcache >/dev/null || \
$(CPP) $(INCLUDE) -MM -MT $@ $< >> $(OUTDIR)/cppcache
@$(CC) -c $(CFLAGS) $(INCLUDE) -o $@ $<
$(OUTDIR)/libmrsh.a: $(libmrsh_objects)
@printf 'AR\t$@\n'
@$(AR) -csr $@ $(libmrsh_objects)
libmrsh.so.$(SOVERSION): $(OUTDIR)/libmrsh.a
@printf 'LD\t$@\n'
@$(CC) -shared $(LDFLAGS) -o $@ $(OUTDIR)/libmrsh.a
$(OUTDIR)/mrsh.pc:
@printf 'MKPC\t$@\n'
@PREFIX=$(PREFIX) ./mkpc $@
mrsh: $(OUTDIR)/libmrsh.a $(mrsh_objects)
@printf 'CCLD\t$@\n'
@$(CC) -o $@ $(LDFLAGS) $(mrsh_objects) -L$(OUTDIR) -lmrsh $(LIBS)
highlight: $(OUTDIR)/libmrsh.a $(highlight_objects)
@printf 'CCLD\t$@\n'
@$(CC) -o $@ $(LDFLAGS) $(highlight_objects) -L$(OUTDIR) -lmrsh $(LIBS)
check: mrsh $(tests)
@for t in $(tests); do \
printf '%-30s... ' "$$t" && \
MRSH=./mrsh REF_SH=$${REF_SH:-sh} ./test/harness.sh $$t >/dev/null && \
echo OK || echo FAIL; \
done
install: mrsh libmrsh.so.$(SOVERSION) $(OUTDIR)/mrsh.pc
mkdir -p $(BINDIR) $(LIBDIR) $(INCDIR)/mrsh $(PCDIR)
install -m755 mrsh $(BINDIR)/mrsh
install -m755 libmrsh.so.$(SOVERSION) $(LIBDIR)/libmrsh.so.$(SOVERSION)
for inc in $(public_includes); do \
install -m644 $$inc $(INCDIR)/mrsh/$$(basename $$inc); \
done
install -m644 $(OUTDIR)/mrsh.pc $(PCDIR)/mrsh.pc
uninstall:
rm -f $(BINDIR)/mrsh
rm -f $(LIBDIR)/libmrsh.so.$(SOVERSION)
for inc in $(public_includes); do \
rm -f $(INCDIR)/mrsh/$$(basename $$inc); \
done
rm -f $(PCDIR)/mrsh.pc
clean:
rm -rf \
$(libmrsh_objects) \
$(mrsh_objects) \
$(highlight_objects) \
mrsh highlight libmrsh.so.$(SOVERSION) $(OUTDIR)/mrsh.pc
mrproper: clean
rm -rf $(OUTDIR)
.PHONY: all install clean check
================================================
FILE: README.md
================================================
# mrsh
A minimal [POSIX] shell.
[](https://builds.sr.ht/~emersion/mrsh/commits/master?)
* POSIX compliant, no less, no more
* Simple, readable code without magic
* Library to build more elaborate shells
This project is a [work in progress].
## Build
Both Meson and POSIX make are supported. To use Meson:
meson build/
ninja -C build/
build/mrsh
To use POSIX make:
./configure
make
./mrsh
## Contributing
Either [send GitHub pull requests][GitHub] or [send patches on the mailing
list][ML].
## License
MIT
[POSIX]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
[work in progress]: https://github.com/emersion/mrsh/issues/8
[GitHub]: https://github.com/emersion/mrsh
[ML]: https://lists.sr.ht/%7Eemersion/mrsh-dev
================================================
FILE: arithm.c
================================================
#include <assert.h>
#include <mrsh/arithm.h>
#include <mrsh/ast.h>
#include <stdlib.h>
void mrsh_arithm_expr_destroy(struct mrsh_arithm_expr *expr) {
if (expr == NULL) {
return;
}
switch (expr->type) {
case MRSH_ARITHM_LITERAL:;
struct mrsh_arithm_literal *al = mrsh_arithm_expr_get_literal(expr);
free(al);
return;
case MRSH_ARITHM_VARIABLE:;
struct mrsh_arithm_variable *av = mrsh_arithm_expr_get_variable(expr);
free(av->name);
free(av);
return;
case MRSH_ARITHM_UNOP:;
struct mrsh_arithm_unop *au = mrsh_arithm_expr_get_unop(expr);
mrsh_arithm_expr_destroy(au->body);
free(au);
return;
case MRSH_ARITHM_BINOP:;
struct mrsh_arithm_binop *ab = mrsh_arithm_expr_get_binop(expr);
mrsh_arithm_expr_destroy(ab->left);
mrsh_arithm_expr_destroy(ab->right);
free(ab);
return;
case MRSH_ARITHM_COND:;
struct mrsh_arithm_cond *ac = mrsh_arithm_expr_get_cond(expr);
mrsh_arithm_expr_destroy(ac->condition);
mrsh_arithm_expr_destroy(ac->body);
mrsh_arithm_expr_destroy(ac->else_part);
free(ac);
return;
case MRSH_ARITHM_ASSIGN:;
struct mrsh_arithm_assign *aa = mrsh_arithm_expr_get_assign(expr);
free(aa->name);
mrsh_arithm_expr_destroy(aa->value);
free(aa);
return;
}
abort();
}
struct mrsh_arithm_literal *mrsh_arithm_literal_create(long value) {
struct mrsh_arithm_literal *al =
calloc(1, sizeof(struct mrsh_arithm_literal));
if (al == NULL) {
return NULL;
}
al->expr.type = MRSH_ARITHM_LITERAL;
al->value = value;
return al;
}
struct mrsh_arithm_variable *mrsh_arithm_variable_create(char *name) {
struct mrsh_arithm_variable *av =
calloc(1, sizeof(struct mrsh_arithm_variable));
if (av == NULL) {
return NULL;
}
av->expr.type = MRSH_ARITHM_VARIABLE;
av->name = name;
return av;
}
struct mrsh_arithm_unop *mrsh_arithm_unop_create(
enum mrsh_arithm_unop_type type, struct mrsh_arithm_expr *body) {
struct mrsh_arithm_unop *au =
calloc(1, sizeof(struct mrsh_arithm_unop));
if (au == NULL) {
return NULL;
}
au->expr.type = MRSH_ARITHM_UNOP;
au->type = type;
au->body = body;
return au;
}
struct mrsh_arithm_binop *mrsh_arithm_binop_create(
enum mrsh_arithm_binop_type type, struct mrsh_arithm_expr *left,
struct mrsh_arithm_expr *right) {
struct mrsh_arithm_binop *ab =
calloc(1, sizeof(struct mrsh_arithm_binop));
if (ab == NULL) {
return NULL;
}
ab->expr.type = MRSH_ARITHM_BINOP;
ab->type = type;
ab->left = left;
ab->right = right;
return ab;
}
struct mrsh_arithm_cond *mrsh_arithm_cond_create(
struct mrsh_arithm_expr *condition, struct mrsh_arithm_expr *body,
struct mrsh_arithm_expr *else_part) {
struct mrsh_arithm_cond *ac =
calloc(1, sizeof(struct mrsh_arithm_cond));
if (ac == NULL) {
return NULL;
}
ac->expr.type = MRSH_ARITHM_COND;
ac->condition = condition;
ac->body = body;
ac->else_part = else_part;
return ac;
}
struct mrsh_arithm_assign *mrsh_arithm_assign_create(
enum mrsh_arithm_assign_op op, char *name,
struct mrsh_arithm_expr *value) {
struct mrsh_arithm_assign *aa =
calloc(1, sizeof(struct mrsh_arithm_assign));
if (aa == NULL) {
return NULL;
}
aa->expr.type = MRSH_ARITHM_ASSIGN;
aa->op = op;
aa->name = name;
aa->value = value;
return aa;
}
struct mrsh_arithm_literal *mrsh_arithm_expr_get_literal(
const struct mrsh_arithm_expr *expr) {
assert(expr->type == MRSH_ARITHM_LITERAL);
return (struct mrsh_arithm_literal *)expr;
}
struct mrsh_arithm_variable *mrsh_arithm_expr_get_variable(
const struct mrsh_arithm_expr *expr) {
assert(expr->type == MRSH_ARITHM_VARIABLE);
return (struct mrsh_arithm_variable *)expr;
}
struct mrsh_arithm_unop *mrsh_arithm_expr_get_unop(
const struct mrsh_arithm_expr *expr) {
assert(expr->type == MRSH_ARITHM_UNOP);
return (struct mrsh_arithm_unop *)expr;
}
struct mrsh_arithm_binop *mrsh_arithm_expr_get_binop(
const struct mrsh_arithm_expr *expr) {
assert(expr->type == MRSH_ARITHM_BINOP);
return (struct mrsh_arithm_binop *)expr;
}
struct mrsh_arithm_cond *mrsh_arithm_expr_get_cond(
const struct mrsh_arithm_expr *expr) {
assert(expr->type == MRSH_ARITHM_COND);
return (struct mrsh_arithm_cond *)expr;
}
struct mrsh_arithm_assign *mrsh_arithm_expr_get_assign(
const struct mrsh_arithm_expr *expr) {
assert(expr->type == MRSH_ARITHM_ASSIGN);
return (struct mrsh_arithm_assign *)expr;
}
================================================
FILE: array.c
================================================
#include <assert.h>
#include <mrsh/array.h>
#include <stdlib.h>
#define INITIAL_SIZE 8
bool mrsh_array_reserve(struct mrsh_array *array, size_t new_cap) {
if (array->cap >= new_cap) {
return true;
}
void *new_data = realloc(array->data, new_cap * sizeof(void *));
if (new_data == NULL) {
return false;
}
array->data = new_data;
array->cap = new_cap;
return true;
}
ssize_t mrsh_array_add(struct mrsh_array *array, void *value) {
assert(array->len <= array->cap);
if (array->len == array->cap) {
size_t new_cap = 2 * array->cap;
if (new_cap < INITIAL_SIZE) {
new_cap = INITIAL_SIZE;
}
if (!mrsh_array_reserve(array, new_cap)) {
return -1;
}
}
size_t i = array->len;
array->data[i] = value;
array->len++;
return i;
}
void mrsh_array_finish(struct mrsh_array *array) {
free(array->data);
array->cap = array->len = 0;
}
================================================
FILE: ast.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <mrsh/buffer.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "ast.h"
bool mrsh_position_valid(const struct mrsh_position *pos) {
return pos->line > 0;
}
bool mrsh_range_valid(const struct mrsh_range *range) {
return mrsh_position_valid(&range->begin) &&
mrsh_position_valid(&range->end);
}
void mrsh_node_destroy(struct mrsh_node *node) {
switch (node->type) {
case MRSH_NODE_PROGRAM:;
struct mrsh_program *prog = mrsh_node_get_program(node);
mrsh_program_destroy(prog);
return;
case MRSH_NODE_COMMAND_LIST:;
struct mrsh_command_list *cl = mrsh_node_get_command_list(node);
mrsh_command_list_destroy(cl);
return;
case MRSH_NODE_AND_OR_LIST:;
struct mrsh_and_or_list *aol = mrsh_node_get_and_or_list(node);
mrsh_and_or_list_destroy(aol);
return;
case MRSH_NODE_COMMAND:;
struct mrsh_command *cmd = mrsh_node_get_command(node);
mrsh_command_destroy(cmd);
return;
case MRSH_NODE_WORD:;
struct mrsh_word *word = mrsh_node_get_word(node);
mrsh_word_destroy(word);
return;
}
abort();
}
void mrsh_word_destroy(struct mrsh_word *word) {
if (word == NULL) {
return;
}
switch (word->type) {
case MRSH_WORD_STRING:;
struct mrsh_word_string *ws = mrsh_word_get_string(word);
free(ws->str);
free(ws);
return;
case MRSH_WORD_PARAMETER:;
struct mrsh_word_parameter *wp = mrsh_word_get_parameter(word);
free(wp->name);
mrsh_word_destroy(wp->arg);
free(wp);
return;
case MRSH_WORD_COMMAND:;
struct mrsh_word_command *wc = mrsh_word_get_command(word);
mrsh_program_destroy(wc->program);
free(wc);
return;
case MRSH_WORD_ARITHMETIC:;
struct mrsh_word_arithmetic *wa = mrsh_word_get_arithmetic(word);
mrsh_word_destroy(wa->body);
free(wa);
return;
case MRSH_WORD_LIST:;
struct mrsh_word_list *wl = mrsh_word_get_list(word);
for (size_t i = 0; i < wl->children.len; ++i) {
struct mrsh_word *child = wl->children.data[i];
mrsh_word_destroy(child);
}
mrsh_array_finish(&wl->children);
free(wl);
return;
}
abort();
}
void mrsh_io_redirect_destroy(struct mrsh_io_redirect *redir) {
if (redir == NULL) {
return;
}
mrsh_word_destroy(redir->name);
for (size_t i = 0; i < redir->here_document.len; ++i) {
struct mrsh_word *line = redir->here_document.data[i];
mrsh_word_destroy(line);
}
mrsh_array_finish(&redir->here_document);
free(redir);
}
void mrsh_assignment_destroy(struct mrsh_assignment *assign) {
if (assign == NULL) {
return;
}
free(assign->name);
mrsh_word_destroy(assign->value);
free(assign);
}
void command_list_array_finish(struct mrsh_array *cmds) {
for (size_t i = 0; i < cmds->len; ++i) {
struct mrsh_command_list *l = cmds->data[i];
mrsh_command_list_destroy(l);
}
mrsh_array_finish(cmds);
}
void case_item_destroy(struct mrsh_case_item *item) {
for (size_t j = 0; j < item->patterns.len; ++j) {
struct mrsh_word *pattern = item->patterns.data[j];
mrsh_word_destroy(pattern);
}
mrsh_array_finish(&item->patterns);
command_list_array_finish(&item->body);
free(item);
}
void mrsh_command_destroy(struct mrsh_command *cmd) {
if (cmd == NULL) {
return;
}
switch (cmd->type) {
case MRSH_SIMPLE_COMMAND:;
struct mrsh_simple_command *sc = mrsh_command_get_simple_command(cmd);
mrsh_word_destroy(sc->name);
for (size_t i = 0; i < sc->arguments.len; ++i) {
struct mrsh_word *arg = sc->arguments.data[i];
mrsh_word_destroy(arg);
}
mrsh_array_finish(&sc->arguments);
for (size_t i = 0; i < sc->io_redirects.len; ++i) {
struct mrsh_io_redirect *redir = sc->io_redirects.data[i];
mrsh_io_redirect_destroy(redir);
}
mrsh_array_finish(&sc->io_redirects);
for (size_t i = 0; i < sc->assignments.len; ++i) {
struct mrsh_assignment *assign = sc->assignments.data[i];
mrsh_assignment_destroy(assign);
}
mrsh_array_finish(&sc->assignments);
free(sc);
return;
case MRSH_BRACE_GROUP:;
struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
command_list_array_finish(&bg->body);
free(bg);
return;
case MRSH_SUBSHELL:;
struct mrsh_subshell *s = mrsh_command_get_subshell(cmd);
command_list_array_finish(&s->body);
free(s);
return;
case MRSH_IF_CLAUSE:;
struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
command_list_array_finish(&ic->condition);
command_list_array_finish(&ic->body);
mrsh_command_destroy(ic->else_part);
free(ic);
return;
case MRSH_FOR_CLAUSE:;
struct mrsh_for_clause *fc = mrsh_command_get_for_clause(cmd);
free(fc->name);
for (size_t i = 0; i < fc->word_list.len; ++i) {
struct mrsh_word *word = fc->word_list.data[i];
mrsh_word_destroy(word);
}
mrsh_array_finish(&fc->word_list);
command_list_array_finish(&fc->body);
free(fc);
return;
case MRSH_LOOP_CLAUSE:;
struct mrsh_loop_clause *lc = mrsh_command_get_loop_clause(cmd);
command_list_array_finish(&lc->condition);
command_list_array_finish(&lc->body);
free(lc);
return;
case MRSH_CASE_CLAUSE:;
struct mrsh_case_clause *cc = mrsh_command_get_case_clause(cmd);
mrsh_word_destroy(cc->word);
for (size_t i = 0; i < cc->items.len; ++i) {
struct mrsh_case_item *item = cc->items.data[i];
case_item_destroy(item);
}
mrsh_array_finish(&cc->items);
free(cc);
return;
case MRSH_FUNCTION_DEFINITION:;
struct mrsh_function_definition *fd =
mrsh_command_get_function_definition(cmd);
free(fd->name);
mrsh_command_destroy(fd->body);
for (size_t i = 0; i < fd->io_redirects.len; ++i) {
struct mrsh_io_redirect *redir = fd->io_redirects.data[i];
mrsh_io_redirect_destroy(redir);
}
mrsh_array_finish(&fd->io_redirects);
free(fd);
return;
}
abort();
}
void mrsh_and_or_list_destroy(struct mrsh_and_or_list *and_or_list) {
if (and_or_list == NULL) {
return;
}
switch (and_or_list->type) {
case MRSH_AND_OR_LIST_PIPELINE:;
struct mrsh_pipeline *p = mrsh_and_or_list_get_pipeline(and_or_list);
for (size_t i = 0; i < p->commands.len; ++i) {
struct mrsh_command *cmd = p->commands.data[i];
mrsh_command_destroy(cmd);
}
mrsh_array_finish(&p->commands);
free(p);
return;
case MRSH_AND_OR_LIST_BINOP:;
struct mrsh_binop *binop = mrsh_and_or_list_get_binop(and_or_list);
mrsh_and_or_list_destroy(binop->left);
mrsh_and_or_list_destroy(binop->right);
free(binop);
return;
}
abort();
}
struct mrsh_command_list *mrsh_command_list_create(void) {
struct mrsh_command_list *list = calloc(1, sizeof(struct mrsh_command_list));
list->node.type = MRSH_NODE_COMMAND_LIST;
return list;
}
void mrsh_command_list_destroy(struct mrsh_command_list *l) {
if (l == NULL) {
return;
}
mrsh_and_or_list_destroy(l->and_or_list);
free(l);
}
struct mrsh_program *mrsh_program_create(void) {
struct mrsh_program *prog = calloc(1, sizeof(struct mrsh_program));
prog->node.type = MRSH_NODE_PROGRAM;
return prog;
}
void mrsh_program_destroy(struct mrsh_program *prog) {
if (prog == NULL) {
return;
}
command_list_array_finish(&prog->body);
free(prog);
}
struct mrsh_word *mrsh_node_get_word(const struct mrsh_node *node) {
assert(node->type == MRSH_NODE_WORD);
return (struct mrsh_word *)node;
}
struct mrsh_command *mrsh_node_get_command(const struct mrsh_node *node) {
assert(node->type == MRSH_NODE_COMMAND);
return (struct mrsh_command *)node;
}
struct mrsh_and_or_list *mrsh_node_get_and_or_list(
const struct mrsh_node *node) {
assert(node->type == MRSH_NODE_AND_OR_LIST);
return (struct mrsh_and_or_list *)node;
}
struct mrsh_command_list *mrsh_node_get_command_list(
const struct mrsh_node *node) {
assert(node->type == MRSH_NODE_COMMAND_LIST);
return (struct mrsh_command_list *)node;
}
struct mrsh_program *mrsh_node_get_program(const struct mrsh_node *node) {
assert(node->type == MRSH_NODE_PROGRAM);
return (struct mrsh_program *)node;
}
struct mrsh_word_string *mrsh_word_string_create(char *str,
bool single_quoted) {
struct mrsh_word_string *ws = calloc(1, sizeof(struct mrsh_word_string));
ws->word.node.type = MRSH_NODE_WORD;
ws->word.type = MRSH_WORD_STRING;
ws->str = str;
ws->single_quoted = single_quoted;
return ws;
}
struct mrsh_word_parameter *mrsh_word_parameter_create(char *name,
enum mrsh_word_parameter_op op, bool colon, struct mrsh_word *arg) {
struct mrsh_word_parameter *wp =
calloc(1, sizeof(struct mrsh_word_parameter));
wp->word.node.type = MRSH_NODE_WORD;
wp->word.type = MRSH_WORD_PARAMETER;
wp->name = name;
wp->op = op;
wp->colon = colon;
wp->arg = arg;
return wp;
}
struct mrsh_word_command *mrsh_word_command_create(struct mrsh_program *prog,
bool back_quoted) {
struct mrsh_word_command *wc =
calloc(1, sizeof(struct mrsh_word_command));
wc->word.node.type = MRSH_NODE_WORD;
wc->word.type = MRSH_WORD_COMMAND;
wc->program = prog;
wc->back_quoted = back_quoted;
return wc;
}
struct mrsh_word_arithmetic *mrsh_word_arithmetic_create(
struct mrsh_word *body) {
struct mrsh_word_arithmetic *wa =
calloc(1, sizeof(struct mrsh_word_arithmetic));
wa->word.node.type = MRSH_NODE_WORD;
wa->word.type = MRSH_WORD_ARITHMETIC;
wa->body = body;
return wa;
}
struct mrsh_word_list *mrsh_word_list_create(struct mrsh_array *children,
bool double_quoted) {
struct mrsh_word_list *wl = calloc(1, sizeof(struct mrsh_word_list));
wl->word.node.type = MRSH_NODE_WORD;
wl->word.type = MRSH_WORD_LIST;
if (children != NULL) {
wl->children = *children;
}
wl->double_quoted = double_quoted;
return wl;
}
struct mrsh_word_string *mrsh_word_get_string(const struct mrsh_word *word) {
assert(word->type == MRSH_WORD_STRING);
return (struct mrsh_word_string *)word;
}
struct mrsh_word_parameter *mrsh_word_get_parameter(
const struct mrsh_word *word) {
assert(word->type == MRSH_WORD_PARAMETER);
return (struct mrsh_word_parameter *)word;
}
struct mrsh_word_command *mrsh_word_get_command(const struct mrsh_word *word) {
assert(word->type == MRSH_WORD_COMMAND);
return (struct mrsh_word_command *)word;
}
struct mrsh_word_arithmetic *mrsh_word_get_arithmetic(
const struct mrsh_word *word) {
assert(word->type == MRSH_WORD_ARITHMETIC);
return (struct mrsh_word_arithmetic *)word;
}
struct mrsh_word_list *mrsh_word_get_list(const struct mrsh_word *word) {
assert(word->type == MRSH_WORD_LIST);
return (struct mrsh_word_list *)word;
}
struct mrsh_simple_command *mrsh_simple_command_create(struct mrsh_word *name,
struct mrsh_array *arguments, struct mrsh_array *io_redirects,
struct mrsh_array *assignments) {
struct mrsh_simple_command *cmd =
calloc(1, sizeof(struct mrsh_simple_command));
cmd->command.node.type = MRSH_NODE_COMMAND;
cmd->command.type = MRSH_SIMPLE_COMMAND;
cmd->name = name;
cmd->arguments = *arguments;
cmd->io_redirects = *io_redirects;
cmd->assignments = *assignments;
return cmd;
}
struct mrsh_brace_group *mrsh_brace_group_create(struct mrsh_array *body) {
struct mrsh_brace_group *bg = calloc(1, sizeof(struct mrsh_brace_group));
bg->command.node.type = MRSH_NODE_COMMAND;
bg->command.type = MRSH_BRACE_GROUP;
bg->body = *body;
return bg;
}
struct mrsh_subshell *mrsh_subshell_create(struct mrsh_array *body) {
struct mrsh_subshell *s = calloc(1, sizeof(struct mrsh_subshell));
s->command.node.type = MRSH_NODE_COMMAND;
s->command.type = MRSH_SUBSHELL;
s->body = *body;
return s;
}
struct mrsh_if_clause *mrsh_if_clause_create(struct mrsh_array *condition,
struct mrsh_array *body, struct mrsh_command *else_part) {
struct mrsh_if_clause *ic = calloc(1, sizeof(struct mrsh_if_clause));
ic->command.node.type = MRSH_NODE_COMMAND;
ic->command.type = MRSH_IF_CLAUSE;
ic->condition = *condition;
ic->body = *body;
ic->else_part = else_part;
return ic;
}
struct mrsh_for_clause *mrsh_for_clause_create(char *name, bool in,
struct mrsh_array *word_list, struct mrsh_array *body) {
struct mrsh_for_clause *fc = calloc(1, sizeof(struct mrsh_for_clause));
fc->command.node.type = MRSH_NODE_COMMAND;
fc->command.type = MRSH_FOR_CLAUSE;
fc->name = name;
fc->in = in;
fc->word_list = *word_list;
fc->body = *body;
return fc;
}
struct mrsh_loop_clause *mrsh_loop_clause_create(enum mrsh_loop_type type,
struct mrsh_array *condition, struct mrsh_array *body) {
struct mrsh_loop_clause *lc = calloc(1, sizeof(struct mrsh_loop_clause));
lc->command.node.type = MRSH_NODE_COMMAND;
lc->command.type = MRSH_LOOP_CLAUSE;
lc->type = type;
lc->condition = *condition;
lc->body = *body;
return lc;
}
struct mrsh_case_clause *mrsh_case_clause_create(struct mrsh_word *word,
struct mrsh_array *items) {
struct mrsh_case_clause *cc = calloc(1, sizeof(struct mrsh_case_clause));
cc->command.node.type = MRSH_NODE_COMMAND;
cc->command.type = MRSH_CASE_CLAUSE;
cc->word = word;
cc->items = *items;
return cc;
}
struct mrsh_function_definition *mrsh_function_definition_create(char *name,
struct mrsh_command *body, struct mrsh_array *io_redirects) {
struct mrsh_function_definition *fd =
calloc(1, sizeof(struct mrsh_function_definition));
fd->command.node.type = MRSH_NODE_COMMAND;
fd->command.type = MRSH_FUNCTION_DEFINITION;
fd->name = name;
fd->body = body;
fd->io_redirects = *io_redirects;
return fd;
}
struct mrsh_simple_command *mrsh_command_get_simple_command(
const struct mrsh_command *cmd) {
assert(cmd->type == MRSH_SIMPLE_COMMAND);
return (struct mrsh_simple_command *)cmd;
}
struct mrsh_brace_group *mrsh_command_get_brace_group(
const struct mrsh_command *cmd) {
assert(cmd->type == MRSH_BRACE_GROUP);
return (struct mrsh_brace_group *)cmd;
}
struct mrsh_subshell *mrsh_command_get_subshell(
const struct mrsh_command *cmd) {
assert(cmd->type == MRSH_SUBSHELL);
return (struct mrsh_subshell *)cmd;
}
struct mrsh_if_clause *mrsh_command_get_if_clause(
const struct mrsh_command *cmd) {
assert(cmd->type == MRSH_IF_CLAUSE);
return (struct mrsh_if_clause *)cmd;
}
struct mrsh_for_clause *mrsh_command_get_for_clause(
const struct mrsh_command *cmd) {
assert(cmd->type == MRSH_FOR_CLAUSE);
return (struct mrsh_for_clause *)cmd;
}
struct mrsh_loop_clause *mrsh_command_get_loop_clause(
const struct mrsh_command *cmd) {
assert(cmd->type == MRSH_LOOP_CLAUSE);
return (struct mrsh_loop_clause *)cmd;
}
struct mrsh_case_clause *mrsh_command_get_case_clause(
const struct mrsh_command *cmd) {
assert(cmd->type == MRSH_CASE_CLAUSE);
return (struct mrsh_case_clause *)cmd;
}
struct mrsh_function_definition *mrsh_command_get_function_definition(
const struct mrsh_command *cmd) {
assert(cmd->type == MRSH_FUNCTION_DEFINITION);
return (struct mrsh_function_definition *)cmd;
}
struct mrsh_pipeline *mrsh_pipeline_create(struct mrsh_array *commands,
bool bang) {
struct mrsh_pipeline *pl = calloc(1, sizeof(struct mrsh_pipeline));
pl->and_or_list.node.type = MRSH_NODE_AND_OR_LIST;
pl->and_or_list.type = MRSH_AND_OR_LIST_PIPELINE;
pl->commands = *commands;
pl->bang = bang;
return pl;
}
struct mrsh_binop *mrsh_binop_create(enum mrsh_binop_type type,
struct mrsh_and_or_list *left, struct mrsh_and_or_list *right) {
struct mrsh_binop *binop = calloc(1, sizeof(struct mrsh_binop));
binop->and_or_list.node.type = MRSH_NODE_AND_OR_LIST;
binop->and_or_list.type = MRSH_AND_OR_LIST_BINOP;
binop->type = type;
binop->left = left;
binop->right = right;
return binop;
}
struct mrsh_pipeline *mrsh_and_or_list_get_pipeline(
const struct mrsh_and_or_list *and_or_list) {
assert(and_or_list->type == MRSH_AND_OR_LIST_PIPELINE);
return (struct mrsh_pipeline *)and_or_list;
}
struct mrsh_binop *mrsh_and_or_list_get_binop(
const struct mrsh_and_or_list *and_or_list) {
assert(and_or_list->type == MRSH_AND_OR_LIST_BINOP);
return (struct mrsh_binop *)and_or_list;
}
static void node_array_for_each(struct mrsh_array *nodes,
mrsh_node_iterator_func iterator, void *user_data) {
for (size_t i = 0; i < nodes->len; ++i) {
struct mrsh_node *node = nodes->data[i];
mrsh_node_for_each(node, iterator, user_data);
}
}
void mrsh_node_for_each(struct mrsh_node *node,
mrsh_node_iterator_func iterator, void *user_data) {
iterator(node, user_data);
switch (node->type) {
case MRSH_NODE_PROGRAM:;
struct mrsh_program *program = mrsh_node_get_program(node);
node_array_for_each(&program->body, iterator, user_data);
return;
case MRSH_NODE_COMMAND_LIST:;
struct mrsh_command_list *list = mrsh_node_get_command_list(node);
mrsh_node_for_each(&list->and_or_list->node, iterator, user_data);
return;
case MRSH_NODE_AND_OR_LIST:;
struct mrsh_and_or_list *and_or_list = mrsh_node_get_and_or_list(node);
switch (and_or_list->type) {
case MRSH_AND_OR_LIST_BINOP:;
struct mrsh_binop *binop = mrsh_and_or_list_get_binop(and_or_list);
mrsh_node_for_each(&binop->left->node, iterator, user_data);
mrsh_node_for_each(&binop->right->node, iterator, user_data);
return;
case MRSH_AND_OR_LIST_PIPELINE:;
struct mrsh_pipeline *pipeline =
mrsh_and_or_list_get_pipeline(and_or_list);
node_array_for_each(&pipeline->commands, iterator, user_data);
return;
}
abort();
case MRSH_NODE_COMMAND:;
struct mrsh_command *cmd = mrsh_node_get_command(node);
switch (cmd->type) {
case MRSH_SIMPLE_COMMAND:;
struct mrsh_simple_command *sc =
mrsh_command_get_simple_command(cmd);
if (sc->name != NULL) {
mrsh_node_for_each(&sc->name->node, iterator, user_data);
}
node_array_for_each(&sc->arguments, iterator, user_data);
// TODO: io_redirects, assignments
return;
case MRSH_BRACE_GROUP:;
struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
node_array_for_each(&bg->body, iterator, user_data);
return;
case MRSH_SUBSHELL:;
struct mrsh_subshell *ss = mrsh_command_get_subshell(cmd);
node_array_for_each(&ss->body, iterator, user_data);
return;
case MRSH_IF_CLAUSE:;
struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
node_array_for_each(&ic->condition, iterator, user_data);
node_array_for_each(&ic->body, iterator, user_data);
if (ic->else_part != NULL) {
mrsh_node_for_each(&ic->else_part->node, iterator, user_data);
}
return;
case MRSH_FOR_CLAUSE:;
struct mrsh_for_clause *fc = mrsh_command_get_for_clause(cmd);
node_array_for_each(&fc->word_list, iterator, user_data);
node_array_for_each(&fc->body, iterator, user_data);
return;
case MRSH_LOOP_CLAUSE:;
struct mrsh_loop_clause *lc = mrsh_command_get_loop_clause(cmd);
node_array_for_each(&lc->condition, iterator, user_data);
node_array_for_each(&lc->body, iterator, user_data);
return;
case MRSH_CASE_CLAUSE:;
struct mrsh_case_clause *cc = mrsh_command_get_case_clause(cmd);
mrsh_node_for_each(&cc->word->node, iterator, user_data);
// TODO: items
return;
case MRSH_FUNCTION_DEFINITION:;
struct mrsh_function_definition *fn =
mrsh_command_get_function_definition(cmd);
mrsh_node_for_each(&fn->body->node, iterator, user_data);
return;
}
abort();
case MRSH_NODE_WORD:;
struct mrsh_word *word = mrsh_node_get_word(node);
switch (word->type) {
case MRSH_WORD_STRING:
return;
case MRSH_WORD_PARAMETER:;
struct mrsh_word_parameter *wp = mrsh_word_get_parameter(word);
if (wp->arg != NULL) {
mrsh_node_for_each(&wp->arg->node, iterator, user_data);
}
return;
case MRSH_WORD_COMMAND:;
struct mrsh_word_command *wc = mrsh_word_get_command(word);
if (wc->program != NULL) {
mrsh_node_for_each(&wc->program->node, iterator, user_data);
}
return;
case MRSH_WORD_ARITHMETIC:;
struct mrsh_word_arithmetic *wa = mrsh_word_get_arithmetic(word);
mrsh_node_for_each(&wa->body->node, iterator, user_data);
return;
case MRSH_WORD_LIST:;
struct mrsh_word_list *wl = mrsh_word_get_list(word);
node_array_for_each(&wl->children, iterator, user_data);
return;
}
abort();
}
abort();
}
static void position_next(struct mrsh_position *dst,
const struct mrsh_position *src) {
*dst = *src;
++dst->offset;
++dst->column;
}
void mrsh_word_range(struct mrsh_word *word, struct mrsh_position *begin,
struct mrsh_position *end) {
if (begin == NULL && end == NULL) {
return;
}
struct mrsh_position _begin, _end;
if (begin == NULL) {
begin = &_begin;
}
if (end == NULL) {
end = &_end;
}
switch (word->type) {
case MRSH_WORD_STRING:;
struct mrsh_word_string *ws = mrsh_word_get_string(word);
*begin = ws->range.begin;
*end = ws->range.end;
return;
case MRSH_WORD_PARAMETER:;
struct mrsh_word_parameter *wp = mrsh_word_get_parameter(word);
*begin = wp->dollar_pos;
if (mrsh_position_valid(&wp->rbrace_pos)) {
position_next(end, &wp->rbrace_pos);
} else {
*end = wp->name_range.end;
}
return;
case MRSH_WORD_COMMAND:;
struct mrsh_word_command *wc = mrsh_word_get_command(word);
*begin = wc->range.begin;
*end = wc->range.end;
return;
case MRSH_WORD_ARITHMETIC:
abort(); // TODO
case MRSH_WORD_LIST:;
struct mrsh_word_list *wl = mrsh_word_get_list(word);
if (wl->children.len == 0) {
*begin = *end = (struct mrsh_position){0};
} else {
struct mrsh_word *first = wl->children.data[0];
struct mrsh_word *last = wl->children.data[wl->children.len - 1];
mrsh_word_range(first, begin, NULL);
mrsh_word_range(last, NULL, end);
}
return;
}
abort();
}
void mrsh_command_range(struct mrsh_command *cmd, struct mrsh_position *begin,
struct mrsh_position *end) {
if (begin == NULL && end == NULL) {
return;
}
struct mrsh_position _begin, _end;
if (begin == NULL) {
begin = &_begin;
}
if (end == NULL) {
end = &_end;
}
switch (cmd->type) {
case MRSH_SIMPLE_COMMAND:;
struct mrsh_simple_command *sc = mrsh_command_get_simple_command(cmd);
if (sc->name != NULL) {
mrsh_word_range(sc->name, begin, end);
} else {
assert(sc->assignments.len > 0);
struct mrsh_assignment *first = sc->assignments.data[0];
*begin = first->name_range.begin;
*end = *begin; // That's a lie, but it'll be fixed by the code below
}
struct mrsh_position maybe_end;
for (size_t i = 0; i < sc->arguments.len; ++i) {
struct mrsh_word *arg = sc->arguments.data[i];
mrsh_word_range(arg, NULL, &maybe_end);
if (maybe_end.offset > end->offset) {
*end = maybe_end;
}
}
for (size_t i = 0; i < sc->io_redirects.len; ++i) {
struct mrsh_io_redirect *redir = sc->io_redirects.data[i];
mrsh_word_range(redir->name, NULL, &maybe_end);
if (maybe_end.offset > end->offset) {
*end = maybe_end;
}
}
for (size_t i = 0; i < sc->assignments.len; ++i) {
struct mrsh_assignment *assign = sc->assignments.data[i];
mrsh_word_range(assign->value, NULL, &maybe_end);
if (maybe_end.offset > end->offset) {
*end = maybe_end;
}
}
return;
case MRSH_BRACE_GROUP:;
struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
*begin = bg->lbrace_pos;
position_next(end, &bg->rbrace_pos);
return;
case MRSH_SUBSHELL:;
struct mrsh_subshell *s = mrsh_command_get_subshell(cmd);
*begin = s->lparen_pos;
position_next(end, &s->rparen_pos);
return;
case MRSH_IF_CLAUSE:;
struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
*begin = ic->if_range.begin;
*end = ic->fi_range.end;
return;
case MRSH_FOR_CLAUSE:;
struct mrsh_for_clause *fc = mrsh_command_get_for_clause(cmd);
*begin = fc->for_range.begin;
*end = fc->done_range.end;
return;
case MRSH_LOOP_CLAUSE:;
struct mrsh_loop_clause *lc = mrsh_command_get_loop_clause(cmd);
*begin = lc->while_until_range.begin;
*end = lc->done_range.end;
return;
case MRSH_CASE_CLAUSE:;
struct mrsh_case_clause *cc = mrsh_command_get_case_clause(cmd);
*begin = cc->case_range.begin;
*end = cc->esac_range.end;
return;
case MRSH_FUNCTION_DEFINITION:;
struct mrsh_function_definition *fd =
mrsh_command_get_function_definition(cmd);
*begin = fd->name_range.begin;
mrsh_command_range(fd->body, NULL, end);
}
abort();
}
static void buffer_append_str(struct mrsh_buffer *buf, const char *str) {
mrsh_buffer_append(buf, str, strlen(str));
}
static void word_str(const struct mrsh_word *word, struct mrsh_buffer *buf) {
switch (word->type) {
case MRSH_WORD_STRING:;
const struct mrsh_word_string *ws = mrsh_word_get_string(word);
buffer_append_str(buf, ws->str);
return;
case MRSH_WORD_PARAMETER:
case MRSH_WORD_COMMAND:
case MRSH_WORD_ARITHMETIC:
abort();
case MRSH_WORD_LIST:;
const struct mrsh_word_list *wl = mrsh_word_get_list(word);
for (size_t i = 0; i < wl->children.len; ++i) {
const struct mrsh_word *child = wl->children.data[i];
word_str(child, buf);
}
return;
}
abort();
}
char *mrsh_word_str(const struct mrsh_word *word) {
struct mrsh_buffer buf = {0};
word_str(word, &buf);
mrsh_buffer_append_char(&buf, '\0');
return mrsh_buffer_steal(&buf);
}
static const char *binop_type_str(enum mrsh_binop_type t) {
switch (t) {
case MRSH_BINOP_AND:
return "&&";
case MRSH_BINOP_OR:
return "||";
}
abort();
}
static void node_format(struct mrsh_node *node, struct mrsh_buffer *buf);
static void node_array_format(struct mrsh_array *array, const char *sep,
struct mrsh_buffer *buf) {
for (size_t i = 0; i < array->len; i++) {
struct mrsh_node *node = array->data[i];
if (i > 0) {
buffer_append_str(buf, sep);
}
node_format(node, buf);
}
}
static void node_format(struct mrsh_node *node, struct mrsh_buffer *buf) {
switch (node->type) {
case MRSH_NODE_PROGRAM:;
struct mrsh_program *program = mrsh_node_get_program(node);
node_array_format(&program->body, " ", buf);
return;
case MRSH_NODE_COMMAND_LIST:;
struct mrsh_command_list *list = mrsh_node_get_command_list(node);
node_format(&list->and_or_list->node, buf);
buffer_append_str(buf, list->ampersand ? " &" : ";");
return;
case MRSH_NODE_AND_OR_LIST:;
struct mrsh_and_or_list *and_or_list = mrsh_node_get_and_or_list(node);
switch (and_or_list->type) {
case MRSH_AND_OR_LIST_BINOP:;
struct mrsh_binop *binop = mrsh_and_or_list_get_binop(and_or_list);
node_format(&binop->left->node, buf);
mrsh_buffer_append_char(buf, ' ');
buffer_append_str(buf, binop_type_str(binop->type));
mrsh_buffer_append_char(buf, ' ');
node_format(&binop->right->node, buf);
return;
case MRSH_AND_OR_LIST_PIPELINE:;
struct mrsh_pipeline *pipeline =
mrsh_and_or_list_get_pipeline(and_or_list);
if (pipeline->bang) {
buffer_append_str(buf, "! ");
}
node_array_format(&pipeline->commands, " | ", buf);
return;
}
abort();
case MRSH_NODE_COMMAND:;
struct mrsh_command *cmd = mrsh_node_get_command(node);
switch (cmd->type) {
case MRSH_SIMPLE_COMMAND:;
struct mrsh_simple_command *sc =
mrsh_command_get_simple_command(cmd);
if (sc->name != NULL) {
node_format(&sc->name->node, buf);
mrsh_buffer_append_char(buf, ' ');
}
node_array_format(&sc->arguments, " ", buf);
// TODO: io_redirects, assignments
return;
case MRSH_BRACE_GROUP:;
struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
buffer_append_str(buf, "{ ");
node_array_format(&bg->body, " ", buf);
buffer_append_str(buf, "; }");
return;
case MRSH_SUBSHELL:;
struct mrsh_subshell *ss = mrsh_command_get_subshell(cmd);
mrsh_buffer_append_char(buf, '(');
node_array_format(&ss->body, " ", buf);
mrsh_buffer_append_char(buf, ')');
return;
case MRSH_IF_CLAUSE:;
struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
buffer_append_str(buf, "if ");
node_array_format(&ic->condition, " ", buf);
buffer_append_str(buf, "then ");
node_array_format(&ic->body, " ", buf);
if (ic->else_part != NULL) {
// TODO: elif
buffer_append_str(buf, "else ");
node_format(&ic->else_part->node, buf);
}
buffer_append_str(buf, "fi");
return;
case MRSH_FOR_CLAUSE:;
//struct mrsh_for_clause *fc = mrsh_command_get_for_clause(cmd);
// TODO
return;
case MRSH_LOOP_CLAUSE:;
struct mrsh_loop_clause *lc = mrsh_command_get_loop_clause(cmd);
buffer_append_str(buf,
lc->type == MRSH_LOOP_WHILE ? "while " : "until ");
node_array_format(&lc->condition, " ", buf);
buffer_append_str(buf, "do ");
node_array_format(&lc->body, " ", buf);
buffer_append_str(buf, "done");
return;
case MRSH_CASE_CLAUSE:;
//struct mrsh_case_clause *cc = mrsh_command_get_case_clause(cmd);
// TODO
return;
case MRSH_FUNCTION_DEFINITION:;
struct mrsh_function_definition *fn =
mrsh_command_get_function_definition(cmd);
buffer_append_str(buf, fn->name);
buffer_append_str(buf, "()");
node_format(&fn->body->node, buf);
// TODO: io-redirect
return;
}
abort();
case MRSH_NODE_WORD:;
// TODO: quoting
struct mrsh_word *word = mrsh_node_get_word(node);
switch (word->type) {
case MRSH_WORD_STRING:;
struct mrsh_word_string *ws = mrsh_word_get_string(word);
if (ws->single_quoted) {
mrsh_buffer_append_char(buf, '\'');
}
buffer_append_str(buf, ws->str);
if (ws->single_quoted) {
mrsh_buffer_append_char(buf, '\'');
}
return;
case MRSH_WORD_PARAMETER:;
struct mrsh_word_parameter *wp = mrsh_word_get_parameter(word);
buffer_append_str(buf, "${");
if (wp->arg != NULL) {
node_format(&wp->arg->node, buf);
}
buffer_append_str(buf, "}");
return;
case MRSH_WORD_COMMAND:;
struct mrsh_word_command *wc = mrsh_word_get_command(word);
buffer_append_str(buf, wc->back_quoted ? "`" : "$(");
if (wc->program != NULL) {
node_format(&wc->program->node, buf);
}
buffer_append_str(buf, wc->back_quoted ? "`" : ")");
return;
case MRSH_WORD_ARITHMETIC:;
struct mrsh_word_arithmetic *wa = mrsh_word_get_arithmetic(word);
node_format(&wa->body->node, buf);
return;
case MRSH_WORD_LIST:;
struct mrsh_word_list *wl = mrsh_word_get_list(word);
if (wl->double_quoted) {
mrsh_buffer_append_char(buf, '"');
}
node_array_format(&wl->children, "", buf);
if (wl->double_quoted) {
mrsh_buffer_append_char(buf, '"');
}
return;
}
abort();
}
abort();
}
char *mrsh_node_format(struct mrsh_node *node) {
struct mrsh_buffer buf = {0};
node_format(node, &buf);
mrsh_buffer_append_char(&buf, '\0');
return mrsh_buffer_steal(&buf);
}
struct mrsh_node *mrsh_node_copy(const struct mrsh_node *node) {
switch (node->type) {
case MRSH_NODE_PROGRAM:;
struct mrsh_program *prog = mrsh_node_get_program(node);
struct mrsh_program *prog_copy = mrsh_program_copy(prog);
return &prog_copy->node;
case MRSH_NODE_COMMAND_LIST:;
struct mrsh_command_list *cl = mrsh_node_get_command_list(node);
struct mrsh_command_list *cl_copy = mrsh_command_list_copy(cl);
return &cl_copy->node;
case MRSH_NODE_AND_OR_LIST:;
struct mrsh_and_or_list *aol = mrsh_node_get_and_or_list(node);
struct mrsh_and_or_list *aol_copy = mrsh_and_or_list_copy(aol);
return &aol_copy->node;
case MRSH_NODE_COMMAND:;
struct mrsh_command *cmd = mrsh_node_get_command(node);
struct mrsh_command *cmd_copy = mrsh_command_copy(cmd);
return &cmd_copy->node;
case MRSH_NODE_WORD:;
struct mrsh_word *word = mrsh_node_get_word(node);
struct mrsh_word *word_copy = mrsh_word_copy(word);
return &word_copy->node;
}
abort();
}
struct mrsh_word *mrsh_word_copy(const struct mrsh_word *word) {
switch (word->type) {
case MRSH_WORD_STRING:;
struct mrsh_word_string *ws = mrsh_word_get_string(word);
struct mrsh_word_string *ws_copy =
mrsh_word_string_create(strdup(ws->str), ws->single_quoted);
return &ws_copy->word;
case MRSH_WORD_PARAMETER:;
struct mrsh_word_parameter *wp = mrsh_word_get_parameter(word);
struct mrsh_word *arg = NULL;
if (wp->arg != NULL) {
arg = mrsh_word_copy(wp->arg);
}
struct mrsh_word_parameter *wp_copy = mrsh_word_parameter_create(
strdup(wp->name), wp->op, wp->colon, arg);
return &wp_copy->word;
case MRSH_WORD_COMMAND:;
struct mrsh_word_command *wc = mrsh_word_get_command(word);
struct mrsh_word_command *wc_copy = mrsh_word_command_create(
mrsh_program_copy(wc->program), wc->back_quoted);
return &wc_copy->word;
case MRSH_WORD_ARITHMETIC:;
struct mrsh_word_arithmetic *wa = mrsh_word_get_arithmetic(word);
struct mrsh_word_arithmetic *wa_copy = mrsh_word_arithmetic_create(
mrsh_word_copy(wa->body));
return &wa_copy->word;
case MRSH_WORD_LIST:;
struct mrsh_word_list *wl = mrsh_word_get_list(word);
struct mrsh_array children = {0};
mrsh_array_reserve(&children, wl->children.len);
for (size_t i = 0; i < wl->children.len; ++i) {
struct mrsh_word *child = wl->children.data[i];
mrsh_array_add(&children, mrsh_word_copy(child));
}
struct mrsh_word_list *wl_copy =
mrsh_word_list_create(&children, wl->double_quoted);
return &wl_copy->word;
}
abort();
}
struct mrsh_io_redirect *mrsh_io_redirect_copy(
const struct mrsh_io_redirect *redir) {
struct mrsh_io_redirect *redir_copy =
calloc(1, sizeof(struct mrsh_io_redirect));
redir_copy->io_number = redir->io_number;
redir_copy->op = redir->op;
redir_copy->name = mrsh_word_copy(redir->name);
mrsh_array_reserve(&redir_copy->here_document, redir->here_document.len);
for (size_t i = 0; i < redir->here_document.len; ++i) {
struct mrsh_word *line = redir->here_document.data[i];
mrsh_array_add(&redir_copy->here_document, mrsh_word_copy(line));
}
return redir_copy;
}
struct mrsh_assignment *mrsh_assignment_copy(
const struct mrsh_assignment *assign) {
struct mrsh_assignment *assign_copy =
calloc(1, sizeof(struct mrsh_assignment));
assign_copy->name = strdup(assign->name);
assign_copy->value = mrsh_word_copy(assign->value);
return assign_copy;
}
static void command_list_array_copy(struct mrsh_array *dst,
const struct mrsh_array *src) {
mrsh_array_reserve(dst, src->len);
for (size_t i = 0; i < src->len; ++i) {
struct mrsh_command_list *l = src->data[i];
mrsh_array_add(dst, mrsh_command_list_copy(l));
}
}
static struct mrsh_case_item *case_item_copy(const struct mrsh_case_item *ci) {
struct mrsh_case_item *ci_copy = calloc(1, sizeof(struct mrsh_case_item));
mrsh_array_reserve(&ci_copy->patterns, ci->patterns.len);
for (size_t i = 0; i < ci->patterns.len; ++i) {
struct mrsh_word *pattern = ci->patterns.data[i];
mrsh_array_add(&ci_copy->patterns, mrsh_word_copy(pattern));
}
command_list_array_copy(&ci_copy->body, &ci->body);
return ci_copy;
}
struct mrsh_command *mrsh_command_copy(const struct mrsh_command *cmd) {
struct mrsh_array io_redirects = {0};
switch (cmd->type) {
case MRSH_SIMPLE_COMMAND:;
struct mrsh_simple_command *sc = mrsh_command_get_simple_command(cmd);
struct mrsh_word *name = NULL;
if (sc->name != NULL) {
name = mrsh_word_copy(sc->name);
}
struct mrsh_array arguments = {0};
mrsh_array_reserve(&arguments, sc->arguments.len);
for (size_t i = 0; i < sc->arguments.len; ++i) {
struct mrsh_word *arg = sc->arguments.data[i];
mrsh_array_add(&arguments, mrsh_word_copy(arg));
}
mrsh_array_reserve(&io_redirects, sc->io_redirects.len);
for (size_t i = 0; i < sc->io_redirects.len; ++i) {
struct mrsh_io_redirect *redir = sc->io_redirects.data[i];
mrsh_array_add(&io_redirects, mrsh_io_redirect_copy(redir));
}
struct mrsh_array assignments = {0};
mrsh_array_reserve(&assignments, sc->assignments.len);
for (size_t i = 0; i < sc->assignments.len; ++i) {
struct mrsh_assignment *assign = sc->assignments.data[i];
mrsh_array_add(&assignments, mrsh_assignment_copy(assign));
}
struct mrsh_simple_command *sc_copy = mrsh_simple_command_create(
name, &arguments, &io_redirects, &assignments);
return &sc_copy->command;
case MRSH_BRACE_GROUP:;
struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
struct mrsh_array bg_body = {0};
command_list_array_copy(&bg_body, &bg->body);
struct mrsh_brace_group *bg_copy = mrsh_brace_group_create(&bg_body);
return &bg_copy->command;
case MRSH_SUBSHELL:;
struct mrsh_subshell *ss = mrsh_command_get_subshell(cmd);
struct mrsh_array ss_body = {0};
command_list_array_copy(&ss_body, &ss->body);
struct mrsh_subshell *ss_copy = mrsh_subshell_create(&ss_body);
return &ss_copy->command;
case MRSH_IF_CLAUSE:;
struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
struct mrsh_array ic_condition = {0};
command_list_array_copy(&ic_condition, &ic->condition);
struct mrsh_array ic_body = {0};
command_list_array_copy(&ic_body, &ic->body);
struct mrsh_command *else_part = NULL;
if (ic->else_part != NULL) {
else_part = mrsh_command_copy(ic->else_part);
}
struct mrsh_if_clause *ic_copy =
mrsh_if_clause_create(&ic_condition, &ic_body, else_part);
return &ic_copy->command;
case MRSH_FOR_CLAUSE:;
struct mrsh_for_clause *fc = mrsh_command_get_for_clause(cmd);
struct mrsh_array word_list = {0};
mrsh_array_reserve(&word_list, fc->word_list.len);
for (size_t i = 0; i < fc->word_list.len; ++i) {
struct mrsh_word *word = fc->word_list.data[i];
mrsh_array_add(&word_list, mrsh_word_copy(word));
}
struct mrsh_array fc_body = {0};
command_list_array_copy(&fc_body, &fc->body);
struct mrsh_for_clause *fc_copy = mrsh_for_clause_create(
strdup(fc->name), fc->in, &word_list, &fc_body);
return &fc_copy->command;
case MRSH_LOOP_CLAUSE:;
struct mrsh_loop_clause *lc = mrsh_command_get_loop_clause(cmd);
struct mrsh_array lc_condition = {0};
command_list_array_copy(&lc_condition, &lc->condition);
struct mrsh_array lc_body = {0};
command_list_array_copy(&lc_body, &lc->body);
struct mrsh_loop_clause *lc_copy =
mrsh_loop_clause_create(lc->type, &lc_condition, &lc_body);
return &lc_copy->command;
case MRSH_CASE_CLAUSE:;
struct mrsh_case_clause *cc = mrsh_command_get_case_clause(cmd);
struct mrsh_array items = {0};
mrsh_array_reserve(&items, cc->items.len);
for (size_t i = 0; i < cc->items.len; ++i) {
struct mrsh_case_item *ci = cc->items.data[i];
mrsh_array_add(&items, case_item_copy(ci));
}
struct mrsh_case_clause *cc_copy =
mrsh_case_clause_create(mrsh_word_copy(cc->word), &items);
return &cc_copy->command;
case MRSH_FUNCTION_DEFINITION:;
struct mrsh_function_definition *fd =
mrsh_command_get_function_definition(cmd);
mrsh_array_reserve(&io_redirects, fd->io_redirects.len);
for (size_t i = 0; i < fd->io_redirects.len; ++i) {
struct mrsh_io_redirect *redir = fd->io_redirects.data[i];
mrsh_array_add(&io_redirects, mrsh_io_redirect_copy(redir));
}
struct mrsh_function_definition *fd_copy =
mrsh_function_definition_create(strdup(fd->name),
mrsh_command_copy(fd->body), &io_redirects);
return &fd_copy->command;
}
abort();
}
struct mrsh_and_or_list *mrsh_and_or_list_copy(
const struct mrsh_and_or_list *and_or_list) {
switch (and_or_list->type) {
case MRSH_AND_OR_LIST_PIPELINE:;
struct mrsh_pipeline *pl = mrsh_and_or_list_get_pipeline(and_or_list);
struct mrsh_array commands = {0};
mrsh_array_reserve(&commands, pl->commands.len);
for (size_t i = 0; i < pl->commands.len; ++i) {
struct mrsh_command *cmd = pl->commands.data[i];
mrsh_array_add(&commands, mrsh_command_copy(cmd));
}
struct mrsh_pipeline *p_copy =
mrsh_pipeline_create(&commands, pl->bang);
return &p_copy->and_or_list;
case MRSH_AND_OR_LIST_BINOP:;
struct mrsh_binop *binop = mrsh_and_or_list_get_binop(and_or_list);
struct mrsh_binop *binop_copy = mrsh_binop_create(binop->type,
mrsh_and_or_list_copy(binop->left),
mrsh_and_or_list_copy(binop->right));
return &binop_copy->and_or_list;
}
abort();
}
struct mrsh_command_list *mrsh_command_list_copy(
const struct mrsh_command_list *l) {
struct mrsh_command_list *l_copy = mrsh_command_list_create();
l_copy->and_or_list = mrsh_and_or_list_copy(l->and_or_list);
l_copy->ampersand = l->ampersand;
return l_copy;
}
struct mrsh_program *mrsh_program_copy(const struct mrsh_program *prog) {
struct mrsh_program *prog_copy = mrsh_program_create();
command_list_array_copy(&prog_copy->body, &prog->body);
return prog_copy;
}
================================================
FILE: ast_print.c
================================================
#include <assert.h>
#include <mrsh/ast.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define L_LINE "│ "
#define L_VAL "├─"
#define L_LAST "└─"
#define L_GAP " "
static size_t make_sub_prefix(const char *prefix, bool last, char *buf) {
if (buf != NULL) {
memcpy(buf, prefix, strlen(prefix) + 1);
strcat(buf, last ? L_GAP : L_LINE);
}
return strlen(prefix) + strlen(L_LINE) + 1;
}
static void print_prefix(const char *prefix, bool last) {
printf("%s%s", prefix, last ? L_LAST : L_VAL);
}
static void print_range(const struct mrsh_range *range) {
printf("[%d:%d → %d:%d]", range->begin.line, range->begin.column,
range->end.line, range->end.column);
}
static const char *word_parameter_op_str(enum mrsh_word_parameter_op op) {
switch (op) {
case MRSH_PARAM_NONE:
return NULL;
case MRSH_PARAM_MINUS:
return "-";
case MRSH_PARAM_EQUAL:
return "=";
case MRSH_PARAM_QMARK:
return "?";
case MRSH_PARAM_PLUS:
return "+";
case MRSH_PARAM_LEADING_HASH:
return "# (leading)";
case MRSH_PARAM_PERCENT:
return "%";
case MRSH_PARAM_DPERCENT:
return "%%";
case MRSH_PARAM_HASH:
return "#";
case MRSH_PARAM_DHASH:
return "##";
}
abort();
}
static void print_program(struct mrsh_program *prog, const char *prefix);
static void print_word(struct mrsh_word *word, const char *prefix) {
char sub_prefix[make_sub_prefix(prefix, true, NULL)];
switch (word->type) {
case MRSH_WORD_STRING:;
struct mrsh_word_string *ws = mrsh_word_get_string(word);
printf("word_string%s ", ws->single_quoted ? " (quoted)" : "");
print_range(&ws->range);
printf(" %s\n", ws->str);
break;
case MRSH_WORD_PARAMETER:;
struct mrsh_word_parameter *wp = mrsh_word_get_parameter(word);
printf("word_parameter\n");
print_prefix(prefix, wp->op == MRSH_PARAM_NONE && wp->arg == NULL);
printf("name %s\n", wp->name);
if (wp->op != MRSH_PARAM_NONE) {
print_prefix(prefix, wp->arg == NULL);
printf("op %s%s\n",
wp->colon ? ":" : "", word_parameter_op_str(wp->op));
}
if (wp->arg != NULL) {
make_sub_prefix(prefix, true, sub_prefix);
print_prefix(prefix, true);
printf("arg ─ ");
print_word(wp->arg, sub_prefix);
}
break;
case MRSH_WORD_COMMAND:;
struct mrsh_word_command *wc = mrsh_word_get_command(word);
printf("word_command%s ─ ", wc->back_quoted ? " (quoted)" : "");
print_program(wc->program, prefix);
break;
case MRSH_WORD_ARITHMETIC:;
struct mrsh_word_arithmetic *wa = mrsh_word_get_arithmetic(word);
printf("word_arithmetic ─ ");
print_word(wa->body, prefix);
break;
case MRSH_WORD_LIST:;
struct mrsh_word_list *wl = mrsh_word_get_list(word);
printf("word_list%s\n", wl->double_quoted ? " (quoted)" : "");
for (size_t i = 0; i < wl->children.len; ++i) {
struct mrsh_word *child = wl->children.data[i];
bool last = i == wl->children.len - 1;
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
print_word(child, sub_prefix);
}
break;
}
}
static const char *io_redirect_op_str(enum mrsh_io_redirect_op op) {
switch (op) {
case MRSH_IO_LESS:
return "<";
case MRSH_IO_GREAT:
return ">";
case MRSH_IO_CLOBBER:
return ">|";
case MRSH_IO_DGREAT:
return ">>";
case MRSH_IO_LESSAND:
return "<&";
case MRSH_IO_GREATAND:
return ">&";
case MRSH_IO_LESSGREAT:
return "<>";
case MRSH_IO_DLESS:
return "<<";
case MRSH_IO_DLESSDASH:
return "<<-";
}
abort();
}
static void print_word_array(struct mrsh_array *words, const char *prefix);
static void print_io_redirect(struct mrsh_io_redirect *redir,
const char *prefix) {
printf("io_redirect\n");
print_prefix(prefix, false);
printf("io_number %d\n", redir->io_number);
print_prefix(prefix, false);
printf("op %s\n", io_redirect_op_str(redir->op));
bool name_is_last = redir->here_document.len == 0;
char sub_prefix[make_sub_prefix(prefix, name_is_last, NULL)];
make_sub_prefix(prefix, name_is_last, sub_prefix);
print_prefix(prefix, name_is_last);
printf("name ─ ");
print_word(redir->name, sub_prefix);
if (redir->here_document.len > 0) {
make_sub_prefix(prefix, true, sub_prefix);
print_prefix(prefix, true);
printf("here_document\n");
print_word_array(&redir->here_document, sub_prefix);
}
}
static void print_assignment(struct mrsh_assignment *assign,
const char *prefix) {
printf("assignment\n");
print_prefix(prefix, false);
printf("name %s\n", assign->name);
char sub_prefix[make_sub_prefix(prefix, true, NULL)];
make_sub_prefix(prefix, true, sub_prefix);
print_prefix(prefix, true);
printf("value ─ ");
print_word(assign->value, sub_prefix);
}
static void print_simple_command(struct mrsh_simple_command *cmd,
const char *prefix) {
printf("simple_command\n");
char sub_prefix[make_sub_prefix(prefix, false, NULL)];
if (cmd->name != NULL) {
bool last = cmd->arguments.len == 0 && cmd->io_redirects.len == 0
&& cmd->assignments.len == 0;
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
printf("name ─ ");
print_word(cmd->name, sub_prefix);
}
for (size_t i = 0; i < cmd->arguments.len; ++i) {
struct mrsh_word *arg = cmd->arguments.data[i];
bool last = i == cmd->arguments.len - 1 && cmd->io_redirects.len == 0
&& cmd->assignments.len == 0;
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
printf("argument %zu ─ ", i + 1);
print_word(arg, sub_prefix);
}
for (size_t i = 0; i < cmd->io_redirects.len; ++i) {
struct mrsh_io_redirect *redir = cmd->io_redirects.data[i];
bool last = i == cmd->io_redirects.len - 1 && cmd->assignments.len == 0;
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
print_io_redirect(redir, sub_prefix);
}
for (size_t i = 0; i < cmd->assignments.len; ++i) {
struct mrsh_assignment *assign = cmd->assignments.data[i];
bool last = i == cmd->assignments.len - 1;
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
print_assignment(assign, sub_prefix);
}
}
static void print_command_list(struct mrsh_command_list *l, const char *prefix);
static void print_command_list_array(struct mrsh_array *array,
const char *prefix) {
for (size_t i = 0; i < array->len; ++i) {
struct mrsh_command_list *l = array->data[i];
bool last = i == array->len - 1;
char sub_prefix[make_sub_prefix(prefix, last, NULL)];
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
print_command_list(l, sub_prefix);
}
}
static void print_command(struct mrsh_command *cmd, const char *prefix);
static void print_if_clause(struct mrsh_if_clause *ic, const char *prefix) {
printf("if_clause\n");
char sub_prefix[make_sub_prefix(prefix, false, NULL)];
make_sub_prefix(prefix, false, sub_prefix);
print_prefix(prefix, false);
printf("condition\n");
print_command_list_array(&ic->condition, sub_prefix);
bool last = ic->else_part == NULL;
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
printf("body\n");
print_command_list_array(&ic->body, sub_prefix);
if (ic->else_part != NULL) {
make_sub_prefix(prefix, true, sub_prefix);
print_prefix(prefix, true);
printf("else_part ─ ");
print_command(ic->else_part, sub_prefix);
}
}
static void print_word_array(struct mrsh_array *words, const char *prefix) {
for (size_t i = 0; i < words->len; ++i) {
struct mrsh_word *word = words->data[i];
bool last = i == words->len - 1;
char sub_prefix[make_sub_prefix(prefix, last, NULL)];
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
print_word(word, sub_prefix);
}
}
static void print_for_clause(struct mrsh_for_clause *fc, const char *prefix) {
printf("for_clause\n");
char sub_prefix[make_sub_prefix(prefix, false, NULL)];
make_sub_prefix(prefix, false, sub_prefix);
print_prefix(prefix, false);
printf("name %s\n", fc->name);
if (fc->in) {
print_prefix(prefix, false);
printf("in\n");
print_word_array(&fc->word_list, sub_prefix);
}
make_sub_prefix(prefix, true, sub_prefix);
print_prefix(prefix, true);
printf("body\n");
print_command_list_array(&fc->body, sub_prefix);
}
static const char *loop_type_str(enum mrsh_loop_type type) {
switch (type) {
case MRSH_LOOP_UNTIL:
return "until";
case MRSH_LOOP_WHILE:
return "while";
}
abort();
}
static void print_loop_clause(struct mrsh_loop_clause *lc, const char *prefix) {
printf("loop_clause %s\n", loop_type_str(lc->type));
char sub_prefix[make_sub_prefix(prefix, false, NULL)];
make_sub_prefix(prefix, false, sub_prefix);
print_prefix(prefix, false);
printf("condition\n");
print_command_list_array(&lc->condition, sub_prefix);
make_sub_prefix(prefix, true, sub_prefix);
print_prefix(prefix, true);
printf("body\n");
print_command_list_array(&lc->body, sub_prefix);
}
static void print_case_item(struct mrsh_case_item *item, const char *prefix) {
printf("case_item\n");
char sub_prefix[make_sub_prefix(prefix, false, NULL)];
make_sub_prefix(prefix, false, sub_prefix);
print_prefix(prefix, false);
printf("patterns\n");
print_word_array(&item->patterns, sub_prefix);
make_sub_prefix(prefix, true, sub_prefix);
print_prefix(prefix, true);
printf("body\n");
print_command_list_array(&item->body, sub_prefix);
}
static void print_case_item_array(struct mrsh_array *items,
const char *prefix) {
for (size_t i = 0; i < items->len; ++i) {
struct mrsh_case_item *item = items->data[i];
bool last = i == items->len - 1;
char sub_prefix[make_sub_prefix(prefix, last, NULL)];
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
print_case_item(item, sub_prefix);
}
}
static void print_case_clause(struct mrsh_case_clause *cc, const char *prefix) {
printf("case_clause\n");
char sub_prefix[make_sub_prefix(prefix, false, NULL)];
make_sub_prefix(prefix, false, sub_prefix);
print_prefix(prefix, false);
printf("word ─ ");
print_word(cc->word, sub_prefix);
make_sub_prefix(prefix, true, sub_prefix);
print_prefix(prefix, true);
printf("items\n");
print_case_item_array(&cc->items, sub_prefix);
}
static void print_function_definition(struct mrsh_function_definition *fd,
const char *prefix) {
printf("function_definition %s ─ ", fd->name);
print_command(fd->body, prefix);
// TODO: print io_redirects
}
static void print_command(struct mrsh_command *cmd, const char *prefix) {
switch (cmd->type) {
case MRSH_SIMPLE_COMMAND:;
struct mrsh_simple_command *sc = mrsh_command_get_simple_command(cmd);
print_simple_command(sc, prefix);
break;
case MRSH_BRACE_GROUP:;
struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
printf("brace_group\n");
print_command_list_array(&bg->body, prefix);
break;
case MRSH_SUBSHELL:;
struct mrsh_subshell *s = mrsh_command_get_subshell(cmd);
printf("subshell\n");
print_command_list_array(&s->body, prefix);
break;
case MRSH_IF_CLAUSE:;
struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
print_if_clause(ic, prefix);
break;
case MRSH_FOR_CLAUSE:;
struct mrsh_for_clause *fc = mrsh_command_get_for_clause(cmd);
print_for_clause(fc, prefix);
break;
case MRSH_LOOP_CLAUSE:;
struct mrsh_loop_clause *lc = mrsh_command_get_loop_clause(cmd);
print_loop_clause(lc, prefix);
break;
case MRSH_CASE_CLAUSE:;
struct mrsh_case_clause *cc = mrsh_command_get_case_clause(cmd);
print_case_clause(cc, prefix);
break;
case MRSH_FUNCTION_DEFINITION:;
struct mrsh_function_definition *fd =
mrsh_command_get_function_definition(cmd);
print_function_definition(fd, prefix);
break;
}
}
static void print_pipeline(struct mrsh_pipeline *pl, const char *prefix) {
printf("pipeline%s\n", pl->bang ? " !" : "");
for (size_t i = 0; i < pl->commands.len; ++i) {
struct mrsh_command *cmd = pl->commands.data[i];
bool last = i == pl->commands.len - 1;
char sub_prefix[make_sub_prefix(prefix, last, NULL)];
make_sub_prefix(prefix, last, sub_prefix);
print_prefix(prefix, last);
print_command(cmd, sub_prefix);
}
}
static const char *binop_type_str(enum mrsh_binop_type type) {
switch (type) {
case MRSH_BINOP_AND:
return "&&";
case MRSH_BINOP_OR:
return "||";
}
return NULL;
}
static void print_and_or_list(struct mrsh_and_or_list *and_or_list, const char *prefix);
static void print_binop(struct mrsh_binop *binop, const char *prefix) {
printf("binop %s\n", binop_type_str(binop->type));
char sub_prefix[make_sub_prefix(prefix, false, NULL)];
make_sub_prefix(prefix, false, sub_prefix);
print_prefix(prefix, false);
print_and_or_list(binop->left, sub_prefix);
make_sub_prefix(prefix, true, sub_prefix);
print_prefix(prefix, true);
print_and_or_list(binop->right, sub_prefix);
}
static void print_and_or_list(struct mrsh_and_or_list *and_or_list, const char *prefix) {
switch (and_or_list->type) {
case MRSH_AND_OR_LIST_PIPELINE:;
struct mrsh_pipeline *pl = mrsh_and_or_list_get_pipeline(and_or_list);
print_pipeline(pl, prefix);
break;
case MRSH_AND_OR_LIST_BINOP:;
struct mrsh_binop *binop = mrsh_and_or_list_get_binop(and_or_list);
print_binop(binop, prefix);
break;
}
}
static void print_command_list(struct mrsh_command_list *list,
const char *prefix) {
printf("command_list%s ─ ", list->ampersand ? " &" : "");
print_and_or_list(list->and_or_list, prefix);
}
static void print_program(struct mrsh_program *prog, const char *prefix) {
printf("program\n");
print_command_list_array(&prog->body, prefix);
}
void mrsh_program_print(struct mrsh_program *prog) {
print_program(prog, "");
}
================================================
FILE: buffer.c
================================================
#include <mrsh/buffer.h>
#include <stdlib.h>
#include <string.h>
char *mrsh_buffer_reserve(struct mrsh_buffer *buf, size_t size) {
size_t new_len = buf->len + size;
if (new_len > buf->cap) {
size_t new_cap = 2 * buf->cap;
if (new_cap == 0) {
new_cap = 32;
}
if (new_cap < new_len) {
new_cap = new_len;
}
char *new_buf = realloc(buf->data, new_cap);
if (new_buf == NULL) {
return NULL;
}
buf->data = new_buf;
buf->cap = new_cap;
}
return &buf->data[buf->len];
}
char *mrsh_buffer_add(struct mrsh_buffer *buf, size_t size) {
char *data = mrsh_buffer_reserve(buf, size);
if (data == NULL) {
return NULL;
}
buf->len += size;
return data;
}
bool mrsh_buffer_append(struct mrsh_buffer *buf, const char *data, size_t size) {
char *dst = mrsh_buffer_add(buf, size);
if (dst == NULL) {
return false;
}
memcpy(dst, data, size);
return true;
}
bool mrsh_buffer_append_char(struct mrsh_buffer *buf, char c) {
char *dst = mrsh_buffer_add(buf, sizeof(char));
if (dst == NULL) {
return false;
}
*dst = c;
return true;
}
char *mrsh_buffer_steal(struct mrsh_buffer *buf) {
char *data = buf->data;
buf->data = NULL;
buf->cap = buf->len = 0;
return data;
}
void mrsh_buffer_finish(struct mrsh_buffer *buf) {
free(buf->data);
buf->data = NULL;
buf->cap = buf->len = 0;
}
================================================
FILE: builtin/alias.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <mrsh/builtin.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/shell.h"
static const char alias_usage[] = "usage: alias [alias-name[=string]...]\n";
static void print_alias_iterator(const char *key, void *_value,
void *user_data) {
const char *value = _value;
printf("%s=", key);
print_escaped(value);
printf("\n");
}
int builtin_alias(struct mrsh_state *state, int argc, char *argv[]) {
struct mrsh_state_priv *priv = state_get_priv(state);
_mrsh_optind = 0;
if (_mrsh_getopt(argc, argv, ":") != -1) {
fprintf(stderr, "alias: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, "%s", alias_usage);
return 1;
}
if (_mrsh_optind == argc) {
mrsh_hashtable_for_each(&priv->aliases, print_alias_iterator, NULL);
return 0;
}
for (int i = _mrsh_optind; i < argc; ++i) {
char *alias = argv[i];
char *equal = strchr(alias, '=');
if (equal != NULL) {
char *value = strdup(equal + 1);
*equal = '\0';
char *old_value = mrsh_hashtable_set(&priv->aliases, alias, value);
free(old_value);
} else {
const char *value = mrsh_hashtable_get(&priv->aliases, alias);
if (value == NULL) {
fprintf(stderr, "%s: %s not found\n", argv[0], alias);
return 1;
}
printf("%s=", alias);
print_escaped(value);
printf("\n");
}
}
return 0;
}
================================================
FILE: builtin/bg.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/job.h"
#include "shell/shell.h"
#include "shell/task.h"
static const char bg_usage[] = "usage: bg [job_id...]\n";
int builtin_bg(struct mrsh_state *state, int argc, char *argv[]) {
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":")) != -1) {
switch (opt) {
default:
fprintf(stderr, "bg: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, bg_usage);
return EXIT_FAILURE;
}
}
if (_mrsh_optind == argc) {
struct mrsh_job *job = job_by_id(state, "%%", true);
if (!job) {
return EXIT_FAILURE;
}
if (!job_set_foreground(job, false, true)) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
for (int i = _mrsh_optind; i < argc; ++i) {
struct mrsh_job *job = job_by_id(state, argv[i], true);
if (!job) {
return EXIT_FAILURE;
}
if (!job_set_foreground(job, false, true)) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
================================================
FILE: builtin/break.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <mrsh/builtin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
#include "shell/shell.h"
#include "shell/task.h"
static const char break_usage[] = "usage: %s [n]\n";
int builtin_break(struct mrsh_state *state, int argc, char *argv[]) {
if (argc > 2) {
fprintf(stderr, break_usage, argv[0]);
return 1;
}
int n = 1;
if (argc == 2) {
char *end;
n = strtol(argv[1], &end, 10);
if (end[0] != '\0' || argv[0][0] == '\0' || n < 0) {
fprintf(stderr, "%s: invalid loop number '%s'\n", argv[0], argv[1]);
return 1;
}
}
struct mrsh_call_frame_priv *frame_priv = call_frame_get_priv(state->frame);
if (n > frame_priv->nloops) {
n = frame_priv->nloops;
}
frame_priv->nloops -= n - 1;
frame_priv->branch_control =
strcmp(argv[0], "break") == 0 ? MRSH_BRANCH_BREAK : MRSH_BRANCH_CONTINUE;
return TASK_STATUS_INTERRUPTED;
}
================================================
FILE: builtin/builtin.c
================================================
#include <assert.h>
#include <mrsh/builtin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "builtin.h"
#include "shell/shell.h"
struct builtin {
const char *name;
mrsh_builtin_func func;
bool special;
};
static const struct builtin builtins[] = {
// Keep alpha sorted
{ ".", builtin_dot, true },
{ ":", builtin_colon, true },
{ "alias", builtin_alias, false },
{ "bg", builtin_bg, false },
{ "break", builtin_break, true },
{ "cd", builtin_cd, false },
{ "command", builtin_command, false },
{ "continue", builtin_break, true },
{ "eval", builtin_eval, true },
{ "exec", builtin_exec, true },
{ "exit", builtin_exit, true },
{ "export", builtin_export, true },
{ "false", builtin_false, false },
// { "fc", builtin_fc, false },
{ "fg", builtin_fg, false },
{ "getopts", builtin_getopts, false },
{ "hash", builtin_hash, false },
{ "jobs", builtin_jobs, false },
// { "kill", builtin_kill, false },
// { "newgrp", builtin_newgrp, false },
{ "pwd", builtin_pwd, false },
{ "read", builtin_read, false },
{ "readonly", builtin_export, true },
{ "return", builtin_return, true },
{ "set", builtin_set, true },
{ "shift", builtin_shift, true },
{ "times", builtin_times, true },
{ "trap", builtin_trap, true },
{ "true", builtin_true, false },
{ "type", builtin_type, false },
{ "ulimit", builtin_ulimit, false },
{ "umask", builtin_umask, false },
{ "unalias", builtin_unalias, false },
{ "unset", builtin_unset, true },
{ "wait", builtin_wait, false },
};
// The following commands are explicitly unspecified by POSIX
static const char *unspecified_names[] = {
"alloc", "autoload", "bind", "bindkey", "builtin", "bye", "caller", "cap",
"chdir", "clone", "comparguments", "compcall", "compctl", "compdescribe",
"compfiles", "compgen", "compgroups", "complete", "compquote", "comptags",
"comptry", "compvalues", "declare", "dirs", "disable", "disown", "dosh",
"echotc", "echoti", "help", "history", "hist", "let", "local", "login",
"logout", "map", "mapfile", "popd", "print", "pushd", "readarray", "repeat",
"savehistory", "source", "shopt", "stop", "suspend", "typeset", "whence"
};
static const struct builtin unspecified = {
.name = "unspecified",
.func = builtin_unspecified,
.special = false,
};
static int builtin_compare(const void *_a, const void *_b) {
const struct builtin *a = _a, *b = _b;
return strcmp(a->name, b->name);
}
static int unspecified_compare(const void *_a, const void *_b) {
const char *a = _a;
const char *const *b = _b;
return strcmp(a, *b);
}
static const struct builtin *get_builtin(const char *name) {
if (bsearch(name, unspecified_names,
sizeof(unspecified_names) / sizeof(unspecified_names[0]),
sizeof(unspecified_names[0]), unspecified_compare)) {
return &unspecified;
}
struct builtin key = { .name = name };
return bsearch(&key, builtins, sizeof(builtins) / sizeof(builtins[0]),
sizeof(builtins[0]), builtin_compare);
}
bool mrsh_has_builtin(const char *name) {
return get_builtin(name) != NULL;
}
bool mrsh_has_special_builtin(const char *name) {
const struct builtin *builtin = get_builtin(name);
return builtin != NULL && builtin->special;
}
int mrsh_run_builtin(struct mrsh_state *state, int argc, char *argv[]) {
assert(argc > 0);
const char *name = argv[0];
const struct builtin *builtin = get_builtin(name);
if (builtin == NULL) {
return -1;
}
return builtin->func(state, argc, argv);
}
void print_escaped(const char *value) {
const char safe[] = "@%+=:,./-";
size_t i;
for (i = 0; value[i] != '\0'; ++i) {
if (!isalnum(value[i]) && !strchr(safe, value[i])) {
break;
}
}
if (value[i] == '\0' && i > 0) {
printf("%s", value);
} else {
printf("'");
for (size_t i = 0; value[i] != '\0'; ++i) {
if (value[i] == '\'') {
printf("'\"'\"'");
} else {
printf("%c", value[i]);
}
}
printf("'");
}
}
struct collect_iter {
size_t cap, len;
uint32_t attribs;
struct mrsh_collect_var *values;
};
static void collect_vars_iterator(const char *key, void *_var, void *data) {
const struct mrsh_variable *var = _var;
struct collect_iter *iter = data;
if (iter->attribs != MRSH_VAR_ATTRIB_NONE
&& !(var->attribs & iter->attribs)) {
return;
}
if ((iter->len + 1) * sizeof(struct mrsh_collect_var) >= iter->cap) {
iter->cap *= 2;
iter->values = realloc(iter->values,
iter->cap * sizeof(struct mrsh_collect_var));
}
iter->values[iter->len].key = key;
iter->values[iter->len++].value = var->value;
}
static int varcmp(const void *p1, const void *p2) {
const struct mrsh_collect_var *v1 = p1;
const struct mrsh_collect_var *v2 = p2;
return strcmp(v1->key, v2->key);
}
struct mrsh_collect_var *collect_vars(struct mrsh_state *state,
uint32_t attribs, size_t *count) {
struct mrsh_state_priv *priv = state_get_priv(state);
struct collect_iter iter = {
.cap = 64,
.len = 0,
.values = malloc(64 * sizeof(struct mrsh_collect_var)),
.attribs = attribs,
};
mrsh_hashtable_for_each(&priv->variables, collect_vars_iterator, &iter);
qsort(iter.values, iter.len, sizeof(struct mrsh_collect_var), varcmp);
*count = iter.len;
return iter.values;
}
================================================
FILE: builtin/cd.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/path.h"
static const char cd_usage[] = "usage: cd [-L|-P] [-|directory]\n";
static int cd(struct mrsh_state *state, const char *path) {
const char *oldPWD = mrsh_env_get(state, "PWD", NULL);
if (chdir(path) != 0) {
// TODO make better error messages
fprintf(stderr, "cd: %s\n", strerror(errno));
return 1;
}
char *cwd = current_working_dir();
if (cwd == NULL) {
perror("current_working_dir failed");
return 1;
}
mrsh_env_set(state, "OLDPWD", oldPWD, MRSH_VAR_ATTRIB_NONE);
mrsh_env_set(state, "PWD", cwd, MRSH_VAR_ATTRIB_EXPORT);
free(cwd);
return 0;
}
static int isdir(char *path) {
struct stat s;
stat(path, &s);
return S_ISDIR(s.st_mode);
}
int builtin_cd(struct mrsh_state *state, int argc, char *argv[]) {
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":LP")) != -1) {
switch (opt) {
case 'L':
case 'P':
// TODO implement `-L` and `-P`
fprintf(stderr, "cd: `-L` and `-P` not yet implemented\n");
return 1;
default:
fprintf(stderr, "cd: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, cd_usage);
return 1;
}
}
if (_mrsh_optind == argc) {
const char *home = mrsh_env_get(state, "HOME", NULL);
if (home && home[0] != '\0') {
return cd(state, home);
}
fprintf(stderr, "cd: No arguments were given and $HOME "
"is not defined.\n");
return 1;
}
char *curpath = argv[_mrsh_optind];
// `cd -`
if (strcmp(curpath, "-") == 0) {
// This case is special as we print `pwd` at the end
const char *oldpwd = mrsh_env_get(state, "OLDPWD", NULL);
const char *pwd = mrsh_env_get(state, "PWD", NULL);
if (!pwd) {
fprintf(stderr, "cd: PWD is not set\n");
return 1;
}
if (!oldpwd) {
fprintf(stderr, "cd: OLDPWD is not set\n");
return 1;
}
if (chdir(oldpwd) != 0) {
fprintf(stderr, "cd: %s\n", strerror(errno));
return 1;
}
char *_pwd = strdup(pwd);
puts(oldpwd);
mrsh_env_set(state, "PWD", oldpwd, MRSH_VAR_ATTRIB_EXPORT);
mrsh_env_set(state, "OLDPWD", _pwd, MRSH_VAR_ATTRIB_NONE);
free(_pwd);
return 0;
}
// $CDPATH
if (curpath[0] != '/' && strncmp(curpath, "./", 2) != 0 &&
strncmp(curpath, "../", 3) != 0) {
const char *_cdpath = mrsh_env_get(state, "CDPATH", NULL);
char *cdpath = NULL;
if (_cdpath) {
cdpath = strdup(_cdpath);
}
char *c = cdpath;
while (c != NULL) {
char *next = strchr(c, ':');
char *slash = strrchr(c, '/');
if (next != NULL) {
*next = '\0';
++next;
}
if (c[0] == '\0') {
// path is empty
c = ".";
slash = NULL;
}
const char *sep = (slash == NULL || slash[1] != '\0') ? "/" : "";
int len = snprintf(NULL, 0, "%s%s%s", c, sep, curpath);
if (len < 0) {
perror("snprintf failed");
continue;
}
char *path = malloc(len + 1);
if (path == NULL) {
perror("malloc failed");
continue;
}
snprintf(path, len + 1, "%s%s%s", c, sep, curpath);
if (isdir(path)) {
free(cdpath);
int ret = cd(state, path);
free(path);
return ret;
}
free(path);
c = next;
}
free(cdpath);
}
return cd(state, curpath);
}
================================================
FILE: builtin/colon.c
================================================
#include <mrsh/builtin.h>
#include <stdlib.h>
#include "builtin.h"
int builtin_colon(struct mrsh_state *state, int argc, char *argv[]) {
return 0; // This builtin does not do anything
}
================================================
FILE: builtin/command.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "parser.h"
#include "shell/job.h"
#include "shell/path.h"
#include "shell/process.h"
#include "shell/shell.h"
static const char command_usage[] = "usage: command [-v|-V|-p] "
"command_name [argument...]\n";
static int verify_command(struct mrsh_state *state, const char *command_name,
bool default_path) {
struct mrsh_state_priv *priv = state_get_priv(state);
size_t len_command_name = strlen(command_name);
const char *look_alias =
mrsh_hashtable_get(&priv->aliases, command_name);
if (look_alias != NULL) {
printf("alias %s='%s'\n", command_name, look_alias);
return 0;
}
const char *look_fn =
mrsh_hashtable_get(&priv->functions, command_name);
if (look_fn != NULL) {
printf("%s\n", command_name);
return 0;
}
if (mrsh_has_builtin(command_name)) {
printf("%s\n", command_name);
return 0;
}
for (size_t i = 0; i < keywords_len; ++i) {
if (strlen(keywords[i]) == len_command_name &&
strcmp(command_name, keywords[i]) == 0) {
printf("%s\n", command_name);
return 0;
}
}
char *expanded = expand_path(state, command_name, true, default_path);
if (expanded != NULL) {
printf("%s\n", expanded);
free(expanded);
return 0;
}
return 1;
}
static int run_command(struct mrsh_state *state, int argc, char *argv[],
bool default_path) {
if (mrsh_has_builtin(argv[0])) {
return mrsh_run_builtin(state, argc - _mrsh_optind, &argv[_mrsh_optind]);
}
char *path = expand_path(state, argv[0], true, default_path);
if (path == NULL) {
fprintf(stderr, "%s: not found\n", argv[0]);
return 127;
}
// TODO: job control support
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return 126;
} else if (pid == 0) {
execv(path, argv);
// Something went wrong
perror(argv[0]);
exit(126);
}
free(path);
struct mrsh_process *proc = process_create(state, pid);
return job_wait_process(proc);
}
int builtin_command(struct mrsh_state *state, int argc, char *argv[]) {
_mrsh_optind = 0;
int opt;
bool verify = false, default_path = false;
while ((opt = _mrsh_getopt(argc, argv, ":vVp")) != -1) {
switch (opt) {
case 'v':
verify = true;
break;
case 'V':
fprintf(stderr, "command: `-V` has an unspecified output format, "
"use `-v` instead\n");
return 0;
case 'p':
default_path = true;
break;
default:
fprintf(stderr, "command: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, command_usage);
return 1;
}
}
if (_mrsh_optind >= argc) {
fprintf(stderr, command_usage);
return 1;
}
if (verify) {
if (_mrsh_optind != argc - 1) {
fprintf(stderr, command_usage);
return 1;
}
return verify_command(state, argv[_mrsh_optind], default_path);
}
return run_command(state, argc - _mrsh_optind, &argv[_mrsh_optind],
default_path);
}
================================================
FILE: builtin/dot.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <mrsh/builtin.h>
#include <mrsh/parser.h>
#include <mrsh/shell.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "builtin.h"
#include "shell/path.h"
static const char source_usage[] = "usage: . <path>\n";
int builtin_dot(struct mrsh_state *state, int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, source_usage);
return 1;
}
char *path = expand_path(state, argv[1], false, false);
if (!path) {
fprintf(stderr, "%s: not found\n", argv[1]);
if (!state->interactive) {
state->exit = 1;
}
return 1;
}
int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
fprintf(stderr, "unable to open %s for reading: %s\n",
argv[1], strerror(errno));
goto error;
}
free(path);
struct mrsh_parser *parser = mrsh_parser_with_fd(fd);
struct mrsh_program *program = mrsh_parse_program(parser);
int ret;
struct mrsh_position err_pos;
const char *err_msg = mrsh_parser_error(parser, &err_pos);
if (err_msg != NULL) {
fprintf(stderr, "%s %d:%d: %s\n",
argv[1], err_pos.line, err_pos.column, err_msg);
ret = 1;
} else if (program != NULL) {
ret = mrsh_run_program(state, program);
} else {
ret = 0;
}
mrsh_program_destroy(program);
mrsh_parser_destroy(parser);
close(fd);
return ret;
error:
if (!state->interactive) {
state->exit = 1;
}
return 1;
}
================================================
FILE: builtin/eval.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <errno.h>
#include <mrsh/buffer.h>
#include <mrsh/builtin.h>
#include <mrsh/parser.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
static const char eval_usage[] = "usage: eval [cmds...]\n";
int builtin_eval(struct mrsh_state *state, int argc, char *argv[]) {
if (argc == 1) {
fprintf(stderr, eval_usage);
return 1;
}
struct mrsh_buffer buf = {0};
for (int i = 1; i < argc; ++i) {
mrsh_buffer_append(&buf, argv[i], strlen(argv[i]));
if (i != argc - 1) {
mrsh_buffer_append_char(&buf, ' ');
}
}
mrsh_buffer_append_char(&buf, '\n');
struct mrsh_parser *parser = mrsh_parser_with_data(buf.data, buf.len);
struct mrsh_program *program = mrsh_parse_program(parser);
int ret;
struct mrsh_position err_pos;
const char *err_msg = mrsh_parser_error(parser, &err_pos);
if (err_msg != NULL) {
fprintf(stderr, "%s %d:%d: %s\n",
argv[1], err_pos.line, err_pos.column, err_msg);
ret = 1;
} else if (program != NULL) {
ret = mrsh_run_program(state, program);
} else {
ret = 0;
}
mrsh_program_destroy(program);
mrsh_parser_destroy(parser);
mrsh_buffer_finish(&buf);
return ret;
}
================================================
FILE: builtin/exec.c
================================================
#include <stdio.h>
#include <unistd.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/path.h"
static const char exec_usage[] = "usage: exec [command [argument...]]\n";
int builtin_exec(struct mrsh_state *state, int argc, char *argv[]) {
_mrsh_optind = 0;
if (_mrsh_getopt(argc, argv, ":") != -1) {
fprintf(stderr, "exec: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, exec_usage);
return 1;
}
if (_mrsh_optind == argc) {
return 0;
}
char *path = expand_path(state, argv[_mrsh_optind], false, false);
if (path == NULL) {
fprintf(stderr, "exec: %s: command not found\n", argv[_mrsh_optind]);
return 127;
}
if (access(path, X_OK) != 0) {
fprintf(stderr, "exec: %s: not executable\n", path);
return 126;
}
execv(path, &argv[_mrsh_optind]);
perror("exec");
return 1;
}
================================================
FILE: builtin/exit.c
================================================
#include <errno.h>
#include <mrsh/builtin.h>
#include <stdio.h>
#include <stdlib.h>
#include "builtin.h"
#include "shell/task.h"
static const char exit_usage[] = "usage: exit [n]\n";
int builtin_exit(struct mrsh_state *state, int argc, char *argv[]) {
if (argc > 2) {
fprintf(stderr, exit_usage);
return 1;
}
int status = 0;
if (argc > 1) {
char *endptr;
errno = 0;
long status_long = strtol(argv[1], &endptr, 10);
if (endptr[0] != '\0' || errno != 0 ||
status_long < 0 || status_long > 255) {
fprintf(stderr, exit_usage);
return 1;
}
status = (int)status_long;
}
struct mrsh_call_frame_priv *frame_priv = call_frame_get_priv(state->frame);
state->exit = status;
frame_priv->branch_control = MRSH_BRANCH_EXIT;
return TASK_STATUS_INTERRUPTED;
}
================================================
FILE: builtin/export.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <mrsh/builtin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
#include "shell/word.h"
static const char export_usage[] = "usage: %s -p|name[=word]...\n";
int builtin_export(struct mrsh_state *state, int argc, char *argv[]) {
uint32_t attrib = MRSH_VAR_ATTRIB_EXPORT;
if (strcmp(argv[0], "readonly") == 0) {
attrib = MRSH_VAR_ATTRIB_READONLY;
}
if (argc < 2) {
fprintf(stderr, export_usage, argv[0]);
return 1;
} else if (argc == 2 && strcmp(argv[1], "-p") == 0) {
size_t count;
struct mrsh_collect_var *vars = collect_vars(
state, attrib, &count);
for (size_t i = 0; i < count; ++i) {
printf("%s %s=", argv[0], vars[i].key);
print_escaped(vars[i].value);
printf("\n");
}
free(vars);
return 0;
}
for (int i = 1; i < argc; ++i) {
char *eql, *key;
const char *val;
uint32_t prev_attribs = 0;
eql = strchr(argv[i], '=');
if (eql) {
size_t klen = eql - argv[i];
key = strndup(argv[i], klen);
val = &eql[1];
mrsh_env_get(state, key, &prev_attribs);
} else {
key = strdup(argv[i]);
val = mrsh_env_get(state, key, &prev_attribs);
if (!val) {
val = "";
}
}
if ((prev_attribs & MRSH_VAR_ATTRIB_READONLY)) {
fprintf(stderr, "%s: cannot modify readonly variable %s\n",
argv[0], key);
free(key);
return 1;
}
struct mrsh_word_string *ws =
mrsh_word_string_create(strdup(val), false);
struct mrsh_word *word = &ws->word;
expand_tilde(state, &word, true);
char *new_val = mrsh_word_str(word);
mrsh_word_destroy(word);
mrsh_env_set(state, key, new_val, attrib | prev_attribs);
free(key);
free(new_val);
}
return 0;
}
================================================
FILE: builtin/false.c
================================================
#include <mrsh/shell.h>
#include <stdlib.h>
#include "builtin.h"
int builtin_false(struct mrsh_state *state, int argc, char *argv[]) {
return 1;
}
================================================
FILE: builtin/fg.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/job.h"
#include "shell/shell.h"
static const char fg_usage[] = "usage: fg [job_id]\n";
int builtin_fg(struct mrsh_state *state, int argc, char *argv[]) {
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":")) != -1) {
switch (opt) {
default:
fprintf(stderr, "fg: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, fg_usage);
return EXIT_FAILURE;
}
}
struct mrsh_job *job;
if (_mrsh_optind == argc) {
job = job_by_id(state, "%%", true);
} else if (_mrsh_optind == argc - 1) {
job = job_by_id(state, argv[_mrsh_optind], true);
} else {
fprintf(stderr, fg_usage);
return EXIT_FAILURE;
}
if (!job) {
return EXIT_FAILURE;
}
if (!job_set_foreground(job, true, true)) {
return EXIT_FAILURE;
}
return job_wait(job);
}
================================================
FILE: builtin/getopts.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <limits.h>
#include <mrsh/buffer.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
#include "mrsh_getopt.h"
static const char getopts_usage[] = "usage: getopts optstring name [arg...]\n";
int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]) {
_mrsh_optind = 0;
if (_mrsh_getopt(argc, argv, ":") != -1) {
fprintf(stderr, "getopts: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, getopts_usage);
return 1;
}
if (_mrsh_optind + 2 < argc) {
fprintf(stderr, getopts_usage);
return 1;
}
int optc;
char **optv;
if (_mrsh_optind + 2 > argc) {
optc = argc - _mrsh_optind - 2;
optv = &argv[_mrsh_optind + 2];
} else {
optc = state->frame->argc;
optv = state->frame->argv;
}
char *optstring = argv[_mrsh_optind];
char *name = argv[_mrsh_optind + 1];
const char *optind_str = mrsh_env_get(state, "OPTIND", NULL);
if (optind_str == NULL) {
fprintf(stderr, "getopts: OPTIND is not defined\n");
return 1;
}
char *endptr;
long optind_long = strtol(optind_str, &endptr, 10);
if (endptr[0] != '\0' || optind_long <= 0 || optind_long > INT_MAX) {
fprintf(stderr, "getopts: OPTIND is not a positive integer\n");
return 1;
}
_mrsh_optind = (int)optind_long;
_mrsh_optopt = 0;
int opt = _mrsh_getopt(optc, optv, optstring);
char optind_fmt[16];
snprintf(optind_fmt, sizeof(optind_fmt), "%d", _mrsh_optind);
mrsh_env_set(state, "OPTIND", optind_fmt, MRSH_VAR_ATTRIB_NONE);
if (_mrsh_optopt != 0) {
if (opt == ':') {
char value[] = {(char)_mrsh_optopt, '\0'};
mrsh_env_set(state, "OPTARG", value, MRSH_VAR_ATTRIB_NONE);
} else if (optstring[0] != ':') {
mrsh_env_unset(state, "OPTARG");
} else {
// either missing option-argument or unknown option character
// in the former case, unset OPTARG
// in the latter case, set OPTARG to _mrsh_optopt
bool opt_exists = false;
size_t len = strlen(optstring);
for (size_t i = 0; i < len; ++i) {
if (optstring[i] == _mrsh_optopt) {
opt_exists = true;
break;
}
}
if (opt_exists) {
mrsh_env_unset(state, "OPTARG");
} else {
char value[] = {(char)_mrsh_optopt, '\0'};
mrsh_env_set(state, "OPTARG", value, MRSH_VAR_ATTRIB_NONE);
}
}
} else if (_mrsh_optarg != NULL) {
mrsh_env_set(state, "OPTARG", _mrsh_optarg, MRSH_VAR_ATTRIB_NONE);
} else {
mrsh_env_unset(state, "OPTARG");
}
char value[] = {opt == -1 ? (char)'?' : (char)opt, '\0'};
mrsh_env_set(state, name, value, MRSH_VAR_ATTRIB_NONE);
if (opt == -1) {
return 1;
}
return 0;
}
================================================
FILE: builtin/hash.c
================================================
#include <mrsh/builtin.h>
#include <shell/path.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
#include "mrsh_getopt.h"
static const char hash_usage[] = "usage: hash -r|utility...\n";
int builtin_hash(struct mrsh_state *state, int argc, char *argv[]) {
/* Hashing and remembering executable location isn't implemented. Thus most
* of this builtin just does nothing. */
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":r")) != -1) {
switch (opt) {
case 'r':
/* no-op: reset list of cached utilities */
return 0;
default:
fprintf(stderr, "hash: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, hash_usage);
return 1;
}
}
if (argc == 1) {
/* no-op: print list of cached utilities */
return 0;
}
for (int i = 1; i < argc; i++) {
const char *utility = argv[i];
if (strchr(utility, '/') != NULL) {
fprintf(stderr,
"hash: undefined behaviour: utility contains a slash\n");
return 1;
}
if (mrsh_has_builtin(utility)) {
continue;
}
char *path = expand_path(state, utility, true, false);
if (path == NULL) {
fprintf(stderr, "hash: command not found: %s\n", utility);
return 1;
}
free(path);
}
return 0;
}
================================================
FILE: builtin/jobs.c
================================================
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/job.h"
#include "shell/process.h"
#include "shell/shell.h"
#include "shell/task.h"
static const char jobs_usage[] = "usage: jobs [-l|-p] [job_id...]\n";
struct jobs_context {
struct mrsh_job *current, *previous;
bool pids;
bool pgids;
bool r;
};
static void show_job(struct mrsh_job *job, const struct jobs_context *ctx) {
if (job_poll(job) >= 0) {
return;
}
char curprev = ' ';
if (job == ctx->current) {
curprev = '+';
} else if (job == ctx->previous) {
curprev = '-';
}
if (ctx->pids) {
for (size_t i = 0; i < job->processes.len; ++i) {
struct mrsh_process *proc = job->processes.data[i];
printf("%d\n", proc->pid);
}
} else if (ctx->pgids) {
char *cmd = mrsh_node_format(job->node);
printf("[%d] %c %d %s %s\n", job->job_id, curprev, job->pgid,
job_state_str(job, ctx->r), cmd);
free(cmd);
} else {
char *cmd = mrsh_node_format(job->node);
printf("[%d] %c %s %s\n", job->job_id, curprev,
job_state_str(job, ctx->r), cmd);
free(cmd);
}
}
int builtin_jobs(struct mrsh_state *state, int argc, char *argv[]) {
struct mrsh_state_priv *priv = state_get_priv(state);
struct mrsh_job *current = job_by_id(state, "%+", false),
*previous = job_by_id(state, "%-", false);
struct jobs_context ctx = {
.current = current,
.previous = previous,
.pids = false,
.pgids = false,
.r = rand() % 2 == 0,
};
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":lp")) != -1) {
switch (opt) {
case 'l':
if (ctx.pids) {
fprintf(stderr, "jobs: the -p and -l options are "
"mutually exclusive\n");
return EXIT_FAILURE;
}
ctx.pgids = true;
break;
case 'p':
if (ctx.pgids) {
fprintf(stderr, "jobs: the -p and -l options are "
"mutually exclusive\n");
return EXIT_FAILURE;
}
ctx.pids = true;
break;
default:
fprintf(stderr, "jobs: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, jobs_usage);
return EXIT_FAILURE;
}
}
if (_mrsh_optind == argc) {
for (size_t i = 0; i < priv->jobs.len; i++) {
struct mrsh_job *job = priv->jobs.data[i];
show_job(job, &ctx);
}
} else {
for (int i = _mrsh_optind; i < argc; i++) {
struct mrsh_job *job = job_by_id(state, argv[i], true);
if (!job) {
return 1;
}
show_job(job, &ctx);
}
}
return EXIT_SUCCESS;
}
================================================
FILE: builtin/pwd.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>
#include "builtin.h"
#include "mrsh_getopt.h"
static const char pwd_usage[] = "usage: pwd [-L|-P]\n";
int builtin_pwd(struct mrsh_state *state, int argc, char *argv[]) {
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":LP")) != -1) {
switch (opt) {
case 'L':
case 'P':
/* This space deliberately left blank */
break;
default:
fprintf(stderr, "pwd: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, pwd_usage);
return 1;
}
}
if (_mrsh_optind < argc) {
fprintf(stderr, pwd_usage);
return 1;
}
const char *pwd = mrsh_env_get(state, "PWD", NULL);
assert(pwd != NULL);
puts(pwd);
return 0;
}
================================================
FILE: builtin/read.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <mrsh/buffer.h>
#include <mrsh/shell.h>
#include <shell/word.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
#include "mrsh_getopt.h"
static const char read_usage[] = "usage: read [-r] var...\n";
int builtin_read(struct mrsh_state *state, int argc, char *argv[]) {
bool raw = false;
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":r")) != -1) {
switch (opt) {
case 'r':
raw = true;
break;
default:
fprintf(stderr, "read: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, read_usage);
return 1;
}
}
if (_mrsh_optind == argc) {
fprintf(stderr, read_usage);
return 1;
}
struct mrsh_buffer buf = {0};
bool escaped = false;
int c;
while ((c = fgetc(stdin)) != EOF) {
if (!raw && !escaped && c == '\\') {
escaped = true;
continue;
}
if (c == '\n') {
if (escaped) {
escaped = false;
const char *ps2 = mrsh_env_get(state, "PS2", NULL);
fprintf(stderr, "%s", ps2 != NULL ? ps2 : "> ");
continue;
}
break;
}
escaped = false;
mrsh_buffer_append_char(&buf, (char)c);
}
mrsh_buffer_append_char(&buf, '\0');
struct mrsh_array fields = {0};
struct mrsh_word_string *ws = mrsh_word_string_create(mrsh_buffer_steal(&buf), false);
split_fields(&fields, &ws->word, mrsh_env_get(state, "IFS", NULL));
mrsh_word_destroy(&ws->word);
struct mrsh_array strs = {0};
get_fields_str(&strs, &fields);
for (size_t i = 0; i < fields.len; ++i) {
mrsh_word_destroy(fields.data[i]);
}
mrsh_array_finish(&fields);
fields = strs;
if (fields.len <= (size_t)(argc - _mrsh_optind)) {
for (size_t i = 0; i < fields.len; ++i) {
mrsh_env_set(state, argv[_mrsh_optind + i], (char *)fields.data[i], MRSH_VAR_ATTRIB_NONE);
}
for (size_t i = fields.len; i < (size_t)(argc - _mrsh_optind); ++i) {
mrsh_env_set(state, argv[_mrsh_optind + i], "", MRSH_VAR_ATTRIB_NONE);
}
} else {
for (int i = 0; i < argc - _mrsh_optind - 1; ++i) {
mrsh_env_set(state, argv[_mrsh_optind + i], (char *)fields.data[i], MRSH_VAR_ATTRIB_NONE);
}
struct mrsh_buffer buf_last = {0};
for (size_t i = (size_t)(argc - _mrsh_optind - 1); i < fields.len; ++i) {
char *field = (char *)fields.data[i];
mrsh_buffer_append(&buf_last, field, strlen(field));
if (i != fields.len - 1) {
// TODO add the field delimiter rather than space (bash and dash always use spaces)
mrsh_buffer_append_char(&buf_last, ' ');
}
}
mrsh_buffer_append_char(&buf_last, '\0');
mrsh_env_set(state, argv[argc - 1], buf_last.data, MRSH_VAR_ATTRIB_NONE);
mrsh_buffer_finish(&buf_last);
}
for (size_t i = 0; i < fields.len; ++i) {
free(fields.data[i]);
}
mrsh_array_finish(&fields);
if (c == EOF) {
return 1;
} else {
return 0;
}
}
================================================
FILE: builtin/return.c
================================================
#include <mrsh/builtin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
#include "shell/task.h"
static const char return_usage[] = "usage: %s [n]\n";
int builtin_return(struct mrsh_state *state, int argc, char *argv[]) {
if (argc > 2) {
fprintf(stderr, return_usage, argv[0]);
return 1;
}
int n = 0;
if (argc == 2) {
char *end;
n = strtol(argv[1], &end, 10);
if (end[0] != '\0' || argv[0][0] == '\0' || n < 0 || n > 255) {
fprintf(stderr, "%s: invalid return number '%s'\n", argv[0], argv[1]);
return 1;
}
}
struct mrsh_call_frame_priv *frame_priv = call_frame_get_priv(state->frame);
frame_priv->nloops = 0;
frame_priv->branch_control = MRSH_BRANCH_RETURN;
state->last_status = n;
return TASK_STATUS_INTERRUPTED;
}
================================================
FILE: builtin/set.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <mrsh/builtin.h>
#include <mrsh/shell.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "builtin.h"
#include "shell/shell.h"
static const char set_usage[] =
"usage: set [(-|+)abCefhmnuvx] [-o option] [args...]\n"
" set [(-|+)abCefhmnuvx] [+o option] [args...]\n"
" set -- [args...]\n"
" set -o\n"
" set +o\n";
struct option_map {
const char *name;
char short_name;
enum mrsh_option value;
};
static const struct option_map options[] = {
{ "allexport", 'a', MRSH_OPT_ALLEXPORT },
{ "notify", 'b', MRSH_OPT_NOTIFY },
{ "noclobber", 'C', MRSH_OPT_NOCLOBBER },
{ "errexit", 'e', MRSH_OPT_ERREXIT },
{ "noglob", 'f', MRSH_OPT_NOGLOB },
{ NULL, 'h', MRSH_OPT_PRELOOKUP },
{ "monitor", 'm', MRSH_OPT_MONITOR },
{ "noexec", 'n', MRSH_OPT_NOEXEC },
{ "ignoreeof", 0, MRSH_OPT_IGNOREEOF },
{ "nolog", 0, MRSH_OPT_NOLOG },
{ "vi", 0, MRSH_OPT_VI },
{ "nounset", 'u', MRSH_OPT_NOUNSET },
{ "verbose", 'v', MRSH_OPT_VERBOSE },
{ "xtrace", 'x', MRSH_OPT_XTRACE },
};
const char *state_get_options(struct mrsh_state *state) {
static char opts[sizeof(options) / sizeof(options[0]) + 1];
int i = 0;
for (size_t j = 0; j < sizeof(options) / sizeof(options[0]); ++j) {
if (options[j].short_name != '\0' &&
(state->options & options[j].value)) {
opts[i++] = options[j].short_name;
}
}
opts[i] = '\0';
return opts;
}
static void print_options(struct mrsh_state *state) {
for (size_t j = 0; j < sizeof(options) / sizeof(options[0]); ++j) {
if (options[j].name != NULL) {
printf("set %co %s\n",
(state->options & options[j].value) ? '-' : '+',
options[j].name);
}
}
}
static const struct option_map *find_option(char opt) {
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); ++i) {
if (options[i].short_name && options[i].short_name == opt) {
return &options[i];
}
}
return NULL;
}
static const struct option_map *find_long_option(const char *opt) {
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); ++i) {
if (options[i].name && strcmp(options[i].name, opt) == 0) {
return &options[i];
}
}
return NULL;
}
static char **argv_dup(char *argv_0, int argc, char *argv[]) {
char **_argv = calloc(argc + 1, sizeof(char *));
_argv[0] = argv_0;
for (int i = 1; i < argc; ++i) {
_argv[i] = strdup(argv[i - 1]);
}
return _argv;
}
static void argv_free(int argc, char **argv) {
if (!argv) {
return;
}
for (int i = 0; i < argc; ++i) {
free(argv[i]);
}
free(argv);
}
static int set(struct mrsh_state *state, int argc, char *argv[],
struct mrsh_init_args *init_args, uint32_t *populated_opts) {
if (argc == 1 && init_args == NULL) {
size_t count;
struct mrsh_collect_var *vars = collect_vars(
state, MRSH_VAR_ATTRIB_NONE, &count);
for (size_t i = 0; i < count; ++i) {
printf("%s=", vars[i].key);
print_escaped(vars[i].value);
printf("\n");
}
free(vars);
return 0;
}
bool force_positional = false;
int i;
for (i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--") == 0) {
force_positional = true;
++i;
break;
}
if (argv[i][0] != '-' && argv[i][0] != '+') {
break;
}
if (argv[i][1] == '\0') {
fprintf(stderr, set_usage);
return 1;
}
const struct option_map *option;
switch (argv[i][1]) {
case 'o':
if (i + 1 == argc) {
print_options(state);
goto out; // we must populate state->argv
}
option = find_long_option(argv[i + 1]);
if (!option) {
fprintf(stderr, set_usage);
return 1;
}
if (argv[i][0] == '-') {
state->options |= option->value;
} else {
state->options &= ~option->value;
}
if (populated_opts != NULL) {
*populated_opts |= option->value;
}
++i;
continue;
case 'c':
if (init_args == NULL) {
fprintf(stderr, set_usage);
return 1;
}
init_args->command_str = argv[i + 1];
++i;
break;
case 's':
if (init_args == NULL) {
fprintf(stderr, set_usage);
return 1;
}
init_args->command_str = NULL;
init_args->command_file = NULL;
break;
default:
for (int j = 1; argv[i][j]; ++j) {
option = find_option(argv[i][j]);
if (!option) {
fprintf(stderr, set_usage);
return 1;
}
if (argv[i][0] == '-') {
state->options |= option->value;
} else {
state->options &= ~option->value;
}
if (populated_opts != NULL) {
*populated_opts |= option->value;
}
}
break;
}
}
if (i != argc || force_positional) {
char *argv_0;
if (init_args != NULL) {
argv_0 = strdup(argv[i++]);
init_args->command_file = argv_0;
} else if (state->frame->argv) {
argv_0 = strdup(state->frame->argv[0]);
} else {
fprintf(stderr, set_usage);
return 1;
}
argv_free(state->frame->argc, state->frame->argv);
state->frame->argc = argc - i + 1;
state->frame->argv = argv_dup(argv_0, state->frame->argc, &argv[i]);
} else
out:
if (init_args != NULL) {
// No args given, but we need to initialize state->argv
state->frame->argc = 1;
state->frame->argv = argv_dup(strdup(argv[0]), 1, argv);
}
return 0;
}
int builtin_set(struct mrsh_state *state, int argc, char *argv[]) {
uint32_t populated_opts = 0;
int ret = set(state, argc, argv, NULL, &populated_opts);
if (ret != 0) {
return ret;
}
if (populated_opts & MRSH_OPT_MONITOR) {
if (!mrsh_set_job_control(state, state->options & MRSH_OPT_MONITOR)) {
return 1;
}
}
return 0;
}
int mrsh_process_args(struct mrsh_state *state, struct mrsh_init_args *init_args,
int argc, char *argv[]) {
struct mrsh_state_priv *priv = state_get_priv(state);
uint32_t populated_opts = 0;
int ret = set(state, argc, argv, init_args, &populated_opts);
if (ret != 0) {
return ret;
}
state->interactive = isatty(priv->term_fd) &&
init_args->command_str == NULL && init_args->command_file == NULL;
if (state->interactive && !(populated_opts & MRSH_OPT_MONITOR)) {
state->options |= MRSH_OPT_MONITOR;
}
return 0;
}
================================================
FILE: builtin/shift.c
================================================
#include <errno.h>
#include <mrsh/builtin.h>
#include <mrsh/shell.h>
#include <stdlib.h>
#include "builtin.h"
static const char shift_usage[] = "usage: shift [n]\n";
int builtin_shift(struct mrsh_state *state, int argc, char *argv[]) {
if (argc > 2) {
fprintf(stderr, shift_usage);
return 1;
}
int n = 1;
if (argc == 2) {
char *endptr;
errno = 0;
long n_long = strtol(argv[1], &endptr, 10);
if (*endptr != '\0' || errno != 0) {
fprintf(stderr, shift_usage);
if (!state->interactive) {
state->exit = 1;
}
return 1;
}
n = (int)n_long;
}
if (n == 0) {
return 0;
} else if (n < 1) {
fprintf(stderr, "shift: [n] must be positive\n");
if (!state->interactive) {
state->exit = 1;
}
return 1;
} else if (n > state->frame->argc - 1) {
fprintf(stderr, "shift: [n] must be less than $#\n");
if (!state->interactive) {
state->exit = 1;
}
return 1;
}
for (int i = 1, j = n + 1; j < state->frame->argc; ++i, ++j) {
if (j <= state->frame->argc - n) {
state->frame->argv[i] = state->frame->argv[j];
} else {
free(state->frame->argv[i]);
}
}
state->frame->argc -= n;
return 0;
}
================================================
FILE: builtin/times.c
================================================
#include <errno.h>
#include <mrsh/builtin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/times.h>
#include <unistd.h>
#include "builtin.h"
static const char times_usage[] = "usage: times\n";
int builtin_times(struct mrsh_state *state, int argc, char *argv[]) {
if (argc > 1) {
fprintf(stderr, times_usage);
return 1;
}
struct tms buf;
long clk_tck = sysconf(_SC_CLK_TCK);
if (clk_tck == -1) {
perror("sysconf");
return 1;
}
if (times(&buf) == (clock_t)-1) {
perror("times");
return 1;
}
printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
(int)(buf.tms_utime / clk_tck / 60),
((double) buf.tms_utime) / clk_tck,
(int)(buf.tms_stime / clk_tck / 60),
((double) buf.tms_stime) / clk_tck,
(int)(buf.tms_cutime / clk_tck / 60),
((double) buf.tms_cutime) / clk_tck,
(int)(buf.tms_cstime / clk_tck / 60),
((double)buf.tms_cstime) / clk_tck);
return 0;
}
================================================
FILE: builtin/trap.c
================================================
#define _XOPEN_SOURCE 1 // for SIGPOLL and SIGVTALRM
#include <assert.h>
#include <mrsh/parser.h>
#include <mrsh/shell.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/shell.h"
#include "shell/trap.h"
static const char trap_usage[] =
"usage: trap <n> [condition...]\n"
" trap [action condition...]\n";
static const char *sig_names[] = {
[SIGABRT] = "ABRT",
[SIGALRM] = "ALRM",
[SIGBUS] = "BUS",
[SIGCHLD] = "CHLD",
[SIGCONT] = "CONT",
[SIGFPE] = "FPE",
[SIGHUP] = "HUP",
[SIGILL] = "ILL",
[SIGINT] = "INT",
[SIGKILL] = "KILL",
[SIGPIPE] = "PIPE",
[SIGQUIT] = "QUIT",
[SIGSEGV] = "SEGV",
[SIGSTOP] = "STOP",
[SIGTERM] = "TERM",
[SIGTSTP] = "TSTP",
[SIGTTIN] = "TTIN",
[SIGTTOU] = "TTOU",
[SIGUSR1] = "USR1",
[SIGUSR2] = "USR2",
// Some BSDs have decided against implementing SIGPOLL for functional and
// security reasons
#ifdef SIGPOLL
[SIGPOLL] = "POLL",
#endif
[SIGPROF] = "PROF",
[SIGSYS] = "SYS",
[SIGTRAP] = "TRAP",
[SIGURG] = "URG",
[SIGVTALRM] = "VTALRM",
[SIGXCPU] = "XCPU",
[SIGXFSZ] = "XFSZ",
};
static bool is_decimal_str(const char *str) {
for (size_t i = 0; str[i] != '\0'; i++) {
if (str[i] < '0' || str[i] > '9') {
return false;
}
}
return true;
}
static int parse_sig(const char *str) {
if (strcmp(str, "0") == 0 || strcmp(str, "EXIT") == 0) {
return 0;
}
// XSI-conformant systems need to recognize a few more numeric signal
// numbers
if (strcmp(str, "1") == 0) {
return SIGHUP;
} else if (strcmp(str, "2") == 0) {
return SIGINT;
} else if (strcmp(str, "3") == 0) {
return SIGQUIT;
} else if (strcmp(str, "6") == 0) {
return SIGABRT;
} else if (strcmp(str, "9") == 0) {
return SIGKILL;
} else if (strcmp(str, "14") == 0) {
return SIGALRM;
} else if (strcmp(str, "15") == 0) {
return SIGTERM;
}
for (size_t i = 0; i < sizeof(sig_names) / sizeof(sig_names[0]); i++) {
if (sig_names[i] == NULL) {
continue;
}
if (strcmp(str, sig_names[i]) == 0) {
return (int)i;
}
}
fprintf(stderr, "trap: failed to parse condition: %s\n", str);
return -1;
}
static const char *sig_str(int sig) {
if (sig == 0) {
return "EXIT";
}
assert(sig > 0);
assert((size_t)sig < sizeof(sig_names) / sizeof(sig_names[0]));
assert(sig_names[sig] != NULL);
return sig_names[sig];
}
static void print_traps(struct mrsh_state *state) {
struct mrsh_state_priv *priv = state_get_priv(state);
for (size_t i = 0; i < sizeof(priv->traps) / sizeof(priv->traps[0]); i++) {
struct mrsh_trap *trap = &priv->traps[i];
if (!trap->set) {
continue;
}
printf("trap -- ");
switch (trap->action) {
case MRSH_TRAP_DEFAULT:
printf("-");
break;
case MRSH_TRAP_IGNORE:
printf("''");
break;
case MRSH_TRAP_CATCH:;
char *cmd = mrsh_node_format(&trap->program->node);
print_escaped(cmd);
free(cmd);
break;
}
printf(" %s\n", sig_str(i));
}
}
int builtin_trap(struct mrsh_state *state, int argc, char *argv[]) {
_mrsh_optind = 0;
if (_mrsh_getopt(argc, argv, ":") != -1) {
fprintf(stderr, "trap: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, trap_usage);
return 1;
}
if (_mrsh_optind == argc) {
print_traps(state);
return 0;
}
const char *action_str;
if (is_decimal_str(argv[_mrsh_optind])) {
action_str = "-";
} else {
action_str = argv[_mrsh_optind];
_mrsh_optind++;
}
enum mrsh_trap_action action;
struct mrsh_program *program = NULL;
if (action_str[0] == '\0') {
action = MRSH_TRAP_IGNORE;
} else if (strcmp(action_str, "-") == 0) {
action = MRSH_TRAP_DEFAULT;
} else {
action = MRSH_TRAP_CATCH;
struct mrsh_parser *parser =
mrsh_parser_with_data(action_str, strlen(action_str));
program = mrsh_parse_program(parser);
struct mrsh_position err_pos;
const char *err_msg = mrsh_parser_error(parser, &err_pos);
if (err_msg != NULL) {
fprintf(stderr, "trap: %d:%d: %s\n",
err_pos.line, err_pos.column, err_msg);
mrsh_parser_destroy(parser);
mrsh_program_destroy(program);
return 1;
}
mrsh_parser_destroy(parser);
}
for (int i = _mrsh_optind; i < argc; i++) {
int sig = parse_sig(argv[i]);
if (sig < 0) {
return 1;
}
if (sig == SIGKILL || sig == SIGSTOP) {
fprintf(stderr, "trap: setting a trap for SIGKILL or SIGSTOP "
"produces undefined results\n");
return 1;
}
if (!set_trap(state, sig, action, program)) {
return 1;
}
}
return 0;
}
================================================
FILE: builtin/true.c
================================================
#include <mrsh/shell.h>
#include <stdlib.h>
#include "builtin.h"
int builtin_true(struct mrsh_state *state, int argc, char *argv[]) {
return 0;
}
================================================
FILE: builtin/type.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <mrsh/builtin.h>
#include <shell/path.h>
#include <stdlib.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/shell.h"
static const char type_usage[] = "usage: type name...\n";
int builtin_type(struct mrsh_state *state, int argc, char *argv[]) {
struct mrsh_state_priv *priv = state_get_priv(state);
_mrsh_optind = 0;
if (_mrsh_getopt(argc, argv, ":") != -1) {
fprintf(stderr, "type: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, type_usage);
return 1;
}
if (_mrsh_optind == argc) {
fprintf(stderr, type_usage);
return 1;
}
bool error = false;
for (int i = _mrsh_optind; i < argc; ++i) {
char *name = argv[i];
char *alias = mrsh_hashtable_get(&priv->aliases, name);
if (alias != NULL) {
fprintf(stdout, "%s is an alias for %s\n", name, alias);
continue;
}
if (mrsh_has_special_builtin(name)) {
fprintf(stdout, "%s is a special shell builtin\n", name);
continue;
}
if (mrsh_has_builtin(name)) {
fprintf(stdout, "%s is a shell builtin\n", name);
continue;
}
char *path = expand_path(state, name, true, false);
if (path != NULL) {
fprintf(stdout, "%s is %s\n", name, path);
free(path);
continue;
}
fprintf(stdout, "%s: not found\n", name);
error = true;
}
return error ? 1 : 0;
}
================================================
FILE: builtin/ulimit.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include "builtin.h"
#include "mrsh_getopt.h"
static const char ulimit_usage[] = "usage: ulimit [-f] [blocks]\n";
int builtin_ulimit(struct mrsh_state *state, int argc, char *argv[]) {
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":f")) != -1) {
if (opt == 'f') {
// Nothing here
} else {
fprintf(stderr, "%s", ulimit_usage);
return 1;
}
}
if (_mrsh_optind == argc - 1) {
char *arg = argv[_mrsh_optind];
char *end;
long int new_limit = strtol(arg, &end, 10);
if (end == arg || end[0] != '\0') {
fprintf(stderr, "ulimit: invalid argument: %s\n", arg);
return 1;
}
struct rlimit new = {
.rlim_cur = new_limit * 512,
.rlim_max = new_limit * 512
};
if (setrlimit(RLIMIT_FSIZE, &new) != 0) {
perror("setrlimit");
return 1;
}
} else if (_mrsh_optind == argc) {
struct rlimit old = { 0 };
if (getrlimit(RLIMIT_FSIZE, &old) != 0) {
perror("getrlimit");
return 1;
}
if (old.rlim_max == RLIM_INFINITY) {
printf("unlimited\n");
} else {
printf("%" PRIuMAX "\n", (uintmax_t)(old.rlim_max / 512));
}
} else {
fprintf(stderr, "%s", ulimit_usage);
return 1;
}
return 0;
}
================================================
FILE: builtin/umask.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <mrsh/builtin.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "builtin.h"
#include "mrsh_getopt.h"
static const char umask_usage[] = "usage: umask [-S] [mode]\n";
enum umask_symbolic_state {
UMASK_WHO,
UMASK_PERM,
};
static mode_t umask_current_mask(void) {
const mode_t default_mode = 0022;
mode_t mask = umask(default_mode);
umask(mask);
return mask;
}
static bool umask_update_mode(mode_t *mode, char action, mode_t *perm_mask, mode_t *who_mask) {
switch (action) {
case '+':
*mode |= (*who_mask & *perm_mask);
break;
case '=':
*mode = (*mode & ~(*who_mask)) | (*perm_mask & *who_mask);
break;
case '-':
*mode &= (0777 & ~(*who_mask & *perm_mask));
break;
default:
fprintf(stderr, "unknown action -- '%c'\n", action);
return false;
}
*perm_mask = *who_mask = 0;
return true;
}
static bool umask_mode(mode_t *mode, char *symbolic) {
mode_t tmp_mode = 0777 & ~(umask_current_mask());
enum umask_symbolic_state state = UMASK_WHO;
mode_t who_mask = 0;
mode_t perm_mask = 0;
char action = '\0';
for (char *c = symbolic; *c != '\0'; c++) {
switch (state) {
case UMASK_WHO:
switch (*c) {
case 'u':
who_mask |= S_IRWXU;
break;
case 'g':
who_mask |= S_IRWXG;
break;
case 'o':
who_mask |= S_IRWXO;
break;
case 'a':
who_mask |= (S_IRWXU | S_IRWXG | S_IRWXO);
break;
case '+':
case '-':
case '=':
if (who_mask == 0) {
who_mask |= (S_IRWXU | S_IRWXG | S_IRWXO);
}
action = *c;
state = UMASK_PERM;
break;
default:
fprintf(stderr, "Unknown who -- '%c'\n", *c);
return false;
}
break;
case UMASK_PERM:
switch (*c) {
case 'u':
perm_mask = (tmp_mode & S_IRWXU) | ((tmp_mode & S_IRWXU) >> 3) | ((tmp_mode & S_IRWXU) >> 6);
break;
case 'g':
perm_mask = ((tmp_mode & S_IRWXG) << 3) | (tmp_mode & S_IRWXG) | ((tmp_mode & S_IRWXG) >> 3);
break;
case 'o':
perm_mask = ((tmp_mode & S_IRWXO) << 6) | ((tmp_mode & S_IRWXO) << 3) | (tmp_mode & S_IRWXO);
break;
case 'r':
perm_mask |= (S_IRUSR | S_IRGRP | S_IROTH);
break;
case 'w':
perm_mask |= (S_IWUSR | S_IWGRP | S_IWOTH);
break;
case 'x':
perm_mask |= (S_IXUSR | S_IXGRP | S_IXOTH);
break;
case ',':
state = UMASK_WHO;
if (!umask_update_mode(&tmp_mode, action, &perm_mask, &who_mask)) {
return false;
}
break;
default:
fprintf(stderr, "Invalid permission -- '%c'\n", *c);
return false;
}
break;
}
}
if (state == UMASK_PERM) {
if (!umask_update_mode(&tmp_mode, action, &perm_mask, &who_mask)) {
return false;
}
} else {
fprintf(stderr, "Missing permission from symbolic mode\n");
return false;
}
*mode = 0777 & ~tmp_mode;
return true;
}
static void umask_modestring(char string[static 4], mode_t mode) {
size_t i = 0;
if (S_IROTH & mode) {
string[i++] = 'r';
}
if (S_IWOTH & mode) {
string[i++] = 'w';
}
if (S_IXOTH & mode) {
string[i++] = 'x';
}
}
static void umask_print_symbolic(mode_t mask) {
char user[4] = {0};
char group[4] = {0};
char other[4] = {0};
mode_t mode = 0777 & ~mask;
umask_modestring(user, (mode & 0700) >> 6);
umask_modestring(group, (mode & 0070) >> 3);
umask_modestring(other, (mode & 0007));
printf("u=%s,g=%s,o=%s\n", user, group, other);
}
int builtin_umask(struct mrsh_state *state, int argc, char *argv[]) {
mode_t mode;
bool umask_symbolic = false;
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":S")) != -1) {
switch (opt) {
case 'S':
umask_symbolic = true;
break;
default:
fprintf(stderr, "Unknown option -- '%c'\n", _mrsh_optopt);
fprintf(stderr, umask_usage);
return 1;
}
}
if (_mrsh_optind == argc) {
mode = umask_current_mask();
if (umask_symbolic) {
umask_print_symbolic(mode);
} else {
printf("%04o\n", mode);
}
return 0;
}
char *endptr;
mode = strtol(argv[_mrsh_optind], &endptr, 8);
if (*endptr != '\0') {
if (!umask_mode(&mode, argv[_mrsh_optind])) {
fprintf(stderr, umask_usage);
return 1;
}
}
umask(mode);
return 0;
}
================================================
FILE: builtin/unalias.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <mrsh/builtin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/shell.h"
static const char unalias_usage[] = "usage: unalias -a|alias-name...\n";
static void delete_alias_iterator(const char *key, void *_value,
void *user_data) {
free(mrsh_hashtable_del((struct mrsh_hashtable*)user_data, key));
}
int builtin_unalias(struct mrsh_state *state, int argc, char *argv[]) {
struct mrsh_state_priv *priv = state_get_priv(state);
bool all = false;
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":a")) != -1) {
switch (opt) {
case 'a':
all = true;
break;
default:
fprintf(stderr, "unalias: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, unalias_usage);
return 1;
}
}
if (all) {
if (_mrsh_optind < argc) {
fprintf(stderr, unalias_usage);
return 1;
}
mrsh_hashtable_for_each(&priv->aliases, delete_alias_iterator,
&priv->aliases);
return 0;
}
if (_mrsh_optind == argc) {
fprintf(stderr, unalias_usage);
return 1;
}
for (int i = _mrsh_optind; i < argc; ++i) {
free(mrsh_hashtable_del(&priv->aliases, argv[i]));
}
return 0;
}
================================================
FILE: builtin/unset.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <mrsh/builtin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "builtin.h"
#include "mrsh_getopt.h"
#include "shell/shell.h"
static const char unset_usage[] = "usage: unset [-fv] name...\n";
int builtin_unset(struct mrsh_state *state, int argc, char *argv[]) {
struct mrsh_state_priv *priv = state_get_priv(state);
bool funcs = false;
_mrsh_optind = 0;
int opt;
while ((opt = _mrsh_getopt(argc, argv, ":fv")) != -1) {
switch (opt) {
case 'f':
funcs = true;
break;
case 'v':
funcs = false;
break;
default:
fprintf(stderr, "unset: unknown option -- %c\n", _mrsh_optopt);
fprintf(stderr, unset_usage);
return 1;
}
}
if (_mrsh_optind >= argc) {
fprintf(stderr, unset_usage);
return 1;
}
for (int i = _mrsh_optind; i < argc; ++i) {
if (!funcs) {
uint32_t prev_attribs = 0;
if (mrsh_env_get(state, argv[i], &prev_attribs)) {
if ((prev_attribs & MRSH_VAR_ATTRIB_READONLY)) {
fprintf(stderr,
"unset: cannot modify readonly variable %s\n", argv[i]);
return 1;
}
mrsh_env_unset(state, argv[i]);
}
} else {
struct mrsh_function *oldfn =
mrsh_hashtable_del(&priv->functions, argv[i]);
function_destroy(oldfn);
}
}
return 0;
}
================================================
FILE: builtin/unspecified.c
================================================
#include <mrsh/shell.h>
#include <stdlib.h>
#include <stdio.h>
#include "builtin.h"
int builtin_unspecified(struct mrsh_state *state, int argc, char *argv[]) {
// Ref: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01
if (state->interactive) {
fprintf(stderr,
"%s: The behavior of this command is undefined.\n", argv[0]);
} else {
fprintf(stderr, "%s: The behavior of this command is undefined. "
"This is an error in your script. Aborting.\n", argv[0]);
state->exit = 1;
}
return 1;
}
================================================
FILE: builtin/wait.c
================================================
#define _POSIX_C_SOURCE 200112L
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include "builtin.h"
#include "shell/process.h"
#include "shell/shell.h"
struct wait_handle {
pid_t pid;
int status;
};
int builtin_wait(struct mrsh_state *state, int argc, char *argv[]) {
struct mrsh_state_priv *priv = state_get_priv(state);
int npids = argc - 1;
if (npids == 0) {
npids = priv->processes.len;
}
struct wait_handle *pids = malloc(npids * sizeof(struct wait_handle));
if (pids == NULL) {
fprintf(stderr, "wait: unable to allocate pid list");
return EXIT_FAILURE;
}
if (argc == 1) {
/* All known processes */
int _npids = 0;
for (size_t j = 0; j < priv->processes.len; ++j) {
struct mrsh_process *process = priv->processes.data[j];
if (process->terminated) {
continue;
}
pids[_npids].pid = process->pid;
pids[_npids].status = -1;
++_npids;
}
npids = _npids;
} else {
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '%') {
struct mrsh_job *job = job_by_id(state, argv[i], true);
if (!job) {
goto failure;
}
pids[i - 1].pid = job->pgid;
pids[i - 1].status = -1;
} else {
char *endptr;
pid_t pid = (pid_t)strtol(argv[i], &endptr, 10);
if (*endptr != '\0' || argv[i][0] == '\0') {
fprintf(stderr, "wait: error parsing pid '%s'", argv[i]);
goto failure;
}
if (pid <= 0) {
fprintf(stderr, "wait: invalid process ID\n");
goto failure;
}
pids[i - 1].pid = pid;
pids[i - 1].status = -1;
/* Check if this pid is known */
bool found = false;
for (size_t j = 0; j < priv->processes.len; ++j) {
struct mrsh_process *process = priv->processes.data[j];
if (process->pid == pid) {
if (process->terminated) {
pids[i - 1].status = process->stat;
}
found = true;
break;
}
}
if (!found) {
/* Unknown pids are assumed to have exited 127 */
pids[i - 1].status = 127;
}
}
}
}
for (int i = 0; i < npids; ++i) {
int stat;
pid_t waited = waitpid(pids[i].pid, &stat, 0);
// TODO: update jobs internal state?
if (waited == -1) {
if (errno == ECHILD) {
continue;
}
perror("wait");
goto failure;
}
update_process(state, waited, stat);
if (WIFEXITED(stat)) {
pids[i].status = WEXITSTATUS(stat);
} else {
pids[i].status = 129;
}
}
int status;
if (argc == 1) {
status = EXIT_SUCCESS;
} else {
status = pids[npids - 1].status;
}
free(pids);
return status;
failure:
free(pids);
return EXIT_FAILURE;
}
================================================
FILE: configure
================================================
#!/bin/sh -e
SOVERSION=0.0.0
pkg_config=${PKG_CONFIG:-pkg-config}
outdir=${OUTDIR:-.build}
srcdir=${SRCDIR:-$(dirname "$0")}
CC=${CC:-cc}
LIBS=
use_readline=-1
readline=readline
static=
for arg
do
case "$arg" in
--prefix=*)
PREFIX=${arg#*=}
;;
--without-readline)
use_readline=0
;;
--with-readline=*)
use_readline=1
readline=${arg#*=}
;;
--static)
static=$arg
;;
--dynamic)
static=
;;
esac
done
libmrsh() {
genrules libmrsh \
'arithm.c' \
'array.c' \
'ast_print.c' \
'ast.c' \
'buffer.c' \
'builtin/alias.c' \
'builtin/bg.c' \
'builtin/break.c' \
'builtin/builtin.c' \
'builtin/cd.c' \
'builtin/colon.c' \
'builtin/command.c' \
'builtin/dot.c' \
'builtin/eval.c' \
'builtin/exec.c' \
'builtin/exit.c' \
'builtin/export.c' \
'builtin/false.c' \
'builtin/fg.c' \
'builtin/getopts.c' \
'builtin/hash.c' \
'builtin/jobs.c' \
'builtin/pwd.c' \
'builtin/read.c' \
'builtin/return.c' \
'builtin/set.c' \
'builtin/shift.c' \
'builtin/times.c' \
'builtin/trap.c' \
'builtin/true.c' \
'builtin/type.c' \
'builtin/ulimit.c' \
'builtin/umask.c' \
'builtin/unalias.c' \
'builtin/unset.c' \
'builtin/unspecified.c' \
'builtin/wait.c' \
'getopt.c' \
'hashtable.c' \
'parser/arithm.c' \
'parser/parser.c' \
'parser/program.c' \
'parser/word.c' \
'shell/arithm.c' \
'shell/entry.c' \
'shell/job.c' \
'shell/path.c' \
'shell/process.c' \
'shell/redir.c' \
'shell/shell.c' \
'shell/task/pipeline.c' \
'shell/task/simple_command.c' \
'shell/task/task.c' \
'shell/task/word.c' \
'shell/trap.c' \
'shell/word.c'
}
mrsh() {
if [ $use_readline -eq 1 ]
then
genrules mrsh \
'main.c' \
'frontend/readline.c'
else
genrules mrsh \
'main.c' \
'frontend/basic.c'
fi
}
highlight() {
genrules highlight example/highlight.c
}
genrules() {
target="$1"
shift
printf '# Begin generated rules for %s\n' "$target"
for file in "$@"
do
file="${file%.*}"
printf '%s.o: %s.c\n' "$file" "$file"
done
printf '%s_objects=\\\n' "$target"
n=0
for file in "$@"
do
file="${file%.*}"
n=$((n+1))
if [ $n -eq $# ]
then
printf '\t%s.o\n' "$file"
else
printf '\t%s.o \\\n' "$file"
fi
done
printf '# End generated rules for %s\n' "$target"
}
append_cflags() {
for flag
do
CFLAGS="$(printf '%s \\\n\t%s' "$CFLAGS" "$flag")"
done
}
append_ldflags() {
for flag
do
LDFLAGS="$(printf '%s \\\n\t%s' "$LDFLAGS" "$flag")"
done
}
append_libs() {
for flag
do
LIBS="$(printf '%s \\\n\t%s' "$LIBS" "$flag")"
done
}
test_cflags() {
[ ! -e "$outdir"/check.c ] && cat <<-EOF > "$outdir"/check.c
int main(void) { return 0; }
EOF
werror=""
case "$CFLAGS" in
*-Werror*)
werror="-Werror"
;;
esac
if $CC $werror "$@" -o /dev/null "$outdir"/check.c >/dev/null 2>&1
then
append_cflags "$@"
else
return 1
fi
}
test_ldflags() {
[ ! -e "$outdir"/check.c ] && cat <<-EOF > "$outdir"/check.c
int main(void) { return 0; }
EOF
if $CC "$@" -o /dev/null "$outdir"/check.c >/dev/null 2>&1
then
append_ldflags "$@"
else
return 1
fi
}
mkdir -p "$outdir"
if [ -n "$static" ]
then
test_ldflags $static
fi
for flag in \
-g -std=c99 -pedantic -Werror -Wundef -Wlogical-op \
-Wmissing-include-dirs -Wold-style-definition -Wpointer-arith -Winit-self \
-Wfloat-equal -Wstrict-prototypes -Wredundant-decls \
-Wimplicit-fallthrough=2 -Wendif-labels -Wstrict-aliasing=2 -Woverflow \
-Wformat=2 -Wno-missing-braces -Wno-missing-field-initializers \
-Wno-unused-parameter -Wno-unused-result
do
printf "Checking for $flag... "
if test_cflags "$flag"
then
echo yes
else
echo no
fi
done
for flag in -fPIC -Wl,--no-undefined -Wl,--as-needed
do
test_ldflags "$flag"
done
soname=libmrsh.so.$(echo "$SOVERSION" | cut -d. -f1)
printf "Checking for specifying soname for shared lib... "
if ! \
test_ldflags -Wl,-soname,$soname || \
test_ldflags -Wl,-install_name,$soname
then
echo no
echo "Unable to specify soname (is $(uname) supported?)" >&2
exit 1
else
echo yes
fi
printf "Checking for exported symbol restrictions... "
if ! \
test_ldflags -Wl,--version-script="libmrsh.gnu.sym" || \
test_ldflags -Wl,-exported_symbols_list,"libmrsh.darwin.sym"
then
echo no
echo "Unable to specify exported symbols (is $(uname) supported?)" >&2
exit 1
else
echo yes
fi
if [ $use_readline -eq -1 ]
then
printf "Checking for readline... "
if $pkg_config readline
then
readline=readline
use_readline=1
append_cflags -DHAVE_READLINE
# TODO: check for rl_replace_line
append_cflags -DHAVE_READLINE_REPLACE_LINE
echo yes
else
echo no
fi
fi
if [ $use_readline -eq -1 ]
then
printf "Checking for libedit... "
if $pkg_config libedit
then
echo yes
readline=libedit
use_readline=1
append_cflags -DHAVE_EDITLINE
else
echo no
fi
fi
if [ $use_readline -eq 1 ]
then
append_cflags $($pkg_config $static --cflags-only-I $readline)
append_libs $($pkg_config $static --libs $readline)
fi
printf "Creating %s/config.mk... " "$outdir"
cat <<EOF > "$outdir"/config.mk
SOVERSION=$SOVERSION
CC=$CC
PREFIX=${PREFIX:-/usr/local}
_INSTDIR=\$(DESTDIR)\$(PREFIX)
BINDIR?=${BINDIR:-\$(_INSTDIR)/bin}
LIBDIR?=${LIBDIR:-\$(_INSTDIR)/lib}
INCDIR?=${INCDIR:-\$(_INSTDIR)/include}
MANDIR?=${MANDIR:-\$(_INSTDIR)/share/man}
PCDIR?=${PCDIR:-\$(_INSTDIR)/lib/pkgconfig}
CFLAGS=${CFLAGS}
LDFLAGS=${LDFLAGS}
LIBS=${LIBS}
SRCDIR=${srcdir}
all: mrsh highlight libmrsh.so.\$(SOVERSION) \$(OUTDIR)/mrsh.pc
EOF
libmrsh >>"$outdir"/config.mk
mrsh >>"$outdir"/config.mk
highlight >>"$outdir"/config.mk
echo done
touch "$outdir"/cppcache
================================================
FILE: example/highlight.c
================================================
#include <assert.h>
#include <errno.h>
#include <mrsh/buffer.h>
#include <mrsh/parser.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define READ_SIZE 4096
#define FORMAT_STACK_SIZE 64
enum format {
FORMAT_RESET = 0,
FORMAT_GREEN = 32,
FORMAT_YELLOW = 33,
FORMAT_BLUE = 34,
FORMAT_CYAN = 36,
FORMAT_DEFAULT = 39,
FORMAT_LIGHT_BLUE = 94,
};
struct highlight_state {
const char *buf;
size_t offset;
enum format fmt_stack[FORMAT_STACK_SIZE];
size_t fmt_stack_len;
};
static void highlight(struct highlight_state *state, struct mrsh_position *pos,
enum format fmt) {
assert(pos->offset >= state->offset);
fwrite(&state->buf[state->offset], sizeof(char),
pos->offset - state->offset, stdout);
state->offset = pos->offset;
if (fmt == FORMAT_RESET) {
assert(state->fmt_stack_len > 0);
--state->fmt_stack_len;
if (state->fmt_stack_len > 0) {
fmt = state->fmt_stack[state->fmt_stack_len - 1];
}
} else {
if (state->fmt_stack_len >= FORMAT_STACK_SIZE) {
fprintf(stderr, "format stack overflow\n");
exit(1);
}
state->fmt_stack[state->fmt_stack_len] = fmt;
++state->fmt_stack_len;
}
fprintf(stdout, "%c[%dm", 0x1B, fmt);
}
static void highlight_str(struct highlight_state *state,
struct mrsh_range *range, enum format fmt) {
highlight(state, &range->begin, fmt);
highlight(state, &range->end, FORMAT_RESET);
}
static void highlight_char(struct highlight_state *state,
struct mrsh_position *pos, enum format fmt) {
struct mrsh_position next = { .offset = pos->offset + 1 };
highlight(state, pos, fmt);
highlight(state, &next, FORMAT_RESET);
}
static void highlight_word(struct highlight_state *state,
struct mrsh_word *word, bool cmd_name, bool quoted) {
switch (word->type) {
case MRSH_WORD_STRING:;
struct mrsh_word_string *ws = mrsh_word_get_string(word);
if (!quoted) {
enum format fmt = FORMAT_CYAN;
if (ws->single_quoted) {
fmt = FORMAT_YELLOW;
} else if (cmd_name) {
fmt = FORMAT_BLUE;
}
highlight_str(state, &ws->range, fmt);
}
break;
case MRSH_WORD_PARAMETER:;
struct mrsh_word_parameter *wp = mrsh_word_get_parameter(word);
highlight(state, &wp->dollar_pos, FORMAT_CYAN);
highlight_char(state, &wp->dollar_pos, FORMAT_GREEN);
if (mrsh_position_valid(&wp->lbrace_pos)) {
highlight_char(state, &wp->lbrace_pos, FORMAT_GREEN);
}
if (mrsh_range_valid(&wp->op_range)) {
highlight_str(state, &wp->op_range, FORMAT_GREEN);
}
if (wp->arg != NULL) {
highlight_word(state, wp->arg, false, false);
}
struct mrsh_position end = {0};
if (mrsh_position_valid(&wp->rbrace_pos)) {
highlight_char(state, &wp->rbrace_pos, FORMAT_GREEN);
end.offset = wp->rbrace_pos.offset + 1;
} else {
end = wp->name_range.end;
}
highlight(state, &end, FORMAT_RESET);
break;
case MRSH_WORD_COMMAND:;
struct mrsh_word_command *wc = mrsh_word_get_command(word);
if (wc->back_quoted) {
highlight_char(state, &wc->range.begin, FORMAT_GREEN);
}
// TODO: highlight inside
if (wc->back_quoted) {
struct mrsh_position rquote = {
.offset = wc->range.end.offset - 1,
};
highlight_char(state, &rquote, FORMAT_GREEN);
}
break;
case MRSH_WORD_ARITHMETIC:
abort(); // TODO
case MRSH_WORD_LIST:;
struct mrsh_word_list *wl = mrsh_word_get_list(word);
if (wl->children.len == 0) {
break;
}
if (wl->double_quoted) {
highlight(state, &wl->lquote_pos, FORMAT_YELLOW);
}
for (size_t i = 0; i < wl->children.len; ++i) {
struct mrsh_word *child = wl->children.data[i];
highlight_word(state, child, cmd_name, wl->double_quoted);
}
if (wl->double_quoted) {
struct mrsh_position end = { .offset = wl->rquote_pos.offset + 1 };
highlight(state, &end, FORMAT_RESET);
}
break;
}
}
static void highlight_simple_command(struct highlight_state *state,
struct mrsh_simple_command *cmd) {
if (cmd->name != NULL) {
highlight_word(state, cmd->name, true, false);
}
for (size_t i = 0; i < cmd->arguments.len; ++i) {
struct mrsh_word *arg = cmd->arguments.data[i];
highlight_word(state, arg, false, false);
}
// TODO: cmd->io_redirects, cmd->assignments
}
static void highlight_command_list_array(struct highlight_state *state,
struct mrsh_array *array);
static void highlight_command(struct highlight_state *state,
struct mrsh_command *cmd) {
switch (cmd->type) {
case MRSH_SIMPLE_COMMAND:;
struct mrsh_simple_command *sc = mrsh_command_get_simple_command(cmd);
highlight_simple_command(state, sc);
break;
case MRSH_BRACE_GROUP:;
struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
highlight_char(state, &bg->lbrace_pos, FORMAT_GREEN);
highlight_command_list_array(state, &bg->body);
highlight_char(state, &bg->rbrace_pos, FORMAT_GREEN);
break;
case MRSH_SUBSHELL:;
struct mrsh_subshell *s = mrsh_command_get_subshell(cmd);
highlight_char(state, &s->lparen_pos, FORMAT_GREEN);
highlight_command_list_array(state, &s->body);
highlight_char(state, &s->rparen_pos, FORMAT_GREEN);
break;
case MRSH_IF_CLAUSE:;
struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
highlight_str(state, &ic->if_range, FORMAT_BLUE);
highlight_command_list_array(state, &ic->condition);
highlight_str(state, &ic->then_range, FORMAT_BLUE);
highlight_command_list_array(state, &ic->body);
if (ic->else_part != NULL) {
if (mrsh_range_valid(&ic->else_range)) {
highlight_str(state, &ic->else_range, FORMAT_BLUE);
}
highlight_command(state, ic->else_part);
}
if (mrsh_range_valid(&ic->fi_range)) {
highlight_str(state, &ic->fi_range, FORMAT_BLUE);
}
break;
case MRSH_FOR_CLAUSE:;
struct mrsh_for_clause *fc = mrsh_command_get_for_clause(cmd);
highlight_str(state, &fc->for_range, FORMAT_BLUE);
highlight_str(state, &fc->name_range, FORMAT_CYAN);
if (mrsh_range_valid(&fc->in_range)) {
highlight_str(state, &fc->in_range, FORMAT_BLUE);
}
for (size_t i = 0; i < fc->word_list.len; ++i) {
struct mrsh_word *word = fc->word_list.data[i];
highlight_word(state, word, false, false);
}
highlight_str(state, &fc->do_range, FORMAT_BLUE);
highlight_command_list_array(state, &fc->body);
highlight_str(state, &fc->done_range, FORMAT_BLUE);
break;
case MRSH_LOOP_CLAUSE:;
struct mrsh_loop_clause *lc = mrsh_command_get_loop_clause(cmd);
highlight_str(state, &lc->while_until_range, FORMAT_BLUE);
highlight_command_list_array(state, &lc->condition);
highlight_str(state, &lc->do_range, FORMAT_BLUE);
highlight_command_list_array(state, &lc->body);
highlight_str(state, &lc->done_range, FORMAT_BLUE);
break;
case MRSH_CASE_CLAUSE:;
struct mrsh_case_clause *cc = mrsh_command_get_case_clause(cmd);
highlight_str(state, &cc->case_range, FORMAT_BLUE);
highlight_word(state, cc->word, false, false);
highlight_str(state, &cc->in_range, FORMAT_BLUE);
for (size_t i = 0; i < cc->items.len; ++i) {
struct mrsh_case_item *item = cc->items.data[i];
if (mrsh_position_valid(&item->lparen_pos)) {
highlight_char(state, &item->lparen_pos, FORMAT_GREEN);
}
for (size_t j = 0; j < item->patterns.len; ++j) {
struct mrsh_word *pattern = item->patterns.data[j];
highlight_word(state, pattern, false, false);
}
highlight_char(state, &item->rparen_pos, FORMAT_GREEN);
highlight_command_list_array(state, &item->body);
if (mrsh_range_valid(&item->dsemi_range)) {
highlight_str(state, &item->dsemi_range, FORMAT_GREEN);
}
}
highlight_str(state, &cc->esac_range, FORMAT_BLUE);
break;
case MRSH_FUNCTION_DEFINITION:;
struct mrsh_function_definition *fd =
mrsh_command_get_function_definition(cmd);
highlight_str(state, &fd->name_range, FORMAT_BLUE);
highlight_char(state, &fd->lparen_pos, FORMAT_GREEN);
highlight_char(state, &fd->rparen_pos, FORMAT_GREEN);
highlight_command(state, fd->body);
break;
}
}
static void highlight_and_or_list(struct highlight_state *state,
struct mrsh_and_or_list *and_or_list) {
switch (and_or_list->type) {
case MRSH_AND_OR_LIST_PIPELINE:;
struct mrsh_pipeline *pl = mrsh_and_or_list_get_pipeline(and_or_list);
if (mrsh_position_valid(&pl->bang_pos)) {
highlight_char(state, &pl->bang_pos, FORMAT_GREEN);
}
for (size_t i = 0; i < pl->commands.len; ++i) {
struct mrsh_command *cmd = pl->commands.data[i];
highlight_command(state, cmd);
}
break;
case MRSH_AND_OR_LIST_BINOP:;
struct mrsh_binop *binop = mrsh_and_or_list_get_binop(and_or_list);
highlight_and_or_list(state, binop->left);
highlight_str(state, &binop->op_range, FORMAT_GREEN);
highlight_and_or_list(state, binop->right);
break;
}
}
static void highlight_command_list(struct highlight_state *state,
struct mrsh_command_list *list) {
highlight_and_or_list(state, list->and_or_list);
if (mrsh_position_valid(&list->separator_pos)) {
highlight_char(state, &list->separator_pos, FORMAT_GREEN);
}
}
static void highlight_command_list_array(struct highlight_state *state,
struct mrsh_array *array) {
for (size_t i = 0; i < array->len; ++i) {
struct mrsh_command_list *l = array->data[i];
highlight_command_list(state, l);
}
}
static void highlight_program(struct highlight_state *state,
struct mrsh_program *prog) {
highlight_command_list_array(state, &prog->body);
}
int main(int argc, char *argv[]) {
struct mrsh_buffer buf = {0};
while (true) {
char *dst = mrsh_buffer_reserve(&buf, READ_SIZE);
ssize_t n_read = read(STDIN_FILENO, dst, READ_SIZE);
if (n_read < 0) {
perror("read");
return 1;
} else if (n_read == 0) {
break;
}
buf.len += n_read;
}
struct mrsh_parser *parser = mrsh_parser_with_data(buf.data, buf.len);
struct mrsh_program *prog = mrsh_parse_program(parser);
const char *err_msg = mrsh_parser_error(parser, NULL);
if (err_msg != NULL) {
fprintf(stderr, "failed to parse script: %s\n", err_msg);
return 1;
}
if (prog == NULL) {
return 0;
}
mrsh_parser_destroy(parser);
struct highlight_state state = {
.buf = buf.data,
};
struct mrsh_position begin = { .offset = 0 };
highlight(&state, &begin, FORMAT_DEFAULT);
highlight_program(&state, prog);
struct mrsh_position end = { .offset = buf.len };
highlight(&state, &end, FORMAT_RESET);
assert(state.fmt_stack_len == 0);
mrsh_buffer_finish(&buf);
return 0;
}
================================================
FILE: example/meson.build
================================================
executable(
'highlight',
files('highlight.c'),
dependencies: [mrsh],
build_by_default: get_option('examples'),
)
================================================
FILE: frontend/basic.c
================================================
/* Basic, strictly POSIX interactive line interface */
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mrsh/shell.h>
#include <mrsh/parser.h>
#include "frontend.h"
void interactive_init(struct mrsh_state *state) {
// no-op
}
size_t interactive_next(struct mrsh_state *state,
char **line, const char *prompt) {
fprintf(stderr, "%s", prompt);
size_t len = 0;
char *_line = NULL;
errno = 0;
ssize_t n_read = getline(&_line, &len, stdin);
if (n_read < 0) {
free(_line);
if (errno != 0) {
perror("getline");
}
return 0;
}
*line = _line;
return n_read;
}
================================================
FILE: frontend/readline.c
================================================
// readline/editline interactive line interface
#define _POSIX_C_SOURCE 200809L
#include <mrsh/parser.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#if defined(HAVE_READLINE)
#include <readline/history.h>
#include <readline/readline.h>
#elif defined(HAVE_EDITLINE)
#include <editline/readline.h>
#include <histedit.h>
#endif
#include "frontend.h"
#if defined(HAVE_READLINE)
#if !defined(HAVE_READLINE_REPLACE_LINE)
static void rl_replace_line(const char *text,
int clear_undo) {
return;
}
#endif
static void sigint_handler(int n) {
/* Signal safety is done here on a best-effort basis. rl_redisplay is not
* signal safe, but under these circumstances it's very likely that the
* interrupted function will not be affected. */
char newline = '\n';
(void)write(STDOUT_FILENO, &newline, 1);
rl_on_new_line();
rl_replace_line("", 0);
rl_redisplay();
}
#endif
static char *get_history_path(void) {
const char *home = getenv("HOME");
int len = snprintf(NULL, 0, "%s/.mrsh_history", home);
char *path = malloc(len + 1);
if (path == NULL) {
return NULL;
}
snprintf(path, len + 1, "%s/.mrsh_history", home);
return path;
}
void interactive_init(struct mrsh_state *state) {
rl_initialize();
char *history_path = get_history_path();
read_history(history_path);
free(history_path);
}
size_t interactive_next(struct mrsh_state *state,
char **line, const char *prompt) {
/* TODO: make SIGINT handling work with editline */
#if defined(HAVE_READLINE)
struct sigaction sa = { .sa_handler = sigint_handler }, old;
sigaction(SIGINT, &sa, &old);
#endif
char *rline = readline(prompt);
#if defined(HAVE_READLINE)
sigaction(SIGINT, &old, NULL);
#endif
if (!rline) {
return 0;
}
size_t len = strlen(rline);
if (!(state->options & MRSH_OPT_NOLOG)) {
add_history(rline);
char *history_path = get_history_path();
write_history(history_path);
free(history_path);
}
*line = malloc(len + 2);
strcpy(*line, rline);
strcat(*line, "\n");
free(rline);
return len + 1;
}
================================================
FILE: getopt.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdio.h>
#include "mrsh_getopt.h"
char *_mrsh_optarg = NULL;
int _mrsh_optind = 1;
int _mrsh_opterr = 1;
int _mrsh_optopt = 0;
int _mrsh_optpos = 1;
int _mrsh_getopt(int argc, char *const argv[], const char *optstring) {
assert(argv[argc] == NULL);
_mrsh_optarg = NULL;
if (_mrsh_optind == 0) {
_mrsh_optind = 1;
_mrsh_optpos = 1;
}
if (_mrsh_optind >= argc) {
return -1;
}
if (argv[_mrsh_optind][0] != '-') {
return -1;
}
if (argv[_mrsh_optind][1] == '\0') {
return -1;
}
if (argv[_mrsh_optind][1] == '-') {
_mrsh_optind++;
return -1;
}
const char *c = optstring;
if (*c == ':') {
c++;
}
_mrsh_optopt = 0;
int opt = argv[_mrsh_optind][_mrsh_optpos];
for (; *c != '\0'; c++) {
if (*c != opt) {
continue;
}
if (c[1] != ':') {
if (argv[_mrsh_optind][_mrsh_optpos + 1] == '\0') {
_mrsh_optind++;
_mrsh_optpos = 1;
} else {
_mrsh_optpos++;
}
return opt;
}
if (argv[_mrsh_optind][_mrsh_optpos + 1] != '\0') {
_mrsh_optarg = &argv[_mrsh_optind][_mrsh_optpos + 1];
} else {
if (_mrsh_optind + 2 > argc) {
_mrsh_optopt = opt;
if (_mrsh_opterr != 0 && optstring[0] != ':') {
fprintf(stderr, "%s: Option '%c' requires an argument.\n",
argv[0], _mrsh_optopt);
}
return optstring[0] == ':' ? ':' : '?';
}
_mrsh_optarg = argv[++_mrsh_optind];
}
_mrsh_optind++;
return opt;
}
if (_mrsh_opterr != 0 && optstring[0] != ':') {
fprintf(stderr, "%s: Option '%c' not found.\n", argv[0], opt);
}
return '?';
}
================================================
FILE: hashtable.c
================================================
#define _POSIX_C_SOURCE 200809L
#include <mrsh/hashtable.h>
#include <stdlib.h>
#include <string.h>
static unsigned int djb2(const char *str) {
unsigned int hash = 5381;
char c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
void *mrsh_hashtable_get(struct mrsh_hashtable *table, const char *key) {
unsigned int hash = djb2(key);
unsigned int bucket = hash % MRSH_HASHTABLE_BUCKETS;
struct mrsh_hashtable_entry *entry = table->buckets[bucket];
while (entry != NULL) {
if (entry->hash == hash && strcmp(entry->key, key) == 0) {
return entry->value;
}
entry = entry->next;
}
return NULL;
}
void *mrsh_hashtable_set(struct mrsh_hashtable *table, const char *key,
void *value) {
unsigned int hash = djb2(key);
unsigned int bucket = hash % MRSH_HASHTABLE_BUCKETS;
struct mrsh_hashtable_entry *entry = table->buckets[bucket];
struct mrsh_hashtable_entry *previous = NULL;
while (entry != NULL) {
if (entry->hash == hash && strcmp(entry->key, key) == 0) {
break;
}
previous = entry;
entry = entry->next;
}
if (entry == NULL) {
entry = calloc(1, sizeof(struct mrsh_hashtable_entry));
entry->hash = hash;
entry->key = strdup(key);
if (previous != NULL) {
previous->next = entry;
} else {
table->buckets[bucket] = entry;
}
}
void *old_value = entry->value;
entry->value = value;
return old_value;
}
static void hashtable_entry_destroy(struct mrsh_hashtable_entry *entry) {
if (entry == NULL) {
return;
}
free(entry->key);
free(entry);
}
void *mrsh_hashtable_del(struct mrsh_hashtable *table, const char *key) {
unsigned int hash = djb2(key);
unsigned int bucket = hash % MRSH_HASHTABLE_BUCKETS;
struct mrsh_hashtable_entry *entry = table->buckets[bucket];
struct mrsh_hashtable_entry *previous = NULL;
while (entry != NULL) {
if (entry->hash == hash && strcmp(entry->key, key) == 0) {
break;
}
previous = entry;
entry = entry->next;
}
if (entry == NULL) {
return NULL;
}
if (previous != NULL) {
previous->next = entry->next;
} else {
table->buckets[bucket] = entry->next;
}
void *old_value = entry->value;
hashtable_entry_destroy(entry);
return old_value;
}
void mrsh_hashtable_finish(struct mrsh_hashtable *table) {
for (size_t i = 0; i < MRSH_HASHTABLE_BUCKETS; ++i) {
struct mrsh_hashtable_entry *entry = table->buckets[i];
while (entry != NULL) {
struct mrsh_hashtable_entry *next = entry->next;
hashtable_entry_destroy(entry);
entry = next;
}
}
}
void mrsh_hashtable_for_each(struct mrsh_hashtable *table,
mrsh_hashtable_iterator_func iterator, void *user_data) {
for (size_t i = 0; i < MRSH_HASHTABLE_BUCKETS; ++i) {
struct mrsh_hashtable_entry *entry = table->buckets[i];
while (entry != NULL) {
struct mrsh_hashtable_entry *next = entry->next;
iterator(entry->key, entry->value, user_data);
entry = next;
}
}
}
================================================
FILE: include/ast.h
================================================
#ifndef AST_H
#define AST_H
#include <mrsh/ast.h>
void command_list_array_finish(struct mrsh_array *cmds);
void case_item_destroy(struct mrsh_case_item *item);
#endif
================================================
FILE: include/builtin.h
================================================
#ifndef BUILTIN_H
#define BUILTIN_H
#include <mrsh/builtin.h>
struct mrsh_state;
typedef int (*mrsh_builtin_func)(struct mrsh_state *state,
int argc, char *argv[]);
void print_escaped(const char *value);
int builtin_alias(struct mrsh_state *state, int argc, char *argv[]);
int builtin_bg(struct mrsh_state *state, int argc, char *argv[]);
int builtin_break(struct mrsh_state *state, int argc, char *argv[]);
int builtin_cd(struct mrsh_state *state, int argc, char *argv[]);
int builtin_command(struct mrsh_state *state, int argc, char *argv[]);
int builtin_colon(struct mrsh_state *state, int argc, char *argv[]);
int builtin_dot(struct mrsh_state *state, int argc, char *argv[]);
int builtin_eval(struct mrsh_state *state, int argc, char *argv[]);
int builtin_exec(struct mrsh_state *state, int argc, char *argv[]);
int builtin_exit(struct mrsh_state *state, int argc, char *argv[]);
int builtin_export(struct mrsh_state *state, int argc, char *argv[]);
int builtin_false(struct mrsh_state *state, int argc, char *argv[]);
int builtin_fg(struct mrsh_state *state, int argc, char *argv[]);
int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]);
int builtin_hash(struct mrsh_state *state, int argc, char *argv[]);
int builtin_jobs(struct mrsh_state *state, int argc, char *argv[]);
int builtin_pwd(struct mrsh_state *state, int argc, char *argv[]);
int builtin_read(struct mrsh_state *state, int argc, char *argv[]);
int builtin_return(struct mrsh_state *state, int argc, char *argv[]);
int builtin_set(struct mrsh_state *state, int argc, char *argv[]);
int builtin_shift(struct mrsh_state *state, int argc, char *argv[]);
int builtin_times(struct mrsh_state *state, int argc, char *argv[]);
int builtin_trap(struct mrsh_state *state, int argc, char *argv[]);
int builtin_true(struct mrsh_state *state, int argc, char *argv[]);
int builtin_type(struct mrsh_state *state, int argc, char *argv[]);
int builtin_ulimit(struct mrsh_state *state, int argc, char *argv[]);
int builtin_umask(struct mrsh_state *state, int argc, char *argv[]);
int builtin_unalias(struct mrsh_state *state, int argc, char *argv[]);
int builtin_unset(struct mrsh_state *state, int argc, char *argv[]);
int builtin_wait(struct mrsh_state *state, int argc, char *argv[]);
int builtin_unspecified(struct mrsh_state *state, int argc, char *argv[]);
const char *state_get_options(struct mrsh_state *state);
struct mrsh_collect_var {
const char *key, *value;
};
/** Collects and alpha-sorts variables matching attribs. Count will be set to
* the number of matching variables. You are responsible for freeing the return
* value when you're done.*/
struct mrsh_collect_var *collect_vars(struct mrsh_state *state,
uint32_t attribs, size_t *count);
#endif
================================================
FILE: include/frontend.h
================================================
#ifndef FRONTEND_H
#define FRONTEND_H
#include <stddef.h>
#include <stdio.h>
void interactive_init(struct mrsh_state *state);
size_t interactive_next(struct mrsh_state *state,
char **restrict line, const char *prompt);
#endif
================================================
FILE: include/mrsh/arithm.h
================================================
#ifndef MRSH_AST_ARITHM_H
#define MRSH_AST_ARITHM_H
enum mrsh_arithm_expr_type {
MRSH_ARITHM_LITERAL,
MRSH_ARITHM_VARIABLE,
MRSH_ARITHM_UNOP,
MRSH_ARITHM_BINOP,
MRSH_ARITHM_COND,
MRSH_ARITHM_ASSIGN,
};
/**
* An aritmetic expression. One of:
* - A literal
* - A variable
* - An unary operation
* - A binary operation
* - A condition
* - An assignment
*/
struct mrsh_arithm_expr {
enum mrsh_arithm_expr_type type;
};
struct mrsh_arithm_literal {
struct mrsh_arithm_expr expr;
long value;
};
struct mrsh_arithm_variable {
struct mrsh_arithm_expr expr;
char *name;
};
enum mrsh_arithm_unop_type {
MRSH_ARITHM_UNOP_PLUS,
MRSH_ARITHM_UNOP_MINUS,
MRSH_ARITHM_UNOP_TILDE,
MRSH_ARITHM_UNOP_BANG,
};
struct mrsh_arithm_unop {
struct mrsh_arithm_expr expr;
enum mrsh_arithm_unop_type type;
struct mrsh_arithm_expr *body;
};
enum mrsh_arithm_binop_type {
MRSH_ARITHM_BINOP_ASTERISK,
MRSH_ARITHM_BINOP_SLASH,
MRSH_ARITHM_BINOP_PERCENT,
MRSH_ARITHM_BINOP_PLUS,
MRSH_ARITHM_BINOP_MINUS,
MRSH_ARITHM_BINOP_DLESS,
MRSH_ARITHM_BINOP_DGREAT,
MRSH_ARITHM_BINOP_LESS,
MRSH_ARITHM_BINOP_LESSEQ,
MRSH_ARITHM_BINOP_GREAT,
MRSH_ARITHM_BINOP_GREATEQ,
MRSH_ARITHM_BINOP_DEQ,
MRSH_ARITHM_BINOP_BANGEQ,
MRSH_ARITHM_BINOP_AND,
MRSH_ARITHM_BINOP_CIRC,
MRSH_ARITHM_BINOP_OR,
MRSH_ARITHM_BINOP_DAND,
MRSH_ARITHM_BINOP_DOR,
};
struct mrsh_arithm_binop {
struct mrsh_arithm_expr expr;
enum mrsh_arithm_binop_type type;
struct mrsh_arithm_expr *left, *right;
};
struct mrsh_arithm_cond {
struct mrsh_arithm_expr expr;
struct mrsh_arithm_expr *condition, *body, *else_part;
};
enum mrsh_arithm_assign_op {
MRSH_ARITHM_ASSIGN_NONE,
MRSH_ARITHM_ASSIGN_ASTERISK,
MRSH_ARITHM_ASSIGN_SLASH,
MRSH_ARITHM_ASSIGN_PERCENT,
MRSH_ARITHM_ASSIGN_PLUS,
MRSH_ARITHM_ASSIGN_MINUS,
MRSH_ARITHM_ASSIGN_DLESS,
MRSH_ARITHM_ASSIGN_DGREAT,
MRSH_ARITHM_ASSIGN_AND,
MRSH_ARITHM_ASSIGN_CIRC,
MRSH_ARITHM_ASSIGN_OR,
};
struct mrsh_arithm_assign {
struct mrsh_arithm_expr expr;
enum mrsh_arithm_assign_op op;
char *name;
struct mrsh_arithm_expr *value;
};
void mrsh_arithm_expr_destroy(struct mrsh_arithm_expr *expr);
struct mrsh_arithm_literal *mrsh_arithm_literal_create(long value);
struct mrsh_arithm_variable *mrsh_arithm_variable_create(char *name);
struct mrsh_arithm_unop *mrsh_arithm_unop_create(
enum mrsh_arithm_unop_type type, struct mrsh_arithm_expr *body);
struct mrsh_arithm_binop *mrsh_arithm_binop_create(
enum mrsh_arithm_binop_type type, struct mrsh_arithm_expr *left,
struct mrsh_arithm_expr *right);
struct mrsh_arithm_cond *mrsh_arithm_cond_create(
struct mrsh_arithm_expr *condition, struct mrsh_arithm_expr *body,
struct mrsh_arithm_expr *else_part);
struct mrsh_arithm_assign *mrsh_arithm_assign_create(
enum mrsh_arithm_assign_op op, char *name,
struct mrsh_arithm_expr *value);
struct mrsh_arithm_literal *mrsh_arithm_expr_get_literal(
const struct mrsh_arithm_expr *expr);
struct mrsh_arithm_variable *mrsh_arithm_expr_get_variable(
const struct mrsh_arithm_expr *expr);
struct mrsh_arithm_unop *mrsh_arithm_expr_get_unop(
const struct mrsh_arithm_expr *expr);
struct mrsh_arithm_binop *mrsh_arithm_expr_get_binop(
const struct mrsh_arithm_expr *expr);
struct mrsh_arithm_cond *mrsh_arithm_expr_get_cond(
const struct mrsh_arithm_expr *expr);
struct mrsh_arithm_assign *mrsh_arithm_expr_get_assign(
const struct mrsh_arithm_expr *expr);
#endif
================================================
FILE: include/mrsh/array.h
================================================
#ifndef MRSH_ARRAY_H
#define MRSH_ARRAY_H
#include <stdbool.h>
#include <stddef.h>
#include <sys/types.h>
struct mrsh_array {
void **data;
size_t len, cap;
};
bool mrsh_array_reserve(struct mrsh_array *array, size_t size);
ssize_t mrsh_array_add(struct mrsh_array *array, void *value);
void mrsh_array_finish(struct mrsh_array *array);
#endif
================================================
FILE: include/mrsh/ast.h
================================================
#ifndef MRSH_AST_H
#define MRSH_AST_H
#include <mrsh/array.h>
#include <stdbool.h>
/**
* Position describes an arbitrary source position including line and column
* location.
*/
struct mrsh_position {
size_t offset; // starting at 0
int line; // starting at 1
int column; // starting at 1
};
/**
* Range describes a continuous source region. It has a beginning position and
* a non-included ending position.
*/
struct mrsh_range {
struct mrsh_position begin, end;
};
enum mrsh_node_type {
MRSH_NODE_PROGRAM,
MRSH_NODE_COMMAND_LIST,
MRSH_NODE_AND_OR_LIST,
MRSH_NODE_COMMAND,
MRSH_NODE_WORD,
};
struct mrsh_node {
enum mrsh_node_type type;
};
enum mrsh_word_type {
MRSH_WORD_STRING,
MRSH_WORD_PARAMETER,
MRSH_WORD_COMMAND,
MRSH_WORD_ARITHMETIC,
MRSH_WORD_LIST,
};
/**
* A word can be:
* - An unquoted or a single-quoted string
* - A candidate for parameter expansion
* - A candidate for command substitution
* - A candidate for arithmetic expansion
* - An unquoted or a double-quoted list of words
*/
struct mrsh_word {
struct mrsh_node node;
enum mrsh_word_type type;
};
/**
* A string word is a type of word. It can be unquoted or single-quoted.
*/
struct mrsh_word_string {
struct mrsh_word word;
char *str;
bool single_quoted;
// true if candidate for field splitting (ie. result of parameter
// expansion, command substitution or arithmetic expansion)
bool split_fields;
struct mrsh_range range;
};
enum mrsh_word_parameter_op {
MRSH_PARAM_NONE, // `$name` or `${parameter}`, no-op
MRSH_PARAM_MINUS, // `${parameter:-[word]}`, Use Default Values
MRSH_PARAM_EQUAL, // `${parameter:=[word]}`, Assign Default Values
MRSH_PARAM_QMARK, // `${parameter:?[word]}`, Indicate Error if Null or Unset
MRSH_PARAM_PLUS, // `${parameter:+[word]}`, Use Alternative Value
MRSH_PARAM_LEADING_HASH, // `${#parameter}`, String Length
MRSH_PARAM_PERCENT, // `${parameter%[word]}`, Remove Smallest Suffix Pattern
MRSH_PARAM_DPERCENT, // `${parameter%%[word]}`, Remove Largest Suffix Pattern
MRSH_PARAM_HASH, // `${parameter#[word]}`, Remove Smallest Prefix Pattern
MRSH_PARAM_DHASH, // `${parameter##[word]}`, Remove Largest Prefix Pattern
};
/**
* A word parameter is a type of word candidate for parameter expansion. The
* format is either `$name` or `${expression}`.
*/
struct mrsh_word_parameter {
struct mrsh_word word;
char *name;
enum mrsh_word_parameter_op op;
bool colon; // only for -, =, ?, +
struct mrsh_word *arg; // can be NULL
struct mrsh_position dollar_pos;
struct mrsh_range name_range;
struct mrsh_range op_range; // can be invalid
struct mrsh_position lbrace_pos, rbrace_pos; // can be invalid
};
/**
* A word command is a type of word candidate for command substitution. The
* format is either `` `command` `` or `$(command)`.
*/
struct mrsh_word_command {
struct mrsh_word word;
struct mrsh_program *program; // can be NULL
bool back_quoted;
struct mrsh_range range;
};
/**
* An arithmetic word is a type of word containing an arithmetic expression. The
* format is `$((expression))`.
*/
struct mrsh_word_arithmetic {
struct mrsh_word word;
struct mrsh_word *body;
};
/**
* A word list is a type of word. It can be unquoted or double-quoted. Its
* children are _not_ separated by blanks. Here's an example:
*
* abc"d ef"g'h i'
*/
struct mrsh_word_list {
struct mrsh_word word;
struct mrsh_array children; // struct mrsh_word *
bool double_quoted;
struct mrsh_position lquote_pos, rquote_pos; // can be invalid
};
enum mrsh_io_redirect_op {
MRSH_IO_LESS, // <
MRSH_IO_GREAT, // >
MRSH_IO_CLOBBER, // >|
MRSH_IO_DGREAT, // >>
MRSH_IO_LESSAND, // <&
MRSH_IO_GREATAND, // >&
MRSH_IO_LESSGREAT, // <>
MRSH_IO_DLESS, // <<
MRSH_IO_DLESSDASH, // <<-
};
/**
* An IO redirection operator. The format is: `[io_number]op name`.
*/
struct mrsh_io_redirect {
int io_number; // -1 if unspecified
enum mrsh_io_redirect_op op;
struct mrsh_word *name; // filename or here-document delimiter
struct mrsh_array here_document; // struct mrsh_word *, only for << and <<-
struct mrsh_position io_number_pos; // can be invalid
struct mrsh_range op_range;
};
/**
* A variable assignment. The format is: `name=value`.
*/
struct mrsh_assignment {
char *name;
struct mrsh_word *value;
struct mrsh_range name_range;
struct mrsh_position equal_pos;
};
enum mrsh_command_type {
MRSH_SIMPLE_COMMAND,
MRSH_BRACE_GROUP,
MRSH_SUBSHELL,
MRSH_IF_CLAUSE,
MRSH_FOR_CLAUSE,
MRSH_LOOP_CLAUSE, // `while` or `until`
MRSH_CASE_CLAUSE,
MRSH_FUNCTION_DEFINITION,
};
/**
* A command. It is either a simple command, a brace group or an if clause.
*/
struct mrsh_command {
struct mrsh_node node;
enum mrsh_command_type type;
};
/**
* A simple command is a type of command. It contains a command name, followed
* by command arguments. It can also contain IO redirections and variable
* assignments.
*/
struct mrsh_simple_command {
struct mrsh_command command;
struct mrsh_word *name; // can be NULL if it contains only assignments
struct mrsh_array arguments; // struct mrsh_word *
struct mrsh_array io_redirects; // struct mrsh_io_redirect *
struct mrsh_array assignments; // struct mrsh_assignment *
};
/**
* A brace group is a type of command. It contains command lists and executes
* them in the current process environment. The format is:
* `{ compound-list ; }`.
*/
struct mrsh_brace_group {
struct mrsh_command command;
struct mrsh_array body; // struct mrsh_command_list *
struct mrsh_position lbrace_pos, rbrace_pos;
};
/**
* A subshell is a type of command. It contains command lists and executes
* them in a subshell environment. The format is: `( compound-list )`.
*/
struct mrsh_subshell {
struct mrsh_command command;
struct mrsh_array body; // struct mrsh_command_list *
struct mrsh_position lparen_pos, rparen_pos;
};
/**
* An if clause is a type of command. The format is:
*
* if compound-list
* then
* compound-list
* [elif compound-list
* then
* compound-list] ...
* [else
* compound-list]
* fi
*/
struct mrsh_if_clause {
struct mrsh_command command;
struct mrsh_array condition; // struct mrsh_command_list *
struct mrsh_array body; // struct mrsh_command_list *
struct mrsh_command *else_part; // can be NULL
struct mrsh_range if_range; // for `if` or `elif`
struct mrsh_range then_range, fi_range;
struct mrsh_range else_range; // can be invalid
};
/**
* A for clause is a type of command. The format is:
*
* for name [ in [word ... ]]
* do
* compound-list
* done
*/
struct mrsh_for_clause {
struct mrsh_command command;
char *name;
bool in;
struct mrsh_array word_list; // struct mrsh_word *
struct mrsh_array body; // struct mrsh_command_list *
struct mrsh_range for_range, name_range, do_range, done_range;
struct mrsh_range in_range; // can be invalid
};
enum mrsh_loop_type {
MRSH_LOOP_WHILE,
MRSH_LOOP_UNTIL,
};
/**
* A loop clause is a type of command. The format is:
*
* while/until compound-list-1
* do
* compound-list-2
* done
*/
struct mrsh_loop_clause {
struct mrsh_command command;
enum mrsh_loop_type type;
struct mrsh_array condition; // struct mrsh_command_list *
struct mrsh_array body; // struct mrsh_command_list *
struct mrsh_range while_until_range; // for `while` or `until`
struct mrsh_range do_range, done_range;
};
/**
* A case item contains one or more patterns with a body. The format is:
*
* [(] pattern[ | pattern] ... ) compound-list ;;
*
* The double-semicolumn is optional if it's the last item.
*/
struct mrsh_case_item {
struct mrsh_array patterns; // struct mrsh_word *
struct mrsh_array body; // struct mrsh_command_list *
struct mrsh_position lparen_pos; // can be invalid
// TODO: pipe positions between each pattern
struct mrsh_position rparen_pos;
struct mrsh_range dsemi_range; // can be invalid
};
/**
* A case clause is a type of command. The format is:
*
* case word in
* [(] pattern1 ) compound-list ;;
* [[(] pattern[ | pattern] ... ) compound-list ;;] ...
* [[(] pattern[ | pattern] ... ) compound-list]
* esac
*/
struct mrsh_case_clause {
struct mrsh_command command;
struct mrsh_word *word;
struct mrsh_array items; // struct mrsh_case_item *
struct mrsh_range case_range, in_range, esac_range;
};
/**
* A function definition is a type of command. The format is:
*
* fname ( ) compound-command [io-redirect ...]
*/
struct mrsh_function_definition {
struct mrsh_command command;
char *name;
struct mrsh_command *body;
struct mrsh_array io_redirects; // struct mrsh_io_redirect *
struct mrsh_range name_range;
struct mrsh_position lparen_pos, rparen_pos;
};
enum mrsh_and_or_list_type {
MRSH_AND_OR_LIST_PIPELINE,
MRSH_AND_OR_LIST_BINOP,
};
/**
* An AND-OR list is a tree of pipelines and binary operations.
*/
struct mrsh_and_or_list {
struct mrsh_node node;
enum mrsh_and_or_list_type type;
};
/**
* A pipeline is a type of AND-OR list which consists of multiple commands
* separated by `|`. The format is: `[!] command1 [ | command2 ...]`.
*/
struct mrsh_pipeline {
struct mrsh_and_or_list and_or_list;
struct mrsh_array commands; // struct mrsh_command *
bool bang; // whether the pipeline begins with `!`
struct mrsh_position bang_pos; // can be invalid
// TODO: pipe positions between each command
};
enum mrsh_binop_type {
MRSH_BINOP_AND, // `&&`
MRSH_BINOP_OR, // `||`
};
/**
* A binary operation is a type of AND-OR list which consists of multiple
* pipelines separated by `&&` or `||`.
*/
struct mrsh_binop {
struct mrsh_and_or_list and_or_list;
enum mrsh_binop_type type;
struct mrsh_and_or_list *left, *right;
struct mrsh_range op_range;
};
/**
* A command list contains AND-OR lists separated by `;` (for sequential
* execution) or `&` (for asynchronous execution).
*/
struct mrsh_command_list {
struct mrsh_node node;
struct mrsh_and_or_list *and_or_list;
bool ampersand; // whether the command list ends with `&`
struct mrsh_position separator_pos; // can be invalid
};
/**
* A shell program. It contains command lists.
*/
struct mrsh_program {
struct mrsh_node node;
struct mrsh_array body; // struct mrsh_command_list *
};
typedef void (*mrsh_node_iterator_func)(struct mrsh_node *node,
void *user_data);
bool mrsh_position_valid(const struct mrsh_position *pos);
bool mrsh_range_valid(const struct mrsh_range *range);
struct mrsh_word_string *mrsh_word_string_create(char *str,
bool single_quoted);
struct mrsh_word_parameter *mrsh_word_parameter_create(char *name,
enum mrsh_word_parameter_op op, bool colon, struct mrsh_word *arg);
struct mrsh_word_command *mrsh_word_command_create(struct mrsh_program *prog,
bool back_quoted);
struct mrsh_word_arithmetic *mrsh_word_arithmetic_create(
struct mrsh_word *body);
struct mrsh_word_list *mrsh_word_list_create(struct mrsh_array *children,
bool double_quoted);
struct mrsh_simple_command *mrsh_simple_command_create(struct mrsh_word *name,
struct mrsh_array *arguments, struct mrsh_array *io_redirects,
struct mrsh_array *assignments);
struct mrsh_brace_group *mrsh_brace_group_create(struct mrsh_array *body);
struct mrsh_subshell *mrsh_subshell_create(struct mrsh_array *body);
struct mrsh_if_clause *mrsh_if_clause_create(struct mrsh_array *condition,
struct mrsh_array *body, struct mrsh_command *else_part);
struct mrsh_for_clause *mrsh_for_clause_create(char *name, bool in,
struct mrsh_array *word_list, struct mrsh_array *body);
struct mrsh_loop_clause *mrsh_loop_clause_create(enum mrsh_loop_type type,
struct mrsh_array *condition, struct mrsh_array *body);
struct mrsh_case_clause *mrsh_case_clause_create(struct mrsh_word *word,
struct mrsh_array *items);
struct mrsh_function_definition *mrsh_function_definition_create(char *name,
struct mrsh_command *body, struct mrsh_array *io_redirects);
struct mrsh_pipeline *mrsh_pipeline_create(struct mrsh_array *commands,
bool bang);
struct mrsh_binop *mrsh_binop_create(enum mrsh_binop_type type,
struct mrsh_and_or_list *left, struct mrsh_and_or_list *right);
struct mrsh_command_list *mrsh_command_list_create(void);
struct mrsh_program *mrsh_program_create(void);
void mrsh_node_destroy(struct mrsh_node *node);
void mrsh_word_destroy(struct mrsh_word *word);
void mrsh_io_redirect_destroy(struct mrsh_io_redirect *redir);
void mrsh_assignment_destroy(struct mrsh_assignment *assign);
void mrsh_command_destroy(struct mrsh_command *cmd);
void mrsh_and_or_list_destroy(struct mrsh_and_or_list *and_or_list);
void mrsh_command_list_destroy(struct mrsh_command_list *l);
void mrsh_program_destroy(struct mrsh_program *prog);
struct mrsh_word *mrsh_node_get_word(const struct mrsh_node *node);
struct mrsh_command *mrsh_node_get_command(const struct mrsh_node *node);
struct mrsh_and_or_list *mrsh_node_get_and_or_list(
const struct mrsh_node *node);
struct mrsh_command_list *mrsh_node_get_command_list(
const struct mrsh_node *node);
struct mrsh_program *mrsh_node_get_program(const struct mrsh_node *node);
struct mrsh_word_string *mrsh_word_get_string(const struct mrsh_word *word);
struct mrsh_word_parameter *mrsh_word_get_parameter(
const struct mrsh_word *word);
struct mrsh_word_command *mrsh_word_get_command(const struct mrsh_word *word);
struct mrsh_word_arithmetic *mrsh_word_get_arithmetic(
const struct mrsh_word *word);
struct mrsh_word_list *mrsh_word_get_list(const struct mrsh_word *word);
struct mrsh_simple_command *mrsh_command_get_simple_command(
const struct mrsh_command *cmd);
struct mrsh_brace_group *mrsh_command_get_brace_group(
const struct mrsh_command *cmd);
struct mrsh_subshell *mrsh_command_get_subshell(
const struct mrsh_command *cmd);
struct mrsh_if_clause *mrsh_command_get_if_clause(
const struct mrsh_command *cmd);
struct mrsh_for_clause *mrsh_command_get_for_clause(
const struct mrsh_command *cmd);
struct mrsh_loop_clause *mrsh_command_get_loop_clause(
const struct mrsh_command *cmd);
struct mrsh_case_clause *mrsh_command_get_case_clause(
const struct mrsh_command *cmd);
struct mrsh_function_definition *mrsh_command_get_function_definition(
const struct mrsh_command *cmd);
struct mrsh_pipeline *mrsh_and_or_list_get_pipeline(
const struct mrsh_and_or_list *and_or_list);
struct mrsh_binop *mrsh_and_or_list_get_binop(
const struct mrsh_and_or_list *and_or_list);
void mrsh_node_for_each(struct mrsh_node *node,
mrsh_node_iterator_func iterator, void *user_data);
void mrsh_word_range(struct mrsh_word *word, struct mrsh_position *begin,
struct mrsh_position *end);
void mrsh_command_range(struct mrsh_command *cmd, struct mrsh_position *begin,
struct mrsh_position *end);
char *mrsh_word_str(const struct mrsh_word *word);
char *mrsh_node_format(struct mrsh_node *node);
void mrsh_program_print(struct mrsh_program *prog);
struct mrsh_node *mrsh_node_copy(const struct mrsh_node *node);
struct mrsh_word *mrsh_word_copy(const struct mrsh_word *word);
struct mrsh_io_redirect *mrsh_io_redirect_copy(
const struct mrsh_io_redirect *redir);
struct mrsh_assignment *mrsh_assignment_copy(
const struct mrsh_assignment *assign);
struct mrsh_command *mrsh_command_copy(const struct mrsh_command *cmd);
struct mrsh_and_or_list *mrsh_and_or_list_copy(
const struct mrsh_and_or_list *and_or_list);
struct mrsh_command_list *mrsh_command_list_copy(
const struct mrsh_command_list *l);
struct mrsh_program *mrsh_program_copy(const struct mrsh_program *prog);
#endif
================================================
FILE: include/mrsh/buffer.h
================================================
#ifndef MRSH_BUFFER_H
#define MRSH_BUFFER_H
#include <stdbool.h>
#include <stddef.h>
struct mrsh_buffer {
char *data;
size_t len, cap;
};
/**
* Makes sure at least `size` bytes can be written to the buffer, without
* increasing its length. Returns a pointer to the end of the buffer, or NULL if
* resizing fails. Callers are responsible for manually increasing `buf->len`.
*
* This function is useful when e.g. reading from a file. Example with error
* handling left out:
*
* char *dst = mrsh_buffer_reserve(buf, READ_SIZE);
* ssize_t n = read(fd, dst, READ_SIZE);
* buf->len += n;
*/
char *mrsh_buffer_reserve(struct mrsh_buffer *buf, size_t size);
/**
* Increases the length of the buffer by `size` bytes. Returns a pointer to the
* beginning of the newly appended space, or NULL if resizing fails.
*/
char *mrsh_buffer_add(struct mrsh_buffer *buf, size_t size);
bool mrsh_buffer_append(struct mrsh_buffer *buf, const char *data, size_t size);
bool mrsh_buffer_append_char(struct mrsh_buffer *buf, char c);
/**
* Get the buffer's current data and reset it.
*/
char *mrsh_buffer_steal(struct mrsh_buffer *buf);
void mrsh_buffer_finish(struct mrsh_buffer *buf);
#endif
================================================
FILE: include/mrsh/builtin.h
================================================
#ifndef MRSH_BUILTIN_H
#define MRSH_BUILTIN_H
#include <mrsh/shell.h>
bool mrsh_has_builtin(const char *name);
bool mrsh_has_special_builtin(const char *name);
int mrsh_run_builtin(struct mrsh_state *state, int argc, char *argv[]);
struct mrsh_init_args {
const char *command_file;
const char *command_str;
};
int mrsh_process_args(struct mrsh_state *state, struct mrsh_init_args *args,
int argc, char *argv[]);
#endif
================================================
FILE: include/mrsh/entry.h
================================================
#ifndef MRSH_ENTRY_H
#define MRSH_ENTRY_H
#include <mrsh/shell.h>
#include <stdbool.h>
/**
* Expands $PS1 or returns the POSIX-specified default of "$" or "#". The caller
* must free the return value.
*/
char *mrsh_get_ps1(struct mrsh_state *state, int next_history_id);
/**
* Expands $PS2 or returns the POSIX-specified default of ">". The caller must
* free the return value.
*/
char *mrsh_get_ps2(struct mrsh_state *state);
/**
* Expands $PS4 or returns the POSIX-specified default of "+ ". The caller must
* free the return value.
*/
char *mrsh_get_ps4(struct mrsh_state *state);
/**
* Copies variables from the environment and sets up internal variables like
* IFS, PPID, PWD, etc.
*/
bool mrsh_populate_env(struct mrsh_state *state, char **environ);
/**
* Sources /etc/profile and $HOME/.profile. Note that this behavior is not
* specified by POSIX. It is recommended to call this file in login shells
* (for which argv[0][0] == '-' by convention).
*/
void mrsh_source_profile(struct mrsh_state *state);
/** Sources $ENV. It is recommended to source this in interactive shells. */
void mrsh_source_env(struct mrsh_state *state);
/**
* Run the trap registered on EXIT. It is recommended to call this function
* right before exiting the shell.
*/
bool mrsh_run_exit_trap(struct mrsh_state *state);
#endif
================================================
FILE: include/mrsh/hashtable.h
================================================
#ifndef MRSH_HASHTABLE_H
#define MRSH_HASHTABLE_H
#define MRSH_HASHTABLE_BUCKETS 256
struct mrsh_hashtable_entry {
struct mrsh_hashtable_entry *next;
unsigned int hash;
char *key;
void *value;
};
struct mrsh_hashtable {
struct mrsh_hashtable_entry *buckets[MRSH_HASHTABLE_BUCKETS];
};
typedef void (*mrsh_hashtable_iterator_func)(const char *key, void *value,
void *user_data);
void mrsh_hashtable_finish(struct mrsh_hashtable *table);
void *mrsh_hashtable_get(struct mrsh_hashtable *table, const char *key);
void *mrsh_hashtable_set(struct mrsh_hashtable *table, const char *key,
void *value);
void *mrsh_hashtable_del(struct mrsh_hashtable *table, const char *key);
/**
* Calls `iterator` for each (key, value) pair in the hash table. It is safe to
* call `mrsh_hashtable_del` on the current element, however it is not safe to
* do so an any other element.
*/
void mrsh_hashtable_for_each(struct mrsh_hashtable *table,
mrsh_hashtable_iterator_func iterator, void *user_data);
#endif
================================================
FILE: include/mrsh/parser.h
================================================
#ifndef MRSH_PARSER_H
#define MRSH_PARSER_H
#include <mrsh/ast.h>
#include <stdio.h>
struct mrsh_parser;
struct mrsh_buffer;
/**
* An alias callback. The alias named is given as a parameter and the alias
* value should be returned. NULL should be returned if the alias doesn't exist.
*/
typedef const char *(*mrsh_parser_alias_func)(const char *name,
void *user_data);
/**
* Create a parser from a file descriptor.
*/
struct mrsh_parser *mrsh_parser_with_fd(int fd);
/**
* Create a parser from a static buffer.
*/
struct mrsh_parser *mrsh_parser_with_data(const char *buf, size_t len);
/**
* Create a parser with a shared buffer. Data will be read from `buf` each time
* the parser needs input data.
*/
struct mrsh_parser *mrsh_parser_with_buffer(struct mrsh_buffer *buf);
void mrsh_parser_destroy(struct mrsh_parser *parser);
/**
* Parse a complete multi-line program.
*/
struct mrsh_program *mrsh_parse_program(struct mrsh_parser *parser);
/**
* Parse a program line. Continuation lines are consumed.
*/
struct mrsh_program *mrsh_parse_line(struct mrsh_parser *parser);
/**
* Parse an arithmetic expression.
*/
struct mrsh_arithm_expr *mrsh_parse_arithm_expr(struct mrsh_parser *parser);
/**
* Check if the input has been completely consumed.
*/
bool mrsh_parser_eof(struct mrsh_parser *parser);
/**
* Set the alias callback.
*/
void mrsh_parser_set_alias_func(struct mrsh_parser *parser,
mrsh_parser_alias_func alias, void *user_data);
/**
* Check if the parser ended with a syntax error. The error message is returned.
* The error position can optionally be obtained.
*/
const char *mrsh_parser_error(struct mrsh_parser *parser,
struct mrsh_position *pos);
/**
* Check if the input ends on a continuation line.
*/
bool mrsh_parser_continuation_line(struct mrsh_parser *parser);
/**
* Reset the parser state.
*/
void mrsh_parser_reset(struct mrsh_parser *parser);
#endif
================================================
FILE: include/mrsh/shell.h
================================================
#ifndef MRSH_SHELL_H
#define MRSH_SHELL_H
#include <mrsh/arithm.h>
#include <mrsh/ast.h>
#include <mrsh/hashtable.h>
#include <stdint.h>
#include <stdio.h>
enum mrsh_option {
// -a: When this option is on, the export attribute shall be set for each
// variable to which an assignment is performed.
MRSH_OPT_ALLEXPORT = 1 << 0,
// -b: Shall cause the shell to notify the user asynchronously of background
// job completions.
MRSH_OPT_NOTIFY = 1 << 1,
// -C: Prevent existing files from being overwritten by the shell's '>'
// redirection operator; the ">|" redirection operator shall override this
// noclobber option for an individual file.
MRSH_OPT_NOCLOBBER = 1 << 2,
// -e: When this option is on, when any command fails (for any of the
// reasons listed in Consequences of Shell Errors or by returning an exit
// status greater than zero), the shell immediately shall exit
MRSH_OPT_ERREXIT = 1 << 3,
// -f: The shell shall disable pathname expansion.
MRSH_OPT_NOGLOB = 1 << 4,
// -h: Locate and remember utilities invoked by functions as those functions
// are defined (the utilities are normally located when the function is
// executed).
MRSH_OPT_PRELOOKUP = 1 << 5,
// -m: All jobs shall be run in their own process groups. Immediately before
// the shell issues a prompt after completion of the background job, a
// message reporting the exit status of the background job shall be written
// to standard error. If a foreground job stops, the shell shall write a
// message to standard error to that effect, formatted as described by the
// jobs utility.
MRSH_OPT_MONITOR = 1 << 6,
// -n: The shell shall read commands but does not execute them; this can be
// used to check for shell script syntax errors. An interactive shell may
// ignore this option.
MRSH_OPT_NOEXEC = 1 << 7,
// -o ignoreeof: Prevent an interactive shell from exiting on end-of-file.
MRSH_OPT_IGNOREEOF = 1 << 8,
// -o nolog: Prevent the entry of function definitions into the command
// history
MRSH_OPT_NOLOG = 1 << 9,
// -o vi: Allow shell command line editing using the built-in vi editor.
MRSH_OPT_VI = 1 << 10,
// -u: When the shell tries to expand an unset parameter other than the '@'
// and '*' special parameters, it shall write a message to standard error
// and the expansion shall fail.
MRSH_OPT_NOUNSET = 1 << 11,
// -v: The shell shall write its input to standard error as it is read.
MRSH_OPT_VERBOSE = 1 << 12,
// -x: The shell shall write to standard error a trace for each command
// after it expands the command and before it executes it.
MRSH_OPT_XTRACE = 1 << 13,
};
enum mrsh_variable_attrib {
MRSH_VAR_ATTRIB_NONE = 0,
MRSH_VAR_ATTRIB_EXPORT = 1 << 0,
MRSH_VAR_ATTRIB_READONLY = 1 << 1,
};
struct mrsh_call_frame {
char **argv;
int argc;
struct mrsh_call_frame *prev;
};
struct mrsh_state {
int exit;
uint32_t options; // enum mrsh_option
struct mrsh_call_frame *frame; // last call frame
bool interactive;
int last_status;
};
struct mrsh_parser;
struct mrsh_state *mrsh_state_create(void);
void mrsh_state_destroy(struct mrsh_state *state);
void mrsh_state_set_parser_alias_func(
struct mrsh_state *state, struct mrsh_parser *parser);
void mrsh_env_set(struct mrsh_state *state,
const char *key, const char *value, uint32_t attribs);
void mrsh_env_unset(struct mrsh_state *state, const char *key);
const char *mrsh_env_get(struct mrsh_state *state,
const char *key, uint32_t *attribs);
int mrsh_run_program(struct mrsh_state *state, struct mrsh_program *prog);
int mrsh_run_word(struct mrsh_state *state, struct mrsh_word **word);
bool mrsh_run_arithm_expr(struct mrsh_state *state,
struct mrsh_arithm_expr *expr, long *result);
/**
* Enable or disable job control. This will setup signal handlers, process
* groups and the terminal accordingly.
*/
bool mrsh_set_job_control(struct mrsh_state *state, bool enabled);
/**
* Destroy terminated jobs and print job notifications. This function should be
* called after mrsh_run_program.
*/
void mrsh_destroy_terminated_jobs(struct mrsh_state *state);
#endif
================================================
FILE: include/mrsh_getopt.h
================================================
#ifndef MRSH_GETOPT_H
#define MRSH_GETOPT_H
extern char *_mrsh_optarg;
extern int _mrsh_opterr, _mrsh_optind, _mrsh_optopt;
int _mrsh_getopt(int argc, char * const argv[], const char *optstring);
#endif
================================================
FILE: include/parser.h
================================================
#ifndef PARSER_H
#define PARSER_H
#include <stdio.h>
#include <mrsh/buffer.h>
#include <mrsh/parser.h>
enum symbol_name {
EOF_TOKEN,
TOKEN,
NEWLINE,
// The following are the operators (see XBD Operator) containing more than
// one character.
AND_IF,
OR_IF,
DSEMI,
DLESS,
DGREAT,
LESSAND,
GREATAND,
LESSGREAT,
DLESSDASH,
CLOBBER,
};
struct symbol {
enum symbol_name name;
char *str;
};
extern const struct symbol operators[];
extern const size_t operators_len;
extern const size_t operators_max_str_len;
extern const char *keywords[];
extern const size_t keywords_len;
struct mrsh_parser {
int fd; // can be -1
struct mrsh_buffer *in_buf; // can be NULL
bool eof;
struct mrsh_buffer buf; // internal read buffer
struct mrsh_position pos;
struct {
char *msg;
struct mrsh_position pos;
} error;
bool has_sym;
enum symbol_name sym;
struct mrsh_array here_documents;
bool continuation_line;
mrsh_parser_alias_func alias;
void *alias_user_data;
int arith_nested_parens;
};
typedef struct mrsh_word *(*word_func)(struct mrsh_parser *parser, char end);
size_t parser_peek(struct mrsh_parser *parser, char *buf, size_t size);
char parser_peek_char(struct mrsh_parser *parser);
size_t parser_read(struct mrsh_parser *parser, char *buf, size_t size);
char parser_read_char(struct mrsh_parser *parser);
bool token(struct mrsh_parser *parser, const char *str,
struct mrsh_range *range);
bool expect_token(struct mrsh_parser *parser, const char *str,
struct mrsh_range *range);
char *read_token(struct mrsh_parser *parser, size_t len,
struct mrsh_range *range);
void read_continuation_line(struct mrsh_parser *parser);
void parser_set_error(struct mrsh_parser *parser, const char *msg);
void parser_begin(struct mrsh_parser *parser);
bool is_operator_start(char c);
enum symbol_name get_symbol(struct mrsh_parser *parser);
/**
* Invalidates the current symbol. Should be used each time manual
* parser_read calls are performed.
*/
void consume_symbol(struct mrsh_parser *parser);
bool symbol(struct mrsh_parser *parser, enum symbol_name sym);
bool eof(struct mrsh_parser *parser);
bool newline(struct mrsh_parser *parser);
void linebreak(struct mrsh_parser *parser);
bool newline_list(struct mrsh_parser *parser);
size_t peek_name(struct mrsh_parser *parser, bool in_braces);
size_t peek_word(struct mrsh_parser *parser, char end);
struct mrsh_word *expect_dollar(struct mrsh_parser *parser);
struct mrsh_word *back_quotes(struct mrsh_parser *parser);
struct mrsh_word *word(struct mrsh_parser *parser, char end);
struct mrsh_word *arithmetic_word(struct mrsh_parser *parser, char end);
struct mrsh_word *parameter_expansion_word(struct mrsh_parser *parser);
#endif
================================================
FILE: include/shell/job.h
================================================
#ifndef SHELL_JOB_H
#define SHELL_JOB_H
#include <mrsh/array.h>
#include <stdbool.h>
#include <sys/types.h>
#include <termios.h>
struct mrsh_node;
struct mrsh_state;
struct mrsh_process;
/**
* A job is a set of processes, comprising a shell pipeline, and any processes
* descended from it, that are all in the same process group.
*
* In practice, a single job is also created when executing an asynchronous
* command list.
*
* This object is guaranteed to be valid until either:
* - The job terminates
* - The shell is destroyed
*/
struct mrsh_job {
struct mrsh_node *node;
pid_t pgid;
int job_id;
struct termios term_modes; // only valid if stopped
struct mrsh_state *state;
struct mrsh_array processes; // struct mrsh_process *
bool pending_notification; // need to print a job status notification
int last_status;
};
/**
* Create a new job. It will start in the background by default.
*/
struct mrsh_job *job_create(struct mrsh_state *state,
const struct mrsh_node *node);
void job_destroy(struct mrsh_job *job);
/**
* Add a process to the job. This puts the process into the job's process
* group. This has to be done both in the parent and in the child to prevent
* race conditions.
*
* If the job doesn't have a process group (because it's empty), then a new
* process group is created.
*/
void job_add_process(struct mrsh_job *job, struct mrsh_process *proc);
/**
* Polls the job's current status without blocking. Returns:
* - TASK_STATUS_WAIT if the job is running (ie. one or more processes are
* running)
* - TASK_STATUS_STOPPED if the job is stopped (ie. one or more processes are
* stopped, all the others are terminated)
* - An integer >= 0 if the job has terminated (ie. all processes have
* terminated)
*/
int job_poll(struct mrsh_job *job);
/**
* Wait for the completion of the job.
*/
int job_wait(struct mrsh_job *job);
/**
* Wait for the completion of the process.
*/
int job_wait_process(struct mrsh_process *proc);
/**
* Put the job in the foreground or in the background. If the job is stopped and
* cont is set to true, it will be continued.
*
* It is illegal to put a job in the foreground if another job is already in the
* foreground.
*/
bool job_set_foreground(struct mrsh_job *job, bool foreground, bool cont);
/**
* Initialize a child process state.
*/
bool init_job_child_process(struct mrsh_state *state);
/**
* Refreshes status for all jobs.
*/
bool refresh_jobs_status(struct mrsh_state *state);
/**
* Look up a job by its XBD Job Control Job ID.
*
* When using this to look up jobs internally, set interactive to false. This
* suppresses error reporting.
*/
struct mrsh_job *job_by_id(struct mrsh_state *state,
const char *id, bool interactive);
/**
* Return a string describing the process' state. `r` is a random boolean.
*/
const char *job_state_str(struct mrsh_job *job, bool r);
/**
* Send SIGHUP to all running jobs.
*/
void broadcast_sighup_to_jobs(struct mrsh_state *state);
#endif
================================================
FILE: include/shell/path.h
================================================
#ifndef SHELL_PATH_H
#define SHELL_PATH_H
#include <mrsh/shell.h>
#include <stdbool.h>
/* Searches $PATH for the requested file and returns it if found. If exec is
* true, it will require the file to be executable in order to be considered a
* match. If default_path is true, the system's default search path will be
* used instead of the $PATH variable. Fully qualified paths are returned
* as-is.
*/
char *expand_path(struct mrsh_state *state, const char *file, bool exec,
bool default_path);
/* Like getcwd, but returns allocated memory */
char *current_working_dir(void);
#endif
================================================
FILE: include/shell/process.h
================================================
#ifndef SHELL_PROCESS_H
#define SHELL_PROCESS_H
#include <mrsh/shell.h>
#include <stdbool.h>
#include <sys/types.h>
/**
* This struct is used to track child processes.
*
* This object is guaranteed to be valid until either:
* - The process terminates
* - The shell is destroyed
*/
struct mrsh_process {
pid_t pid;
struct mrsh_state *state;
bool stopped;
bool terminated;
int stat; // only valid if terminated
int signal; // only valid if stopped is true
};
/**
* Register a new process.
*/
struct mrsh_process *process_create(struct mrsh_state *state, pid_t pid);
void process_destroy(struct mrsh_process *process);
/**
* Polls the process' current status without blocking. Returns:
* - An integer >= 0 if the process has terminated
* - TASK_STATUS_STOPPED if the process is stopped
* - TASK_STATUS_WAIT if the process is running
*/
int process_poll(struct mrsh_process *process);
/**
* Update the shell's state with a child process status.
*/
void update_process(struct mrsh_state *state, pid_t pid, int stat);
#endif
================================================
FILE: include/shell/redir.h
================================================
#ifndef SHELL_REDIR_H
#define SHELL_REDIR_H
#include <mrsh/ast.h>
int process_redir(const struct mrsh_io_redirect *redir, int *redir_fd);
#endif
================================================
FILE: include/shell/shell.h
================================================
#ifndef SHELL_SHELL_H
#define SHELL_SHELL_H
#include <mrsh/shell.h>
#include <termios.h>
#include "job.h"
#include "process.h"
#include "shell/trap.h"
struct mrsh_variable {
char *value;
uint32_t attribs; // enum mrsh_variable_attrib
};
struct mrsh_function {
struct mrsh_command *body;
};
enum mrsh_branch_control {
MRSH_BRANCH_BREAK,
MRSH_BRANCH_CONTINUE,
MRSH_BRANCH_RETURN,
MRSH_BRANCH_EXIT,
};
struct mrsh_call_frame_priv {
struct mrsh_call_frame pub;
enum mrsh_branch_control branch_control;
int nloops;
};
struct mrsh_trap {
bool set;
enum mrsh_trap_action action;
struct mrsh_program *program;
};
struct mrsh_state_priv {
struct mrsh_state pub;
int term_fd;
struct mrsh_array processes;
struct mrsh_hashtable aliases; // char *
struct mrsh_hashtable variables; // struct mrsh_variable *
struct mrsh_hashtable functions; // struct mrsh_function *
bool job_control;
pid_t pgid;
struct termios term_modes;
struct mrsh_array jobs; // struct mrsh_job *
struct mrsh_job *foreground_job;
struct mrsh_trap traps[MRSH_NSIG];
// TODO: move this to context
bool child; // true if we're not the main shell process
};
/**
* A context holds state information and per-job information. A context is
* guaranteed to be shared between all members of a job.
*/
struct mrsh_context {
struct mrsh_state *state;
// When executing a pipeline, this is set to the job created for the
// pipeline
struct mrsh_job *job;
// When executing an asynchronous list, this is set to true
bool background;
};
void function_destroy(struct mrsh_function *fn);
struct mrsh_call_frame_priv *call_frame_get_priv(struct mrsh_call_frame *frame);
struct mrsh_state_priv *state_get_priv(struct mrsh_state *state);
void push_frame(struct mrsh_state *state, int argc, const char *argv[]);
void pop_frame(struct mrsh_state *state);
#endif
================================================
FILE: include/shell/task.h
================================================
#ifndef SHELL_TASK_H
#define SHELL_TASK_H
#include "shell/shell.h"
#include "shell/word.h"
/**
* The task is waiting for child processes to finish.
*/
#define TASK_STATUS_WAIT -1
/**
* A fatal error occured, the task should be destroyed.
*/
#define TASK_STATUS_ERROR -2
/**
* The task has been stopped and the job has been put in the background.
*/
#define TASK_STATUS_STOPPED -3
/**
* The task has been interrupted for some reason.
*/
#define TASK_STATUS_INTERRUPTED -4
struct mrsh_context;
/* Perform parameter expansion, command substitution and arithmetic expansion. */
int run_word(struct mrsh_context *ctx, struct mrsh_word **word_ptr);
/* Perform all word expansions, as specified in section 2.6. Fills `fields`
* with `char *` elements. Not suitable for assignments. */
int expand_word(struct mrsh_context *ctx, const struct mrsh_word *word,
struct mrsh_array *fields);
int run_simple_command(struct mrsh_context *ctx, struct mrsh_simple_command *sc);
int run_command(struct mrsh_context *ctx, struct mrsh_command *cmd);
int run_and_or_list(struct mrsh_context *ctx, struct mrsh_and_or_list *and_or_list);
int run_pipeline(struct mrsh_context *ctx, struct mrsh_pipeline *pipeline);
int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array);
#endif
================================================
FILE: include/shell/trap.h
================================================
#ifndef SHELL_TRAP_H
#define SHELL_TRAP_H
#include <stdbool.h>
str
gitextract_mub_c0wg/
├── .builds/
│ ├── alpine.yml
│ ├── archlinux.yml
│ └── freebsd.yml
├── .editorconfig
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── arithm.c
├── array.c
├── ast.c
├── ast_print.c
├── buffer.c
├── builtin/
│ ├── alias.c
│ ├── bg.c
│ ├── break.c
│ ├── builtin.c
│ ├── cd.c
│ ├── colon.c
│ ├── command.c
│ ├── dot.c
│ ├── eval.c
│ ├── exec.c
│ ├── exit.c
│ ├── export.c
│ ├── false.c
│ ├── fg.c
│ ├── getopts.c
│ ├── hash.c
│ ├── jobs.c
│ ├── pwd.c
│ ├── read.c
│ ├── return.c
│ ├── set.c
│ ├── shift.c
│ ├── times.c
│ ├── trap.c
│ ├── true.c
│ ├── type.c
│ ├── ulimit.c
│ ├── umask.c
│ ├── unalias.c
│ ├── unset.c
│ ├── unspecified.c
│ └── wait.c
├── configure
├── example/
│ ├── highlight.c
│ └── meson.build
├── frontend/
│ ├── basic.c
│ └── readline.c
├── getopt.c
├── hashtable.c
├── include/
│ ├── ast.h
│ ├── builtin.h
│ ├── frontend.h
│ ├── mrsh/
│ │ ├── arithm.h
│ │ ├── array.h
│ │ ├── ast.h
│ │ ├── buffer.h
│ │ ├── builtin.h
│ │ ├── entry.h
│ │ ├── hashtable.h
│ │ ├── parser.h
│ │ └── shell.h
│ ├── mrsh_getopt.h
│ ├── parser.h
│ └── shell/
│ ├── job.h
│ ├── path.h
│ ├── process.h
│ ├── redir.h
│ ├── shell.h
│ ├── task.h
│ ├── trap.h
│ └── word.h
├── libmrsh.darwin.sym
├── libmrsh.gnu.sym
├── main.c
├── meson.build
├── meson_options.txt
├── mkpc
├── parser/
│ ├── arithm.c
│ ├── parser.c
│ ├── program.c
│ └── word.c
├── shell/
│ ├── arithm.c
│ ├── entry.c
│ ├── job.c
│ ├── path.c
│ ├── process.c
│ ├── redir.c
│ ├── shell.c
│ ├── task/
│ │ ├── pipeline.c
│ │ ├── simple_command.c
│ │ ├── task.c
│ │ └── word.c
│ ├── trap.c
│ └── word.c
└── test/
├── args.sh
├── arithm.sh
├── async.sh
├── case.sh
├── command.sh
├── conformance/
│ ├── 2.2-quoted-characters.sh
│ ├── 2.2-quoted-characters.stdout
│ ├── 2.2.2-nested-single-quotes.fail.sh
│ ├── 2.2.3-alias-expansion.fail.sh
│ ├── 2.2.3-backquote-nonterminated-dquote.undefined.sh
│ ├── 2.2.3-backquote-nonterminated-squote.undefined.sh
│ ├── 2.2.3-dquote-nonterminated-backquote.undefined.sh
│ ├── README
│ ├── harness.sh
│ └── meson.build
├── for.sh
├── function.sh
├── harness.sh
├── if.sh
├── loop.sh
├── meson.build
├── pipeline.sh
├── read.sh
├── readonly.sh
├── redir.sh
├── return.sh
├── subshell.sh
├── syntax.sh
├── ulimit.sh
└── word.sh
SYMBOL INDEX (1479 symbols across 81 files)
FILE: arithm.c
function mrsh_arithm_expr_destroy (line 6) | void mrsh_arithm_expr_destroy(struct mrsh_arithm_expr *expr) {
type mrsh_arithm_literal (line 48) | struct mrsh_arithm_literal
type mrsh_arithm_literal (line 49) | struct mrsh_arithm_literal
type mrsh_arithm_literal (line 50) | struct mrsh_arithm_literal
type mrsh_arithm_variable (line 59) | struct mrsh_arithm_variable
type mrsh_arithm_variable (line 60) | struct mrsh_arithm_variable
type mrsh_arithm_variable (line 61) | struct mrsh_arithm_variable
type mrsh_arithm_unop (line 70) | struct mrsh_arithm_unop
type mrsh_arithm_unop_type (line 71) | enum mrsh_arithm_unop_type
type mrsh_arithm_expr (line 71) | struct mrsh_arithm_expr
type mrsh_arithm_unop (line 72) | struct mrsh_arithm_unop
type mrsh_arithm_unop (line 73) | struct mrsh_arithm_unop
type mrsh_arithm_binop (line 83) | struct mrsh_arithm_binop
type mrsh_arithm_binop_type (line 84) | enum mrsh_arithm_binop_type
type mrsh_arithm_expr (line 84) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 85) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 86) | struct mrsh_arithm_binop
type mrsh_arithm_binop (line 87) | struct mrsh_arithm_binop
type mrsh_arithm_cond (line 98) | struct mrsh_arithm_cond
type mrsh_arithm_expr (line 99) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 99) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 100) | struct mrsh_arithm_expr
type mrsh_arithm_cond (line 101) | struct mrsh_arithm_cond
type mrsh_arithm_cond (line 102) | struct mrsh_arithm_cond
type mrsh_arithm_assign (line 113) | struct mrsh_arithm_assign
type mrsh_arithm_assign_op (line 114) | enum mrsh_arithm_assign_op
type mrsh_arithm_expr (line 115) | struct mrsh_arithm_expr
type mrsh_arithm_assign (line 116) | struct mrsh_arithm_assign
type mrsh_arithm_assign (line 117) | struct mrsh_arithm_assign
type mrsh_arithm_literal (line 128) | struct mrsh_arithm_literal
type mrsh_arithm_expr (line 129) | struct mrsh_arithm_expr
type mrsh_arithm_literal (line 131) | struct mrsh_arithm_literal
type mrsh_arithm_variable (line 134) | struct mrsh_arithm_variable
type mrsh_arithm_expr (line 135) | struct mrsh_arithm_expr
type mrsh_arithm_variable (line 137) | struct mrsh_arithm_variable
type mrsh_arithm_unop (line 140) | struct mrsh_arithm_unop
type mrsh_arithm_expr (line 141) | struct mrsh_arithm_expr
type mrsh_arithm_unop (line 143) | struct mrsh_arithm_unop
type mrsh_arithm_binop (line 146) | struct mrsh_arithm_binop
type mrsh_arithm_expr (line 147) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 149) | struct mrsh_arithm_binop
type mrsh_arithm_cond (line 152) | struct mrsh_arithm_cond
type mrsh_arithm_expr (line 153) | struct mrsh_arithm_expr
type mrsh_arithm_cond (line 155) | struct mrsh_arithm_cond
type mrsh_arithm_assign (line 158) | struct mrsh_arithm_assign
type mrsh_arithm_expr (line 159) | struct mrsh_arithm_expr
type mrsh_arithm_assign (line 161) | struct mrsh_arithm_assign
FILE: array.c
function mrsh_array_reserve (line 7) | bool mrsh_array_reserve(struct mrsh_array *array, size_t new_cap) {
function mrsh_array_add (line 21) | ssize_t mrsh_array_add(struct mrsh_array *array, void *value) {
function mrsh_array_finish (line 40) | void mrsh_array_finish(struct mrsh_array *array) {
FILE: ast.c
function mrsh_position_valid (line 10) | bool mrsh_position_valid(const struct mrsh_position *pos) {
function mrsh_range_valid (line 14) | bool mrsh_range_valid(const struct mrsh_range *range) {
function mrsh_node_destroy (line 19) | void mrsh_node_destroy(struct mrsh_node *node) {
function mrsh_word_destroy (line 45) | void mrsh_word_destroy(struct mrsh_word *word) {
function mrsh_io_redirect_destroy (line 85) | void mrsh_io_redirect_destroy(struct mrsh_io_redirect *redir) {
function mrsh_assignment_destroy (line 98) | void mrsh_assignment_destroy(struct mrsh_assignment *assign) {
function command_list_array_finish (line 107) | void command_list_array_finish(struct mrsh_array *cmds) {
function case_item_destroy (line 115) | void case_item_destroy(struct mrsh_case_item *item) {
function mrsh_command_destroy (line 125) | void mrsh_command_destroy(struct mrsh_command *cmd) {
function mrsh_and_or_list_destroy (line 211) | void mrsh_and_or_list_destroy(struct mrsh_and_or_list *and_or_list) {
type mrsh_command_list (line 236) | struct mrsh_command_list
type mrsh_command_list (line 237) | struct mrsh_command_list
type mrsh_command_list (line 237) | struct mrsh_command_list
function mrsh_command_list_destroy (line 242) | void mrsh_command_list_destroy(struct mrsh_command_list *l) {
type mrsh_program (line 251) | struct mrsh_program
type mrsh_program (line 252) | struct mrsh_program
type mrsh_program (line 252) | struct mrsh_program
function mrsh_program_destroy (line 257) | void mrsh_program_destroy(struct mrsh_program *prog) {
type mrsh_word (line 266) | struct mrsh_word
type mrsh_node (line 266) | struct mrsh_node
type mrsh_word (line 268) | struct mrsh_word
type mrsh_command (line 271) | struct mrsh_command
type mrsh_node (line 271) | struct mrsh_node
type mrsh_command (line 273) | struct mrsh_command
type mrsh_and_or_list (line 276) | struct mrsh_and_or_list
type mrsh_node (line 277) | struct mrsh_node
type mrsh_and_or_list (line 279) | struct mrsh_and_or_list
type mrsh_command_list (line 282) | struct mrsh_command_list
type mrsh_node (line 283) | struct mrsh_node
type mrsh_command_list (line 285) | struct mrsh_command_list
type mrsh_program (line 288) | struct mrsh_program
type mrsh_node (line 288) | struct mrsh_node
type mrsh_program (line 290) | struct mrsh_program
type mrsh_word_string (line 293) | struct mrsh_word_string
type mrsh_word_string (line 295) | struct mrsh_word_string
type mrsh_word_string (line 295) | struct mrsh_word_string
type mrsh_word_parameter (line 303) | struct mrsh_word_parameter
type mrsh_word_parameter_op (line 304) | enum mrsh_word_parameter_op
type mrsh_word (line 304) | struct mrsh_word
type mrsh_word_parameter (line 305) | struct mrsh_word_parameter
type mrsh_word_parameter (line 306) | struct mrsh_word_parameter
type mrsh_word_command (line 316) | struct mrsh_word_command
type mrsh_program (line 316) | struct mrsh_program
type mrsh_word_command (line 318) | struct mrsh_word_command
type mrsh_word_command (line 319) | struct mrsh_word_command
type mrsh_word_arithmetic (line 327) | struct mrsh_word_arithmetic
type mrsh_word (line 328) | struct mrsh_word
type mrsh_word_arithmetic (line 329) | struct mrsh_word_arithmetic
type mrsh_word_arithmetic (line 330) | struct mrsh_word_arithmetic
type mrsh_word_list (line 337) | struct mrsh_word_list
type mrsh_array (line 337) | struct mrsh_array
type mrsh_word_list (line 339) | struct mrsh_word_list
type mrsh_word_list (line 339) | struct mrsh_word_list
type mrsh_word_string (line 349) | struct mrsh_word_string
type mrsh_word (line 349) | struct mrsh_word
type mrsh_word_string (line 351) | struct mrsh_word_string
type mrsh_word_parameter (line 354) | struct mrsh_word_parameter
type mrsh_word (line 355) | struct mrsh_word
type mrsh_word_parameter (line 357) | struct mrsh_word_parameter
type mrsh_word_command (line 360) | struct mrsh_word_command
type mrsh_word (line 360) | struct mrsh_word
type mrsh_word_command (line 362) | struct mrsh_word_command
type mrsh_word_arithmetic (line 365) | struct mrsh_word_arithmetic
type mrsh_word (line 366) | struct mrsh_word
type mrsh_word_arithmetic (line 368) | struct mrsh_word_arithmetic
type mrsh_word_list (line 371) | struct mrsh_word_list
type mrsh_word (line 371) | struct mrsh_word
type mrsh_word_list (line 373) | struct mrsh_word_list
type mrsh_simple_command (line 376) | struct mrsh_simple_command
type mrsh_word (line 376) | struct mrsh_word
type mrsh_array (line 377) | struct mrsh_array
type mrsh_array (line 377) | struct mrsh_array
type mrsh_array (line 378) | struct mrsh_array
type mrsh_simple_command (line 379) | struct mrsh_simple_command
type mrsh_simple_command (line 380) | struct mrsh_simple_command
type mrsh_brace_group (line 390) | struct mrsh_brace_group
type mrsh_array (line 390) | struct mrsh_array
type mrsh_brace_group (line 391) | struct mrsh_brace_group
type mrsh_brace_group (line 391) | struct mrsh_brace_group
type mrsh_subshell (line 398) | struct mrsh_subshell
type mrsh_array (line 398) | struct mrsh_array
type mrsh_subshell (line 399) | struct mrsh_subshell
type mrsh_subshell (line 399) | struct mrsh_subshell
type mrsh_if_clause (line 406) | struct mrsh_if_clause
type mrsh_array (line 406) | struct mrsh_array
type mrsh_array (line 407) | struct mrsh_array
type mrsh_command (line 407) | struct mrsh_command
type mrsh_if_clause (line 408) | struct mrsh_if_clause
type mrsh_if_clause (line 408) | struct mrsh_if_clause
type mrsh_for_clause (line 417) | struct mrsh_for_clause
type mrsh_array (line 418) | struct mrsh_array
type mrsh_array (line 418) | struct mrsh_array
type mrsh_for_clause (line 419) | struct mrsh_for_clause
type mrsh_for_clause (line 419) | struct mrsh_for_clause
type mrsh_loop_clause (line 429) | struct mrsh_loop_clause
type mrsh_loop_type (line 429) | enum mrsh_loop_type
type mrsh_array (line 430) | struct mrsh_array
type mrsh_array (line 430) | struct mrsh_array
type mrsh_loop_clause (line 431) | struct mrsh_loop_clause
type mrsh_loop_clause (line 431) | struct mrsh_loop_clause
type mrsh_case_clause (line 440) | struct mrsh_case_clause
type mrsh_word (line 440) | struct mrsh_word
type mrsh_array (line 441) | struct mrsh_array
type mrsh_case_clause (line 442) | struct mrsh_case_clause
type mrsh_case_clause (line 442) | struct mrsh_case_clause
type mrsh_function_definition (line 450) | struct mrsh_function_definition
type mrsh_command (line 451) | struct mrsh_command
type mrsh_array (line 451) | struct mrsh_array
type mrsh_function_definition (line 452) | struct mrsh_function_definition
type mrsh_function_definition (line 453) | struct mrsh_function_definition
type mrsh_simple_command (line 462) | struct mrsh_simple_command
type mrsh_command (line 463) | struct mrsh_command
type mrsh_simple_command (line 465) | struct mrsh_simple_command
type mrsh_brace_group (line 468) | struct mrsh_brace_group
type mrsh_command (line 469) | struct mrsh_command
type mrsh_brace_group (line 471) | struct mrsh_brace_group
type mrsh_subshell (line 474) | struct mrsh_subshell
type mrsh_command (line 475) | struct mrsh_command
type mrsh_subshell (line 477) | struct mrsh_subshell
type mrsh_if_clause (line 480) | struct mrsh_if_clause
type mrsh_command (line 481) | struct mrsh_command
type mrsh_if_clause (line 483) | struct mrsh_if_clause
type mrsh_for_clause (line 486) | struct mrsh_for_clause
type mrsh_command (line 487) | struct mrsh_command
type mrsh_for_clause (line 489) | struct mrsh_for_clause
type mrsh_loop_clause (line 492) | struct mrsh_loop_clause
type mrsh_command (line 493) | struct mrsh_command
type mrsh_loop_clause (line 495) | struct mrsh_loop_clause
type mrsh_case_clause (line 498) | struct mrsh_case_clause
type mrsh_command (line 499) | struct mrsh_command
type mrsh_case_clause (line 501) | struct mrsh_case_clause
type mrsh_function_definition (line 504) | struct mrsh_function_definition
type mrsh_command (line 505) | struct mrsh_command
type mrsh_function_definition (line 507) | struct mrsh_function_definition
type mrsh_pipeline (line 510) | struct mrsh_pipeline
type mrsh_array (line 510) | struct mrsh_array
type mrsh_pipeline (line 512) | struct mrsh_pipeline
type mrsh_pipeline (line 512) | struct mrsh_pipeline
type mrsh_binop (line 520) | struct mrsh_binop
type mrsh_binop_type (line 520) | enum mrsh_binop_type
type mrsh_and_or_list (line 521) | struct mrsh_and_or_list
type mrsh_and_or_list (line 521) | struct mrsh_and_or_list
type mrsh_binop (line 522) | struct mrsh_binop
type mrsh_binop (line 522) | struct mrsh_binop
type mrsh_pipeline (line 531) | struct mrsh_pipeline
type mrsh_and_or_list (line 532) | struct mrsh_and_or_list
type mrsh_pipeline (line 534) | struct mrsh_pipeline
type mrsh_binop (line 537) | struct mrsh_binop
type mrsh_and_or_list (line 538) | struct mrsh_and_or_list
type mrsh_binop (line 540) | struct mrsh_binop
function node_array_for_each (line 543) | static void node_array_for_each(struct mrsh_array *nodes,
function mrsh_node_for_each (line 551) | void mrsh_node_for_each(struct mrsh_node *node,
function position_next (line 660) | static void position_next(struct mrsh_position *dst,
function mrsh_word_range (line 667) | void mrsh_word_range(struct mrsh_word *word, struct mrsh_position *begin,
function mrsh_command_range (line 718) | void mrsh_command_range(struct mrsh_command *cmd, struct mrsh_position *...
function buffer_append_str (line 807) | static void buffer_append_str(struct mrsh_buffer *buf, const char *str) {
function word_str (line 811) | static void word_str(const struct mrsh_word *word, struct mrsh_buffer *b...
type mrsh_word (line 832) | struct mrsh_word
type mrsh_buffer (line 833) | struct mrsh_buffer
type mrsh_binop_type (line 839) | enum mrsh_binop_type
type mrsh_node (line 849) | struct mrsh_node
type mrsh_buffer (line 849) | struct mrsh_buffer
function node_array_format (line 851) | static void node_array_format(struct mrsh_array *array, const char *sep,
function node_format (line 862) | static void node_format(struct mrsh_node *node, struct mrsh_buffer *buf) {
type mrsh_node (line 1009) | struct mrsh_node
type mrsh_buffer (line 1010) | struct mrsh_buffer
type mrsh_node (line 1016) | struct mrsh_node
type mrsh_node (line 1016) | struct mrsh_node
type mrsh_program (line 1019) | struct mrsh_program
type mrsh_program (line 1020) | struct mrsh_program
type mrsh_command_list (line 1023) | struct mrsh_command_list
type mrsh_command_list (line 1024) | struct mrsh_command_list
type mrsh_and_or_list (line 1027) | struct mrsh_and_or_list
type mrsh_and_or_list (line 1028) | struct mrsh_and_or_list
type mrsh_command (line 1031) | struct mrsh_command
type mrsh_command (line 1032) | struct mrsh_command
type mrsh_word (line 1035) | struct mrsh_word
type mrsh_word (line 1036) | struct mrsh_word
type mrsh_word (line 1042) | struct mrsh_word
type mrsh_word (line 1042) | struct mrsh_word
type mrsh_word_string (line 1045) | struct mrsh_word_string
type mrsh_word_string (line 1046) | struct mrsh_word_string
type mrsh_word_parameter (line 1050) | struct mrsh_word_parameter
type mrsh_word (line 1052) | struct mrsh_word
type mrsh_word_parameter (line 1057) | struct mrsh_word_parameter
type mrsh_word_command (line 1061) | struct mrsh_word_command
type mrsh_word_command (line 1062) | struct mrsh_word_command
type mrsh_word_arithmetic (line 1066) | struct mrsh_word_arithmetic
type mrsh_word_arithmetic (line 1067) | struct mrsh_word_arithmetic
type mrsh_word_list (line 1071) | struct mrsh_word_list
type mrsh_array (line 1072) | struct mrsh_array
type mrsh_word (line 1075) | struct mrsh_word
type mrsh_word_list (line 1078) | struct mrsh_word_list
type mrsh_io_redirect (line 1085) | struct mrsh_io_redirect
type mrsh_io_redirect (line 1086) | struct mrsh_io_redirect
type mrsh_io_redirect (line 1087) | struct mrsh_io_redirect
type mrsh_io_redirect (line 1088) | struct mrsh_io_redirect
type mrsh_word (line 1095) | struct mrsh_word
type mrsh_assignment (line 1102) | struct mrsh_assignment
type mrsh_assignment (line 1103) | struct mrsh_assignment
type mrsh_assignment (line 1104) | struct mrsh_assignment
type mrsh_assignment (line 1105) | struct mrsh_assignment
function command_list_array_copy (line 1111) | static void command_list_array_copy(struct mrsh_array *dst,
type mrsh_case_item (line 1120) | struct mrsh_case_item
type mrsh_case_item (line 1120) | struct mrsh_case_item
type mrsh_case_item (line 1121) | struct mrsh_case_item
type mrsh_case_item (line 1121) | struct mrsh_case_item
type mrsh_word (line 1125) | struct mrsh_word
type mrsh_command (line 1134) | struct mrsh_command
type mrsh_command (line 1134) | struct mrsh_command
type mrsh_array (line 1135) | struct mrsh_array
type mrsh_simple_command (line 1138) | struct mrsh_simple_command
type mrsh_word (line 1140) | struct mrsh_word
type mrsh_array (line 1145) | struct mrsh_array
type mrsh_word (line 1148) | struct mrsh_word
type mrsh_io_redirect (line 1154) | struct mrsh_io_redirect
type mrsh_array (line 1158) | struct mrsh_array
type mrsh_assignment (line 1161) | struct mrsh_assignment
type mrsh_simple_command (line 1165) | struct mrsh_simple_command
type mrsh_brace_group (line 1169) | struct mrsh_brace_group
type mrsh_array (line 1170) | struct mrsh_array
type mrsh_brace_group (line 1172) | struct mrsh_brace_group
type mrsh_subshell (line 1175) | struct mrsh_subshell
type mrsh_array (line 1176) | struct mrsh_array
type mrsh_subshell (line 1178) | struct mrsh_subshell
type mrsh_if_clause (line 1181) | struct mrsh_if_clause
type mrsh_array (line 1183) | struct mrsh_array
type mrsh_array (line 1186) | struct mrsh_array
type mrsh_command (line 1189) | struct mrsh_command
type mrsh_if_clause (line 1194) | struct mrsh_if_clause
type mrsh_for_clause (line 1198) | struct mrsh_for_clause
type mrsh_array (line 1200) | struct mrsh_array
type mrsh_word (line 1203) | struct mrsh_word
type mrsh_array (line 1207) | struct mrsh_array
type mrsh_for_clause (line 1210) | struct mrsh_for_clause
type mrsh_loop_clause (line 1214) | struct mrsh_loop_clause
type mrsh_array (line 1216) | struct mrsh_array
type mrsh_array (line 1219) | struct mrsh_array
type mrsh_loop_clause (line 1222) | struct mrsh_loop_clause
type mrsh_case_clause (line 1226) | struct mrsh_case_clause
type mrsh_array (line 1228) | struct mrsh_array
type mrsh_case_item (line 1231) | struct mrsh_case_item
type mrsh_case_clause (line 1235) | struct mrsh_case_clause
type mrsh_function_definition (line 1239) | struct mrsh_function_definition
type mrsh_io_redirect (line 1244) | struct mrsh_io_redirect
type mrsh_function_definition (line 1248) | struct mrsh_function_definition
type mrsh_and_or_list (line 1256) | struct mrsh_and_or_list
type mrsh_and_or_list (line 1257) | struct mrsh_and_or_list
type mrsh_pipeline (line 1260) | struct mrsh_pipeline
type mrsh_array (line 1261) | struct mrsh_array
type mrsh_command (line 1264) | struct mrsh_command
type mrsh_pipeline (line 1267) | struct mrsh_pipeline
type mrsh_binop (line 1271) | struct mrsh_binop
type mrsh_binop (line 1272) | struct mrsh_binop
type mrsh_command_list (line 1280) | struct mrsh_command_list
type mrsh_command_list (line 1281) | struct mrsh_command_list
type mrsh_command_list (line 1282) | struct mrsh_command_list
type mrsh_program (line 1288) | struct mrsh_program
type mrsh_program (line 1288) | struct mrsh_program
type mrsh_program (line 1289) | struct mrsh_program
FILE: ast_print.c
function make_sub_prefix (line 13) | static size_t make_sub_prefix(const char *prefix, bool last, char *buf) {
function print_prefix (line 21) | static void print_prefix(const char *prefix, bool last) {
function print_range (line 25) | static void print_range(const struct mrsh_range *range) {
type mrsh_word_parameter_op (line 30) | enum mrsh_word_parameter_op
type mrsh_program (line 56) | struct mrsh_program
function print_word (line 58) | static void print_word(struct mrsh_word *word, const char *prefix) {
type mrsh_io_redirect_op (line 116) | enum mrsh_io_redirect_op
type mrsh_array (line 140) | struct mrsh_array
function print_io_redirect (line 142) | static void print_io_redirect(struct mrsh_io_redirect *redir,
function print_assignment (line 169) | static void print_assignment(struct mrsh_assignment *assign,
function print_simple_command (line 184) | static void print_simple_command(struct mrsh_simple_command *cmd,
type mrsh_command_list (line 233) | struct mrsh_command_list
function print_command_list_array (line 235) | static void print_command_list_array(struct mrsh_array *array,
type mrsh_command (line 249) | struct mrsh_command
function print_if_clause (line 251) | static void print_if_clause(struct mrsh_if_clause *ic, const char *prefi...
function print_word_array (line 277) | static void print_word_array(struct mrsh_array *words, const char *prefi...
function print_for_clause (line 290) | static void print_for_clause(struct mrsh_for_clause *fc, const char *pre...
type mrsh_loop_type (line 312) | enum mrsh_loop_type
function print_loop_clause (line 322) | static void print_loop_clause(struct mrsh_loop_clause *lc, const char *p...
function print_case_item (line 339) | static void print_case_item(struct mrsh_case_item *item, const char *pre...
function print_case_item_array (line 356) | static void print_case_item_array(struct mrsh_array *items,
function print_case_clause (line 370) | static void print_case_clause(struct mrsh_case_clause *cc, const char *p...
function print_function_definition (line 387) | static void print_function_definition(struct mrsh_function_definition *fd,
function print_command (line 394) | static void print_command(struct mrsh_command *cmd, const char *prefix) {
function print_pipeline (line 434) | static void print_pipeline(struct mrsh_pipeline *pl, const char *prefix) {
type mrsh_binop_type (line 449) | enum mrsh_binop_type
type mrsh_and_or_list (line 459) | struct mrsh_and_or_list
function print_binop (line 461) | static void print_binop(struct mrsh_binop *binop, const char *prefix) {
function print_and_or_list (line 475) | static void print_and_or_list(struct mrsh_and_or_list *and_or_list, cons...
function print_command_list (line 488) | static void print_command_list(struct mrsh_command_list *list,
function print_program (line 495) | static void print_program(struct mrsh_program *prog, const char *prefix) {
function mrsh_program_print (line 501) | void mrsh_program_print(struct mrsh_program *prog) {
FILE: buffer.c
type mrsh_buffer (line 5) | struct mrsh_buffer
type mrsh_buffer (line 27) | struct mrsh_buffer
function mrsh_buffer_append (line 37) | bool mrsh_buffer_append(struct mrsh_buffer *buf, const char *data, size_...
function mrsh_buffer_append_char (line 47) | bool mrsh_buffer_append_char(struct mrsh_buffer *buf, char c) {
type mrsh_buffer (line 57) | struct mrsh_buffer
function mrsh_buffer_finish (line 64) | void mrsh_buffer_finish(struct mrsh_buffer *buf) {
FILE: builtin/alias.c
function print_alias_iterator (line 12) | static void print_alias_iterator(const char *key, void *_value,
function builtin_alias (line 20) | int builtin_alias(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/bg.c
function builtin_bg (line 11) | int builtin_bg(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/break.c
function builtin_break (line 12) | int builtin_break(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/builtin.c
type builtin (line 10) | struct builtin {
type builtin (line 16) | struct builtin
type builtin (line 66) | struct builtin
function builtin_compare (line 72) | static int builtin_compare(const void *_a, const void *_b) {
function unspecified_compare (line 77) | static int unspecified_compare(const void *_a, const void *_b) {
type builtin (line 83) | struct builtin
type builtin (line 89) | struct builtin
function mrsh_has_builtin (line 94) | bool mrsh_has_builtin(const char *name) {
function mrsh_has_special_builtin (line 98) | bool mrsh_has_special_builtin(const char *name) {
function mrsh_run_builtin (line 103) | int mrsh_run_builtin(struct mrsh_state *state, int argc, char *argv[]) {
function print_escaped (line 115) | void print_escaped(const char *value) {
type collect_iter (line 138) | struct collect_iter {
function collect_vars_iterator (line 144) | static void collect_vars_iterator(const char *key, void *_var, void *dat...
function varcmp (line 160) | static int varcmp(const void *p1, const void *p2) {
type mrsh_collect_var (line 166) | struct mrsh_collect_var
type mrsh_state (line 166) | struct mrsh_state
type mrsh_state_priv (line 168) | struct mrsh_state_priv
type collect_iter (line 170) | struct collect_iter
type mrsh_collect_var (line 173) | struct mrsh_collect_var
type mrsh_collect_var (line 177) | struct mrsh_collect_var
FILE: builtin/cd.c
function cd (line 15) | static int cd(struct mrsh_state *state, const char *path) {
function isdir (line 33) | static int isdir(char *path) {
function builtin_cd (line 39) | int builtin_cd(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/colon.c
function builtin_colon (line 5) | int builtin_colon(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/command.c
function verify_command (line 17) | static int verify_command(struct mrsh_state *state, const char *command_...
function run_command (line 60) | static int run_command(struct mrsh_state *state, int argc, char *argv[],
function builtin_command (line 91) | int builtin_command(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/dot.c
function builtin_dot (line 16) | int builtin_dot(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/eval.c
function builtin_eval (line 15) | int builtin_eval(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/exec.c
function builtin_exec (line 9) | int builtin_exec(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/exit.c
function builtin_exit (line 10) | int builtin_exit(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/export.c
function builtin_export (line 11) | int builtin_export(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/false.c
function builtin_false (line 5) | int builtin_false(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/fg.c
function builtin_fg (line 10) | int builtin_fg(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/getopts.c
function builtin_getopts (line 14) | int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/hash.c
function builtin_hash (line 11) | int builtin_hash(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/jobs.c
type jobs_context (line 15) | struct jobs_context {
function show_job (line 22) | static void show_job(struct mrsh_job *job, const struct jobs_context *ct...
function builtin_jobs (line 50) | int builtin_jobs(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/pwd.c
function builtin_pwd (line 11) | int builtin_pwd(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/read.c
function builtin_read (line 14) | int builtin_read(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/return.c
function builtin_return (line 10) | int builtin_return(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/set.c
type option_map (line 20) | struct option_map {
type option_map (line 26) | struct option_map
type mrsh_state (line 43) | struct mrsh_state
function print_options (line 56) | static void print_options(struct mrsh_state *state) {
type option_map (line 66) | struct option_map
type option_map (line 75) | struct option_map
function argv_free (line 93) | static void argv_free(int argc, char **argv) {
function set (line 103) | static int set(struct mrsh_state *state, int argc, char *argv[],
function builtin_set (line 216) | int builtin_set(struct mrsh_state *state, int argc, char *argv[]) {
function mrsh_process_args (line 232) | int mrsh_process_args(struct mrsh_state *state, struct mrsh_init_args *i...
FILE: builtin/shift.c
function builtin_shift (line 9) | int builtin_shift(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/times.c
function builtin_times (line 12) | int builtin_times(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/trap.c
function is_decimal_str (line 53) | static bool is_decimal_str(const char *str) {
function parse_sig (line 62) | static int parse_sig(const char *str) {
function print_traps (line 109) | static void print_traps(struct mrsh_state *state) {
function builtin_trap (line 134) | int builtin_trap(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/true.c
function builtin_true (line 5) | int builtin_true(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/type.c
function builtin_type (line 11) | int builtin_type(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/ulimit.c
function builtin_ulimit (line 16) | int builtin_ulimit(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/umask.c
type umask_symbolic_state (line 12) | enum umask_symbolic_state {
function mode_t (line 17) | static mode_t umask_current_mask(void) {
function umask_update_mode (line 25) | static bool umask_update_mode(mode_t *mode, char action, mode_t *perm_ma...
function umask_mode (line 45) | static bool umask_mode(mode_t *mode, char *symbolic) {
function umask_modestring (line 132) | static void umask_modestring(char string[static 4], mode_t mode) {
function umask_print_symbolic (line 148) | static void umask_print_symbolic(mode_t mask) {
function builtin_umask (line 161) | int builtin_umask(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/unalias.c
function delete_alias_iterator (line 12) | static void delete_alias_iterator(const char *key, void *_value,
function builtin_unalias (line 17) | int builtin_unalias(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/unset.c
function builtin_unset (line 12) | int builtin_unset(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/unspecified.c
function builtin_unspecified (line 6) | int builtin_unspecified(struct mrsh_state *state, int argc, char *argv[]) {
FILE: builtin/wait.c
type wait_handle (line 12) | struct wait_handle {
function builtin_wait (line 17) | int builtin_wait(struct mrsh_state *state, int argc, char *argv[]) {
FILE: example/highlight.c
type format (line 13) | enum format {
type highlight_state (line 23) | struct highlight_state {
function highlight (line 31) | static void highlight(struct highlight_state *state, struct mrsh_positio...
function highlight_str (line 57) | static void highlight_str(struct highlight_state *state,
function highlight_char (line 63) | static void highlight_char(struct highlight_state *state,
function highlight_word (line 70) | static void highlight_word(struct highlight_state *state,
function highlight_simple_command (line 145) | static void highlight_simple_command(struct highlight_state *state,
type highlight_state (line 159) | struct highlight_state
type mrsh_array (line 160) | struct mrsh_array
function highlight_command (line 162) | static void highlight_command(struct highlight_state *state,
function highlight_and_or_list (line 253) | static void highlight_and_or_list(struct highlight_state *state,
function highlight_command_list (line 275) | static void highlight_command_list(struct highlight_state *state,
function highlight_command_list_array (line 283) | static void highlight_command_list_array(struct highlight_state *state,
function highlight_program (line 291) | static void highlight_program(struct highlight_state *state,
function main (line 296) | int main(int argc, char *argv[]) {
FILE: frontend/basic.c
function interactive_init (line 11) | void interactive_init(struct mrsh_state *state) {
function interactive_next (line 15) | size_t interactive_next(struct mrsh_state *state,
FILE: frontend/readline.c
function rl_replace_line (line 21) | static void rl_replace_line(const char *text,
function sigint_handler (line 27) | static void sigint_handler(int n) {
function interactive_init (line 50) | void interactive_init(struct mrsh_state *state) {
function interactive_next (line 57) | size_t interactive_next(struct mrsh_state *state,
FILE: getopt.c
function _mrsh_getopt (line 12) | int _mrsh_getopt(int argc, char *const argv[], const char *optstring) {
FILE: hashtable.c
function djb2 (line 6) | static unsigned int djb2(const char *str) {
type mrsh_hashtable (line 15) | struct mrsh_hashtable
type mrsh_hashtable_entry (line 18) | struct mrsh_hashtable_entry
type mrsh_hashtable (line 30) | struct mrsh_hashtable
type mrsh_hashtable_entry (line 34) | struct mrsh_hashtable_entry
type mrsh_hashtable_entry (line 36) | struct mrsh_hashtable_entry
type mrsh_hashtable_entry (line 46) | struct mrsh_hashtable_entry
function hashtable_entry_destroy (line 61) | static void hashtable_entry_destroy(struct mrsh_hashtable_entry *entry) {
type mrsh_hashtable (line 69) | struct mrsh_hashtable
type mrsh_hashtable_entry (line 72) | struct mrsh_hashtable_entry
type mrsh_hashtable_entry (line 74) | struct mrsh_hashtable_entry
function mrsh_hashtable_finish (line 97) | void mrsh_hashtable_finish(struct mrsh_hashtable *table) {
function mrsh_hashtable_for_each (line 108) | void mrsh_hashtable_for_each(struct mrsh_hashtable *table,
FILE: include/ast.h
type mrsh_array (line 6) | struct mrsh_array
type mrsh_case_item (line 7) | struct mrsh_case_item
FILE: include/builtin.h
type mrsh_state (line 6) | struct mrsh_state
type mrsh_state (line 8) | struct mrsh_state
type mrsh_state (line 13) | struct mrsh_state
type mrsh_state (line 14) | struct mrsh_state
type mrsh_state (line 15) | struct mrsh_state
type mrsh_state (line 16) | struct mrsh_state
type mrsh_state (line 17) | struct mrsh_state
type mrsh_state (line 18) | struct mrsh_state
type mrsh_state (line 19) | struct mrsh_state
type mrsh_state (line 20) | struct mrsh_state
type mrsh_state (line 21) | struct mrsh_state
type mrsh_state (line 22) | struct mrsh_state
type mrsh_state (line 23) | struct mrsh_state
type mrsh_state (line 24) | struct mrsh_state
type mrsh_state (line 25) | struct mrsh_state
type mrsh_state (line 26) | struct mrsh_state
type mrsh_state (line 27) | struct mrsh_state
type mrsh_state (line 28) | struct mrsh_state
type mrsh_state (line 29) | struct mrsh_state
type mrsh_state (line 30) | struct mrsh_state
type mrsh_state (line 31) | struct mrsh_state
type mrsh_state (line 32) | struct mrsh_state
type mrsh_state (line 33) | struct mrsh_state
type mrsh_state (line 34) | struct mrsh_state
type mrsh_state (line 35) | struct mrsh_state
type mrsh_state (line 36) | struct mrsh_state
type mrsh_state (line 37) | struct mrsh_state
type mrsh_state (line 38) | struct mrsh_state
type mrsh_state (line 39) | struct mrsh_state
type mrsh_state (line 40) | struct mrsh_state
type mrsh_state (line 41) | struct mrsh_state
type mrsh_state (line 42) | struct mrsh_state
type mrsh_state (line 44) | struct mrsh_state
type mrsh_state (line 46) | struct mrsh_state
type mrsh_collect_var (line 48) | struct mrsh_collect_var {
type mrsh_collect_var (line 55) | struct mrsh_collect_var
type mrsh_state (line 55) | struct mrsh_state
FILE: include/frontend.h
type mrsh_state (line 7) | struct mrsh_state
type mrsh_state (line 8) | struct mrsh_state
FILE: include/mrsh/arithm.h
type mrsh_arithm_expr_type (line 4) | enum mrsh_arithm_expr_type {
type mrsh_arithm_expr (line 22) | struct mrsh_arithm_expr {
type mrsh_arithm_literal (line 26) | struct mrsh_arithm_literal {
type mrsh_arithm_variable (line 31) | struct mrsh_arithm_variable {
type mrsh_arithm_unop_type (line 36) | enum mrsh_arithm_unop_type {
type mrsh_arithm_unop (line 43) | struct mrsh_arithm_unop {
type mrsh_arithm_binop_type (line 49) | enum mrsh_arithm_binop_type {
type mrsh_arithm_binop (line 70) | struct mrsh_arithm_binop {
type mrsh_arithm_cond (line 76) | struct mrsh_arithm_cond {
type mrsh_arithm_assign_op (line 81) | enum mrsh_arithm_assign_op {
type mrsh_arithm_assign (line 95) | struct mrsh_arithm_assign {
type mrsh_arithm_expr (line 102) | struct mrsh_arithm_expr
type mrsh_arithm_literal (line 103) | struct mrsh_arithm_literal
type mrsh_arithm_variable (line 104) | struct mrsh_arithm_variable
type mrsh_arithm_unop (line 105) | struct mrsh_arithm_unop
type mrsh_arithm_unop_type (line 106) | enum mrsh_arithm_unop_type
type mrsh_arithm_expr (line 106) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 107) | struct mrsh_arithm_binop
type mrsh_arithm_binop_type (line 108) | enum mrsh_arithm_binop_type
type mrsh_arithm_expr (line 108) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 109) | struct mrsh_arithm_expr
type mrsh_arithm_cond (line 110) | struct mrsh_arithm_cond
type mrsh_arithm_expr (line 111) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 111) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 112) | struct mrsh_arithm_expr
type mrsh_arithm_assign (line 113) | struct mrsh_arithm_assign
type mrsh_arithm_assign_op (line 114) | enum mrsh_arithm_assign_op
type mrsh_arithm_expr (line 115) | struct mrsh_arithm_expr
type mrsh_arithm_literal (line 116) | struct mrsh_arithm_literal
type mrsh_arithm_expr (line 117) | struct mrsh_arithm_expr
type mrsh_arithm_variable (line 118) | struct mrsh_arithm_variable
type mrsh_arithm_expr (line 119) | struct mrsh_arithm_expr
type mrsh_arithm_unop (line 120) | struct mrsh_arithm_unop
type mrsh_arithm_expr (line 121) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 122) | struct mrsh_arithm_binop
type mrsh_arithm_expr (line 123) | struct mrsh_arithm_expr
type mrsh_arithm_cond (line 124) | struct mrsh_arithm_cond
type mrsh_arithm_expr (line 125) | struct mrsh_arithm_expr
type mrsh_arithm_assign (line 126) | struct mrsh_arithm_assign
type mrsh_arithm_expr (line 127) | struct mrsh_arithm_expr
FILE: include/mrsh/array.h
type mrsh_array (line 8) | struct mrsh_array {
type mrsh_array (line 13) | struct mrsh_array
type mrsh_array (line 14) | struct mrsh_array
type mrsh_array (line 15) | struct mrsh_array
FILE: include/mrsh/ast.h
type mrsh_position (line 11) | struct mrsh_position {
type mrsh_range (line 21) | struct mrsh_range {
type mrsh_node_type (line 25) | enum mrsh_node_type {
type mrsh_node (line 33) | struct mrsh_node {
type mrsh_word_type (line 37) | enum mrsh_word_type {
type mrsh_word (line 53) | struct mrsh_word {
type mrsh_word_string (line 61) | struct mrsh_word_string {
type mrsh_word_parameter_op (line 73) | enum mrsh_word_parameter_op {
type mrsh_word_parameter (line 90) | struct mrsh_word_parameter {
type mrsh_word_command (line 107) | struct mrsh_word_command {
type mrsh_word_arithmetic (line 119) | struct mrsh_word_arithmetic {
type mrsh_word_list (line 130) | struct mrsh_word_list {
type mrsh_io_redirect_op (line 138) | enum mrsh_io_redirect_op {
type mrsh_io_redirect (line 153) | struct mrsh_io_redirect {
type mrsh_assignment (line 166) | struct mrsh_assignment {
type mrsh_command_type (line 174) | enum mrsh_command_type {
type mrsh_command (line 188) | struct mrsh_command {
type mrsh_simple_command (line 198) | struct mrsh_simple_command {
type mrsh_brace_group (line 211) | struct mrsh_brace_group {
type mrsh_subshell (line 222) | struct mrsh_subshell {
type mrsh_if_clause (line 242) | struct mrsh_if_clause {
type mrsh_for_clause (line 261) | struct mrsh_for_clause {
type mrsh_loop_type (line 272) | enum mrsh_loop_type {
type mrsh_loop_clause (line 285) | struct mrsh_loop_clause {
type mrsh_case_item (line 302) | struct mrsh_case_item {
type mrsh_case_clause (line 321) | struct mrsh_case_clause {
type mrsh_function_definition (line 334) | struct mrsh_function_definition {
type mrsh_and_or_list_type (line 344) | enum mrsh_and_or_list_type {
type mrsh_and_or_list (line 352) | struct mrsh_and_or_list {
type mrsh_pipeline (line 361) | struct mrsh_pipeline {
type mrsh_binop_type (line 370) | enum mrsh_binop_type {
type mrsh_binop (line 379) | struct mrsh_binop {
type mrsh_command_list (line 391) | struct mrsh_command_list {
type mrsh_program (line 402) | struct mrsh_program {
type mrsh_node (line 407) | struct mrsh_node
type mrsh_position (line 410) | struct mrsh_position
type mrsh_range (line 411) | struct mrsh_range
type mrsh_word_string (line 413) | struct mrsh_word_string
type mrsh_word_parameter (line 415) | struct mrsh_word_parameter
type mrsh_word_parameter_op (line 416) | enum mrsh_word_parameter_op
type mrsh_word (line 416) | struct mrsh_word
type mrsh_word_command (line 417) | struct mrsh_word_command
type mrsh_program (line 417) | struct mrsh_program
type mrsh_word_arithmetic (line 419) | struct mrsh_word_arithmetic
type mrsh_word (line 420) | struct mrsh_word
type mrsh_word_list (line 421) | struct mrsh_word_list
type mrsh_array (line 421) | struct mrsh_array
type mrsh_simple_command (line 423) | struct mrsh_simple_command
type mrsh_word (line 423) | struct mrsh_word
type mrsh_array (line 424) | struct mrsh_array
type mrsh_array (line 424) | struct mrsh_array
type mrsh_array (line 425) | struct mrsh_array
type mrsh_brace_group (line 426) | struct mrsh_brace_group
type mrsh_array (line 426) | struct mrsh_array
type mrsh_subshell (line 427) | struct mrsh_subshell
type mrsh_array (line 427) | struct mrsh_array
type mrsh_if_clause (line 428) | struct mrsh_if_clause
type mrsh_array (line 428) | struct mrsh_array
type mrsh_array (line 429) | struct mrsh_array
type mrsh_command (line 429) | struct mrsh_command
type mrsh_for_clause (line 430) | struct mrsh_for_clause
type mrsh_array (line 431) | struct mrsh_array
type mrsh_array (line 431) | struct mrsh_array
type mrsh_loop_clause (line 432) | struct mrsh_loop_clause
type mrsh_loop_type (line 432) | enum mrsh_loop_type
type mrsh_array (line 433) | struct mrsh_array
type mrsh_array (line 433) | struct mrsh_array
type mrsh_case_clause (line 434) | struct mrsh_case_clause
type mrsh_word (line 434) | struct mrsh_word
type mrsh_array (line 435) | struct mrsh_array
type mrsh_function_definition (line 436) | struct mrsh_function_definition
type mrsh_command (line 437) | struct mrsh_command
type mrsh_array (line 437) | struct mrsh_array
type mrsh_pipeline (line 438) | struct mrsh_pipeline
type mrsh_array (line 438) | struct mrsh_array
type mrsh_binop (line 440) | struct mrsh_binop
type mrsh_binop_type (line 440) | enum mrsh_binop_type
type mrsh_and_or_list (line 441) | struct mrsh_and_or_list
type mrsh_and_or_list (line 441) | struct mrsh_and_or_list
type mrsh_command_list (line 442) | struct mrsh_command_list
type mrsh_program (line 443) | struct mrsh_program
type mrsh_node (line 445) | struct mrsh_node
type mrsh_word (line 446) | struct mrsh_word
type mrsh_io_redirect (line 447) | struct mrsh_io_redirect
type mrsh_assignment (line 448) | struct mrsh_assignment
type mrsh_command (line 449) | struct mrsh_command
type mrsh_and_or_list (line 450) | struct mrsh_and_or_list
type mrsh_command_list (line 451) | struct mrsh_command_list
type mrsh_program (line 452) | struct mrsh_program
type mrsh_word (line 454) | struct mrsh_word
type mrsh_node (line 454) | struct mrsh_node
type mrsh_command (line 455) | struct mrsh_command
type mrsh_node (line 455) | struct mrsh_node
type mrsh_and_or_list (line 456) | struct mrsh_and_or_list
type mrsh_node (line 457) | struct mrsh_node
type mrsh_command_list (line 458) | struct mrsh_command_list
type mrsh_node (line 459) | struct mrsh_node
type mrsh_program (line 460) | struct mrsh_program
type mrsh_node (line 460) | struct mrsh_node
type mrsh_word_string (line 462) | struct mrsh_word_string
type mrsh_word (line 462) | struct mrsh_word
type mrsh_word_parameter (line 463) | struct mrsh_word_parameter
type mrsh_word (line 464) | struct mrsh_word
type mrsh_word_command (line 465) | struct mrsh_word_command
type mrsh_word (line 465) | struct mrsh_word
type mrsh_word_arithmetic (line 466) | struct mrsh_word_arithmetic
type mrsh_word (line 467) | struct mrsh_word
type mrsh_word_list (line 468) | struct mrsh_word_list
type mrsh_word (line 468) | struct mrsh_word
type mrsh_simple_command (line 470) | struct mrsh_simple_command
type mrsh_command (line 471) | struct mrsh_command
type mrsh_brace_group (line 472) | struct mrsh_brace_group
type mrsh_command (line 473) | struct mrsh_command
type mrsh_subshell (line 474) | struct mrsh_subshell
type mrsh_command (line 475) | struct mrsh_command
type mrsh_if_clause (line 476) | struct mrsh_if_clause
type mrsh_command (line 477) | struct mrsh_command
type mrsh_for_clause (line 478) | struct mrsh_for_clause
type mrsh_command (line 479) | struct mrsh_command
type mrsh_loop_clause (line 480) | struct mrsh_loop_clause
type mrsh_command (line 481) | struct mrsh_command
type mrsh_case_clause (line 482) | struct mrsh_case_clause
type mrsh_command (line 483) | struct mrsh_command
type mrsh_function_definition (line 484) | struct mrsh_function_definition
type mrsh_command (line 485) | struct mrsh_command
type mrsh_pipeline (line 487) | struct mrsh_pipeline
type mrsh_and_or_list (line 488) | struct mrsh_and_or_list
type mrsh_binop (line 489) | struct mrsh_binop
type mrsh_and_or_list (line 490) | struct mrsh_and_or_list
type mrsh_node (line 492) | struct mrsh_node
type mrsh_word (line 495) | struct mrsh_word
type mrsh_position (line 495) | struct mrsh_position
type mrsh_position (line 496) | struct mrsh_position
type mrsh_command (line 497) | struct mrsh_command
type mrsh_position (line 497) | struct mrsh_position
type mrsh_position (line 498) | struct mrsh_position
type mrsh_word (line 499) | struct mrsh_word
type mrsh_node (line 500) | struct mrsh_node
type mrsh_program (line 501) | struct mrsh_program
type mrsh_node (line 503) | struct mrsh_node
type mrsh_node (line 503) | struct mrsh_node
type mrsh_word (line 504) | struct mrsh_word
type mrsh_word (line 504) | struct mrsh_word
type mrsh_io_redirect (line 505) | struct mrsh_io_redirect
type mrsh_io_redirect (line 506) | struct mrsh_io_redirect
type mrsh_assignment (line 507) | struct mrsh_assignment
type mrsh_assignment (line 508) | struct mrsh_assignment
type mrsh_command (line 509) | struct mrsh_command
type mrsh_command (line 509) | struct mrsh_command
type mrsh_and_or_list (line 510) | struct mrsh_and_or_list
type mrsh_and_or_list (line 511) | struct mrsh_and_or_list
type mrsh_command_list (line 512) | struct mrsh_command_list
type mrsh_command_list (line 513) | struct mrsh_command_list
type mrsh_program (line 514) | struct mrsh_program
type mrsh_program (line 514) | struct mrsh_program
FILE: include/mrsh/buffer.h
type mrsh_buffer (line 7) | struct mrsh_buffer {
type mrsh_buffer (line 24) | struct mrsh_buffer
type mrsh_buffer (line 29) | struct mrsh_buffer
type mrsh_buffer (line 30) | struct mrsh_buffer
type mrsh_buffer (line 31) | struct mrsh_buffer
type mrsh_buffer (line 35) | struct mrsh_buffer
type mrsh_buffer (line 36) | struct mrsh_buffer
FILE: include/mrsh/builtin.h
type mrsh_state (line 8) | struct mrsh_state
type mrsh_init_args (line 10) | struct mrsh_init_args {
type mrsh_state (line 15) | struct mrsh_state
type mrsh_init_args (line 15) | struct mrsh_init_args
FILE: include/mrsh/entry.h
type mrsh_state (line 11) | struct mrsh_state
type mrsh_state (line 17) | struct mrsh_state
type mrsh_state (line 23) | struct mrsh_state
type mrsh_state (line 29) | struct mrsh_state
type mrsh_state (line 36) | struct mrsh_state
type mrsh_state (line 39) | struct mrsh_state
type mrsh_state (line 45) | struct mrsh_state
FILE: include/mrsh/hashtable.h
type mrsh_hashtable_entry (line 6) | struct mrsh_hashtable_entry {
type mrsh_hashtable (line 13) | struct mrsh_hashtable {
type mrsh_hashtable (line 20) | struct mrsh_hashtable
type mrsh_hashtable (line 21) | struct mrsh_hashtable
type mrsh_hashtable (line 22) | struct mrsh_hashtable
type mrsh_hashtable (line 24) | struct mrsh_hashtable
type mrsh_hashtable (line 30) | struct mrsh_hashtable
FILE: include/mrsh/parser.h
type mrsh_parser (line 7) | struct mrsh_parser
type mrsh_buffer (line 8) | struct mrsh_buffer
type mrsh_parser (line 20) | struct mrsh_parser
type mrsh_parser (line 24) | struct mrsh_parser
type mrsh_parser (line 29) | struct mrsh_parser
type mrsh_buffer (line 29) | struct mrsh_buffer
type mrsh_parser (line 30) | struct mrsh_parser
type mrsh_program (line 34) | struct mrsh_program
type mrsh_parser (line 34) | struct mrsh_parser
type mrsh_program (line 38) | struct mrsh_program
type mrsh_parser (line 38) | struct mrsh_parser
type mrsh_arithm_expr (line 43) | struct mrsh_arithm_expr
type mrsh_parser (line 43) | struct mrsh_parser
type mrsh_parser (line 47) | struct mrsh_parser
type mrsh_parser (line 51) | struct mrsh_parser
type mrsh_parser (line 57) | struct mrsh_parser
type mrsh_position (line 58) | struct mrsh_position
type mrsh_parser (line 62) | struct mrsh_parser
type mrsh_parser (line 66) | struct mrsh_parser
FILE: include/mrsh/shell.h
type mrsh_option (line 10) | enum mrsh_option {
type mrsh_variable_attrib (line 60) | enum mrsh_variable_attrib {
type mrsh_call_frame (line 66) | struct mrsh_call_frame {
type mrsh_state (line 72) | struct mrsh_state {
type mrsh_parser (line 80) | struct mrsh_parser
type mrsh_state (line 82) | struct mrsh_state
type mrsh_state (line 83) | struct mrsh_state
type mrsh_state (line 85) | struct mrsh_state
type mrsh_parser (line 85) | struct mrsh_parser
type mrsh_state (line 86) | struct mrsh_state
type mrsh_state (line 88) | struct mrsh_state
type mrsh_state (line 89) | struct mrsh_state
type mrsh_state (line 91) | struct mrsh_state
type mrsh_program (line 91) | struct mrsh_program
type mrsh_state (line 92) | struct mrsh_state
type mrsh_word (line 92) | struct mrsh_word
type mrsh_state (line 93) | struct mrsh_state
type mrsh_arithm_expr (line 94) | struct mrsh_arithm_expr
type mrsh_state (line 99) | struct mrsh_state
type mrsh_state (line 104) | struct mrsh_state
FILE: include/parser.h
type symbol_name (line 8) | enum symbol_name {
type symbol (line 31) | struct symbol {
type symbol (line 36) | struct symbol
type mrsh_parser (line 43) | struct mrsh_parser {
type mrsh_word (line 68) | struct mrsh_word
type mrsh_parser (line 68) | struct mrsh_parser
type mrsh_parser (line 70) | struct mrsh_parser
type mrsh_parser (line 71) | struct mrsh_parser
type mrsh_parser (line 72) | struct mrsh_parser
type mrsh_parser (line 73) | struct mrsh_parser
type mrsh_parser (line 74) | struct mrsh_parser
type mrsh_range (line 75) | struct mrsh_range
type mrsh_parser (line 76) | struct mrsh_parser
type mrsh_range (line 77) | struct mrsh_range
type mrsh_parser (line 78) | struct mrsh_parser
type mrsh_range (line 79) | struct mrsh_range
type mrsh_parser (line 80) | struct mrsh_parser
type mrsh_parser (line 81) | struct mrsh_parser
type mrsh_parser (line 82) | struct mrsh_parser
type symbol_name (line 84) | enum symbol_name
type mrsh_parser (line 84) | struct mrsh_parser
type mrsh_parser (line 89) | struct mrsh_parser
type mrsh_parser (line 90) | struct mrsh_parser
type symbol_name (line 90) | enum symbol_name
type mrsh_parser (line 91) | struct mrsh_parser
type mrsh_parser (line 92) | struct mrsh_parser
type mrsh_parser (line 93) | struct mrsh_parser
type mrsh_parser (line 94) | struct mrsh_parser
type mrsh_parser (line 96) | struct mrsh_parser
type mrsh_parser (line 97) | struct mrsh_parser
type mrsh_word (line 98) | struct mrsh_word
type mrsh_parser (line 98) | struct mrsh_parser
type mrsh_word (line 99) | struct mrsh_word
type mrsh_parser (line 99) | struct mrsh_parser
type mrsh_word (line 100) | struct mrsh_word
type mrsh_parser (line 100) | struct mrsh_parser
type mrsh_word (line 101) | struct mrsh_word
type mrsh_parser (line 101) | struct mrsh_parser
type mrsh_word (line 102) | struct mrsh_word
type mrsh_parser (line 102) | struct mrsh_parser
FILE: include/shell/job.h
type mrsh_node (line 9) | struct mrsh_node
type mrsh_state (line 10) | struct mrsh_state
type mrsh_process (line 11) | struct mrsh_process
type mrsh_job (line 24) | struct mrsh_job {
type mrsh_job (line 39) | struct mrsh_job
type mrsh_state (line 39) | struct mrsh_state
type mrsh_node (line 40) | struct mrsh_node
type mrsh_job (line 41) | struct mrsh_job
type mrsh_job (line 50) | struct mrsh_job
type mrsh_process (line 50) | struct mrsh_process
type mrsh_job (line 60) | struct mrsh_job
type mrsh_job (line 64) | struct mrsh_job
type mrsh_process (line 68) | struct mrsh_process
type mrsh_job (line 76) | struct mrsh_job
type mrsh_state (line 81) | struct mrsh_state
type mrsh_state (line 85) | struct mrsh_state
type mrsh_job (line 92) | struct mrsh_job
type mrsh_state (line 92) | struct mrsh_state
type mrsh_job (line 97) | struct mrsh_job
type mrsh_state (line 101) | struct mrsh_state
FILE: include/shell/path.h
type mrsh_state (line 13) | struct mrsh_state
FILE: include/shell/process.h
type mrsh_process (line 15) | struct mrsh_process {
type mrsh_process (line 27) | struct mrsh_process
type mrsh_state (line 27) | struct mrsh_state
type mrsh_process (line 28) | struct mrsh_process
type mrsh_process (line 35) | struct mrsh_process
type mrsh_state (line 40) | struct mrsh_state
FILE: include/shell/redir.h
type mrsh_io_redirect (line 6) | struct mrsh_io_redirect
FILE: include/shell/shell.h
type mrsh_variable (line 10) | struct mrsh_variable {
type mrsh_function (line 15) | struct mrsh_function {
type mrsh_branch_control (line 19) | enum mrsh_branch_control {
type mrsh_call_frame_priv (line 26) | struct mrsh_call_frame_priv {
type mrsh_trap (line 33) | struct mrsh_trap {
type mrsh_state_priv (line 39) | struct mrsh_state_priv {
type mrsh_context (line 64) | struct mrsh_context {
type mrsh_function (line 73) | struct mrsh_function
type mrsh_call_frame_priv (line 75) | struct mrsh_call_frame_priv
type mrsh_call_frame (line 75) | struct mrsh_call_frame
type mrsh_state_priv (line 77) | struct mrsh_state_priv
type mrsh_state (line 77) | struct mrsh_state
type mrsh_state (line 78) | struct mrsh_state
type mrsh_state (line 79) | struct mrsh_state
FILE: include/shell/task.h
type mrsh_context (line 24) | struct mrsh_context
type mrsh_context (line 27) | struct mrsh_context
type mrsh_word (line 27) | struct mrsh_word
type mrsh_context (line 30) | struct mrsh_context
type mrsh_word (line 30) | struct mrsh_word
type mrsh_array (line 31) | struct mrsh_array
type mrsh_context (line 32) | struct mrsh_context
type mrsh_simple_command (line 32) | struct mrsh_simple_command
type mrsh_context (line 33) | struct mrsh_context
type mrsh_command (line 33) | struct mrsh_command
type mrsh_context (line 34) | struct mrsh_context
type mrsh_and_or_list (line 34) | struct mrsh_and_or_list
type mrsh_context (line 35) | struct mrsh_context
type mrsh_pipeline (line 35) | struct mrsh_pipeline
type mrsh_context (line 36) | struct mrsh_context
type mrsh_array (line 36) | struct mrsh_array
FILE: include/shell/trap.h
type mrsh_state (line 6) | struct mrsh_state
type mrsh_program (line 7) | struct mrsh_program
type mrsh_trap_action (line 12) | enum mrsh_trap_action {
type mrsh_state (line 18) | struct mrsh_state
type mrsh_trap_action (line 18) | enum mrsh_trap_action
type mrsh_program (line 19) | struct mrsh_program
type mrsh_state (line 20) | struct mrsh_state
type mrsh_state (line 21) | struct mrsh_state
type mrsh_state (line 22) | struct mrsh_state
type mrsh_state (line 23) | struct mrsh_state
FILE: include/shell/word.h
type mrsh_state (line 9) | struct mrsh_state
type mrsh_word (line 9) | struct mrsh_word
type mrsh_array (line 15) | struct mrsh_array
type mrsh_word (line 15) | struct mrsh_word
type mrsh_array (line 17) | struct mrsh_array
type mrsh_array (line 17) | struct mrsh_array
type mrsh_word (line 22) | struct mrsh_word
type mrsh_array (line 26) | struct mrsh_array
type mrsh_array (line 27) | struct mrsh_array
FILE: main.c
function main (line 19) | int main(int argc, char *argv[]) {
FILE: parser/arithm.c
function parse_char (line 9) | static bool parse_char(struct mrsh_parser *parser, char c) {
function parse_whitespace (line 17) | static bool parse_whitespace(struct mrsh_parser *parser) {
function consume_whitespace (line 25) | static inline void consume_whitespace(struct mrsh_parser *parser) {
function expect_char (line 31) | static bool expect_char(struct mrsh_parser *parser, char c) {
function parse_str (line 41) | static bool parse_str(struct mrsh_parser *parser, const char *str) {
function peek_literal (line 56) | static size_t peek_literal(struct mrsh_parser *parser) {
type mrsh_arithm_literal (line 73) | struct mrsh_arithm_literal
type mrsh_parser (line 73) | struct mrsh_parser
type mrsh_arithm_variable (line 95) | struct mrsh_arithm_variable
type mrsh_parser (line 95) | struct mrsh_parser
type mrsh_arithm_expr (line 108) | struct mrsh_arithm_expr
type mrsh_parser (line 108) | struct mrsh_parser
type mrsh_arithm_unop (line 110) | struct mrsh_arithm_unop
type mrsh_parser (line 110) | struct mrsh_parser
type mrsh_arithm_unop_type (line 111) | enum mrsh_arithm_unop_type
type mrsh_arithm_expr (line 130) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 140) | struct mrsh_arithm_expr
type mrsh_parser (line 140) | struct mrsh_parser
type mrsh_arithm_expr (line 146) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 158) | struct mrsh_arithm_expr
type mrsh_parser (line 158) | struct mrsh_parser
type mrsh_arithm_expr (line 159) | struct mrsh_arithm_expr
type mrsh_arithm_unop (line 164) | struct mrsh_arithm_unop
type mrsh_arithm_literal (line 169) | struct mrsh_arithm_literal
type mrsh_arithm_variable (line 174) | struct mrsh_arithm_variable
type mrsh_arithm_expr (line 182) | struct mrsh_arithm_expr
type mrsh_parser (line 182) | struct mrsh_parser
type mrsh_arithm_expr (line 183) | struct mrsh_arithm_expr
type mrsh_arithm_binop_type (line 191) | enum mrsh_arithm_binop_type
type mrsh_arithm_expr (line 205) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 211) | struct mrsh_arithm_binop
type mrsh_arithm_expr (line 217) | struct mrsh_arithm_expr
type mrsh_parser (line 217) | struct mrsh_parser
type mrsh_arithm_expr (line 218) | struct mrsh_arithm_expr
type mrsh_arithm_binop_type (line 227) | enum mrsh_arithm_binop_type
type mrsh_arithm_expr (line 239) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 245) | struct mrsh_arithm_binop
type mrsh_arithm_expr (line 251) | struct mrsh_arithm_expr
type mrsh_parser (line 251) | struct mrsh_parser
type mrsh_arithm_expr (line 252) | struct mrsh_arithm_expr
type mrsh_arithm_binop_type (line 259) | enum mrsh_arithm_binop_type
type mrsh_arithm_expr (line 269) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 276) | struct mrsh_arithm_binop
type mrsh_arithm_expr (line 280) | struct mrsh_arithm_expr
type mrsh_parser (line 280) | struct mrsh_parser
type mrsh_arithm_expr (line 281) | struct mrsh_arithm_expr
type mrsh_arithm_binop_type (line 286) | enum mrsh_arithm_binop_type
type mrsh_arithm_expr (line 300) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 307) | struct mrsh_arithm_binop
type mrsh_arithm_expr (line 311) | struct mrsh_arithm_expr
type mrsh_parser (line 311) | struct mrsh_parser
type mrsh_arithm_expr (line 312) | struct mrsh_arithm_expr
type mrsh_arithm_binop_type (line 317) | enum mrsh_arithm_binop_type
type mrsh_arithm_expr (line 326) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 333) | struct mrsh_arithm_binop
function parse_binop (line 337) | static bool parse_binop(struct mrsh_parser *parser, const char *str) {
type mrsh_arithm_expr (line 360) | struct mrsh_arithm_expr
type mrsh_parser (line 360) | struct mrsh_parser
type mrsh_arithm_binop_type (line 361) | enum mrsh_arithm_binop_type
type mrsh_arithm_expr (line 362) | struct mrsh_arithm_expr
type mrsh_parser (line 362) | struct mrsh_parser
type mrsh_arithm_expr (line 363) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 372) | struct mrsh_arithm_expr
type mrsh_arithm_binop (line 379) | struct mrsh_arithm_binop
type mrsh_arithm_expr (line 383) | struct mrsh_arithm_expr
type mrsh_parser (line 383) | struct mrsh_parser
type mrsh_arithm_expr (line 387) | struct mrsh_arithm_expr
type mrsh_parser (line 387) | struct mrsh_parser
type mrsh_arithm_expr (line 391) | struct mrsh_arithm_expr
type mrsh_parser (line 391) | struct mrsh_parser
type mrsh_arithm_expr (line 395) | struct mrsh_arithm_expr
type mrsh_parser (line 395) | struct mrsh_parser
type mrsh_arithm_expr (line 399) | struct mrsh_arithm_expr
type mrsh_parser (line 399) | struct mrsh_parser
type mrsh_arithm_expr (line 403) | struct mrsh_arithm_expr
type mrsh_parser (line 403) | struct mrsh_parser
type mrsh_arithm_expr (line 404) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 411) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 413) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 423) | struct mrsh_arithm_expr
type mrsh_arithm_cond (line 429) | struct mrsh_arithm_cond
function peek_assign_op (line 441) | static bool peek_assign_op(struct mrsh_parser *parser, size_t *offset,
type mrsh_arithm_expr (line 457) | struct mrsh_arithm_expr
type mrsh_parser (line 457) | struct mrsh_parser
type mrsh_arithm_assign_op (line 463) | enum mrsh_arithm_assign_op
type mrsh_arithm_expr (line 498) | struct mrsh_arithm_expr
type mrsh_arithm_assign (line 505) | struct mrsh_arithm_assign
type mrsh_arithm_expr (line 509) | struct mrsh_arithm_expr
type mrsh_parser (line 509) | struct mrsh_parser
type mrsh_arithm_expr (line 510) | struct mrsh_arithm_expr
type mrsh_arithm_expr (line 518) | struct mrsh_arithm_expr
type mrsh_parser (line 518) | struct mrsh_parser
type mrsh_arithm_expr (line 521) | struct mrsh_arithm_expr
FILE: parser/parser.c
type symbol (line 17) | struct symbol
type mrsh_parser (line 56) | struct mrsh_parser
type mrsh_parser (line 57) | struct mrsh_parser
type mrsh_parser (line 57) | struct mrsh_parser
type mrsh_parser (line 63) | struct mrsh_parser
type mrsh_parser (line 64) | struct mrsh_parser
type mrsh_parser (line 69) | struct mrsh_parser
type mrsh_parser (line 70) | struct mrsh_parser
type mrsh_parser (line 76) | struct mrsh_parser
type mrsh_buffer (line 76) | struct mrsh_buffer
type mrsh_parser (line 77) | struct mrsh_parser
function mrsh_parser_destroy (line 82) | void mrsh_parser_destroy(struct mrsh_parser *parser) {
function parser_peek_fd (line 92) | static ssize_t parser_peek_fd(struct mrsh_parser *parser, size_t size) {
function parser_peek_buffer (line 116) | static ssize_t parser_peek_buffer(struct mrsh_parser *parser, size_t siz...
function parser_peek (line 137) | size_t parser_peek(struct mrsh_parser *parser, char *buf, size_t size) {
function parser_peek_char (line 170) | char parser_peek_char(struct mrsh_parser *parser) {
function parser_read (line 176) | size_t parser_read(struct mrsh_parser *parser, char *buf, size_t size) {
function parser_read_char (line 197) | char parser_read_char(struct mrsh_parser *parser) {
function read_continuation_line (line 203) | void read_continuation_line(struct mrsh_parser *parser) {
function is_operator_start (line 209) | bool is_operator_start(char c) {
function parser_set_error (line 222) | void parser_set_error(struct mrsh_parser *parser, const char *msg) {
type mrsh_parser (line 239) | struct mrsh_parser
type mrsh_position (line 240) | struct mrsh_position
function parser_begin (line 247) | void parser_begin(struct mrsh_parser *parser) {
function next_symbol (line 253) | static void next_symbol(struct mrsh_parser *parser) {
function get_symbol (line 308) | enum symbol_name get_symbol(struct mrsh_parser *parser) {
function consume_symbol (line 315) | void consume_symbol(struct mrsh_parser *parser) {
function symbol (line 319) | bool symbol(struct mrsh_parser *parser, enum symbol_name sym) {
function eof (line 323) | bool eof(struct mrsh_parser *parser) {
function newline (line 327) | bool newline(struct mrsh_parser *parser) {
function linebreak (line 337) | void linebreak(struct mrsh_parser *parser) {
function newline_list (line 343) | bool newline_list(struct mrsh_parser *parser) {
function mrsh_parser_eof (line 352) | bool mrsh_parser_eof(struct mrsh_parser *parser) {
function mrsh_parser_set_alias_func (line 356) | void mrsh_parser_set_alias_func(struct mrsh_parser *parser,
function mrsh_parser_continuation_line (line 362) | bool mrsh_parser_continuation_line(struct mrsh_parser *parser) {
function mrsh_parser_reset (line 366) | void mrsh_parser_reset(struct mrsh_parser *parser) {
FILE: parser/program.c
type symbol_name (line 14) | enum symbol_name
function operator (line 23) | static bool operator(struct mrsh_parser *parser, enum symbol_name sym,
function separator_op (line 47) | static int separator_op(struct mrsh_parser *parser) {
function peek_alias (line 57) | static size_t peek_alias(struct mrsh_parser *parser) {
function apply_aliases (line 79) | static void apply_aliases(struct mrsh_parser *parser) {
function io_here (line 120) | static bool io_here(struct mrsh_parser *parser, struct mrsh_io_redirect ...
type mrsh_word (line 140) | struct mrsh_word
type mrsh_parser (line 140) | struct mrsh_parser
function io_redirect_op (line 145) | static int io_redirect_op(struct mrsh_parser *parser, struct mrsh_range ...
function io_file (line 165) | static bool io_file(struct mrsh_parser *parser,
function io_number (line 183) | static int io_number(struct mrsh_parser *parser) {
type mrsh_io_redirect (line 204) | struct mrsh_io_redirect
type mrsh_parser (line 204) | struct mrsh_parser
type mrsh_io_redirect (line 205) | struct mrsh_io_redirect
type mrsh_position (line 207) | struct mrsh_position
type mrsh_io_redirect (line 215) | struct mrsh_io_redirect
type mrsh_io_redirect (line 216) | struct mrsh_io_redirect
type mrsh_io_redirect (line 217) | struct mrsh_io_redirect
type mrsh_io_redirect (line 222) | struct mrsh_io_redirect
type mrsh_io_redirect (line 223) | struct mrsh_io_redirect
type mrsh_io_redirect (line 224) | struct mrsh_io_redirect
type mrsh_assignment (line 236) | struct mrsh_assignment
type mrsh_parser (line 236) | struct mrsh_parser
type mrsh_range (line 251) | struct mrsh_range
type mrsh_position (line 254) | struct mrsh_position
type mrsh_word (line 257) | struct mrsh_word
type mrsh_assignment (line 262) | struct mrsh_assignment
type mrsh_assignment (line 262) | struct mrsh_assignment
function cmd_prefix (line 270) | static bool cmd_prefix(struct mrsh_parser *parser,
type mrsh_word (line 287) | struct mrsh_word
type mrsh_parser (line 287) | struct mrsh_parser
type mrsh_range (line 303) | struct mrsh_range
type mrsh_word_string (line 306) | struct mrsh_word_string
function cmd_suffix (line 311) | static bool cmd_suffix(struct mrsh_parser *parser,
type mrsh_simple_command (line 328) | struct mrsh_simple_command
type mrsh_parser (line 328) | struct mrsh_parser
type mrsh_simple_command (line 329) | struct mrsh_simple_command
function separator (line 349) | static int separator(struct mrsh_parser *parser) {
type mrsh_and_or_list (line 363) | struct mrsh_and_or_list
type mrsh_parser (line 363) | struct mrsh_parser
type mrsh_parser (line 365) | struct mrsh_parser
type mrsh_io_redirect (line 366) | struct mrsh_io_redirect
type mrsh_command_list (line 368) | struct mrsh_command_list
type mrsh_parser (line 368) | struct mrsh_parser
type mrsh_and_or_list (line 369) | struct mrsh_and_or_list
type mrsh_command_list (line 374) | struct mrsh_command_list
type mrsh_position (line 377) | struct mrsh_position
type mrsh_io_redirect (line 388) | struct mrsh_io_redirect
function compound_list (line 404) | static bool compound_list(struct mrsh_parser *parser, struct mrsh_array ...
function expect_compound_list (line 424) | static bool expect_compound_list(struct mrsh_parser *parser,
type mrsh_brace_group (line 434) | struct mrsh_brace_group
type mrsh_parser (line 434) | struct mrsh_parser
type mrsh_position (line 435) | struct mrsh_position
type mrsh_array (line 440) | struct mrsh_array
type mrsh_position (line 445) | struct mrsh_position
type mrsh_brace_group (line 451) | struct mrsh_brace_group
type mrsh_subshell (line 457) | struct mrsh_subshell
type mrsh_parser (line 457) | struct mrsh_parser
type mrsh_position (line 458) | struct mrsh_position
type mrsh_array (line 463) | struct mrsh_array
type mrsh_position (line 468) | struct mrsh_position
type mrsh_subshell (line 474) | struct mrsh_subshell
type mrsh_command (line 480) | struct mrsh_command
type mrsh_parser (line 480) | struct mrsh_parser
type mrsh_range (line 481) | struct mrsh_range
type mrsh_array (line 483) | struct mrsh_array
type mrsh_range (line 488) | struct mrsh_range
type mrsh_array (line 494) | struct mrsh_array
type mrsh_command (line 500) | struct mrsh_command
type mrsh_if_clause (line 502) | struct mrsh_if_clause
type mrsh_array (line 509) | struct mrsh_array
type mrsh_brace_group (line 515) | struct mrsh_brace_group
type mrsh_if_clause (line 522) | struct mrsh_if_clause
type mrsh_parser (line 522) | struct mrsh_parser
type mrsh_range (line 523) | struct mrsh_range
type mrsh_array (line 528) | struct mrsh_array
type mrsh_range (line 533) | struct mrsh_range
type mrsh_array (line 538) | struct mrsh_array
type mrsh_command (line 543) | struct mrsh_command
type mrsh_range (line 545) | struct mrsh_range
type mrsh_if_clause (line 550) | struct mrsh_if_clause
function sequential_sep (line 565) | static bool sequential_sep(struct mrsh_parser *parser) {
function wordlist (line 573) | static void wordlist(struct mrsh_parser *parser,
function expect_do_group (line 584) | static bool expect_do_group(struct mrsh_parser *parser,
type mrsh_for_clause (line 605) | struct mrsh_for_clause
type mrsh_parser (line 605) | struct mrsh_parser
type mrsh_range (line 606) | struct mrsh_range
type mrsh_range (line 617) | struct mrsh_range
type mrsh_range (line 622) | struct mrsh_range
type mrsh_array (line 626) | struct mrsh_array
type mrsh_array (line 638) | struct mrsh_array
type mrsh_range (line 639) | struct mrsh_range
type mrsh_for_clause (line 644) | struct mrsh_for_clause
type mrsh_word (line 655) | struct mrsh_word
type mrsh_loop_clause (line 663) | struct mrsh_loop_clause
type mrsh_parser (line 663) | struct mrsh_parser
type mrsh_loop_type (line 664) | enum mrsh_loop_type
type mrsh_range (line 665) | struct mrsh_range
type mrsh_array (line 674) | struct mrsh_array
type mrsh_array (line 679) | struct mrsh_array
type mrsh_range (line 680) | struct mrsh_range
type mrsh_loop_clause (line 686) | struct mrsh_loop_clause
type mrsh_case_item (line 694) | struct mrsh_case_item
type mrsh_parser (line 694) | struct mrsh_parser
type mrsh_position (line 696) | struct mrsh_position
type mrsh_position (line 698) | struct mrsh_position
type mrsh_word (line 701) | struct mrsh_word
type mrsh_array (line 707) | struct mrsh_array
type mrsh_word (line 711) | struct mrsh_word
type mrsh_position (line 719) | struct mrsh_position
type mrsh_array (line 725) | struct mrsh_array
type mrsh_range (line 731) | struct mrsh_range
type mrsh_case_item (line 737) | struct mrsh_case_item
type mrsh_case_item (line 737) | struct mrsh_case_item
type mrsh_word (line 752) | struct mrsh_word
type mrsh_case_clause (line 759) | struct mrsh_case_clause
type mrsh_parser (line 759) | struct mrsh_parser
type mrsh_range (line 760) | struct mrsh_range
type mrsh_word (line 765) | struct mrsh_word
type mrsh_range (line 773) | struct mrsh_range
type mrsh_array (line 781) | struct mrsh_array
type mrsh_range (line 782) | struct mrsh_range
type mrsh_case_item (line 784) | struct mrsh_case_item
type mrsh_case_clause (line 799) | struct mrsh_case_clause
type mrsh_case_item (line 807) | struct mrsh_case_item
type mrsh_command (line 816) | struct mrsh_command
type mrsh_parser (line 816) | struct mrsh_parser
type mrsh_function_definition (line 818) | struct mrsh_function_definition
type mrsh_parser (line 819) | struct mrsh_parser
type mrsh_range (line 839) | struct mrsh_range
type mrsh_position (line 842) | struct mrsh_position
type mrsh_position (line 847) | struct mrsh_position
type mrsh_command (line 854) | struct mrsh_command
type mrsh_array (line 860) | struct mrsh_array
type mrsh_io_redirect (line 862) | struct mrsh_io_redirect
type mrsh_function_definition (line 869) | struct mrsh_function_definition
function unspecified_word (line 877) | static bool unspecified_word(struct mrsh_parser *parser) {
type mrsh_command (line 917) | struct mrsh_command
type mrsh_parser (line 917) | struct mrsh_parser
type mrsh_brace_group (line 918) | struct mrsh_brace_group
type mrsh_subshell (line 925) | struct mrsh_subshell
type mrsh_if_clause (line 932) | struct mrsh_if_clause
type mrsh_for_clause (line 939) | struct mrsh_for_clause
type mrsh_loop_clause (line 946) | struct mrsh_loop_clause
type mrsh_case_clause (line 953) | struct mrsh_case_clause
type mrsh_function_definition (line 964) | struct mrsh_function_definition
type mrsh_command (line 974) | struct mrsh_command
type mrsh_parser (line 974) | struct mrsh_parser
type mrsh_command (line 977) | struct mrsh_command
type mrsh_simple_command (line 984) | struct mrsh_simple_command
type mrsh_pipeline (line 992) | struct mrsh_pipeline
type mrsh_parser (line 992) | struct mrsh_parser
type mrsh_range (line 993) | struct mrsh_range
type mrsh_position (line 995) | struct mrsh_position
type mrsh_command (line 997) | struct mrsh_command
type mrsh_array (line 1002) | struct mrsh_array
type mrsh_command (line 1007) | struct mrsh_command
type mrsh_pipeline (line 1015) | struct mrsh_pipeline
type mrsh_command (line 1021) | struct mrsh_command
type mrsh_and_or_list (line 1027) | struct mrsh_and_or_list
type mrsh_parser (line 1027) | struct mrsh_parser
type mrsh_pipeline (line 1028) | struct mrsh_pipeline
type mrsh_binop_type (line 1033) | enum mrsh_binop_type
type mrsh_range (line 1034) | struct mrsh_range
type mrsh_and_or_list (line 1044) | struct mrsh_and_or_list
type mrsh_binop (line 1051) | struct mrsh_binop
type mrsh_command_list (line 1056) | struct mrsh_command_list
type mrsh_parser (line 1056) | struct mrsh_parser
type mrsh_and_or_list (line 1057) | struct mrsh_and_or_list
type mrsh_command_list (line 1062) | struct mrsh_command_list
type mrsh_position (line 1065) | struct mrsh_position
function push_buffer_word_string (line 1081) | static void push_buffer_word_string(struct mrsh_array *children,
type mrsh_word (line 1094) | struct mrsh_word
type mrsh_parser (line 1094) | struct mrsh_parser
type mrsh_array (line 1095) | struct mrsh_array
type mrsh_buffer (line 1096) | struct mrsh_buffer
type mrsh_word (line 1106) | struct mrsh_word
type mrsh_word (line 1116) | struct mrsh_word
type mrsh_word (line 1144) | struct mrsh_word
type mrsh_word_list (line 1148) | struct mrsh_word_list
function is_word_quoted (line 1153) | static bool is_word_quoted(struct mrsh_word *word) {
function expect_here_document (line 1175) | static bool expect_here_document(struct mrsh_parser *parser,
function complete_command (line 1234) | static bool complete_command(struct mrsh_parser *parser,
function expect_complete_command (line 1274) | static bool expect_complete_command(struct mrsh_parser *parser,
type mrsh_program (line 1284) | struct mrsh_program
type mrsh_parser (line 1284) | struct mrsh_parser
type mrsh_program (line 1285) | struct mrsh_program
type mrsh_program (line 1314) | struct mrsh_program
type mrsh_parser (line 1314) | struct mrsh_parser
type mrsh_program (line 1321) | struct mrsh_program
type mrsh_program (line 1361) | struct mrsh_program
type mrsh_parser (line 1361) | struct mrsh_parser
FILE: parser/word.c
type mrsh_word (line 14) | struct mrsh_word
type mrsh_parser (line 14) | struct mrsh_parser
type mrsh_position (line 15) | struct mrsh_position
type mrsh_buffer (line 20) | struct mrsh_buffer
type mrsh_word_string (line 44) | struct mrsh_word_string
function peek_name (line 50) | size_t peek_name(struct mrsh_parser *parser, bool in_braces) {
function peek_word (line 76) | size_t peek_word(struct mrsh_parser *parser, char end) {
function token (line 108) | bool token(struct mrsh_parser *parser, const char *str,
function expect_token (line 143) | bool expect_token(struct mrsh_parser *parser, const char *str,
type mrsh_parser (line 154) | struct mrsh_parser
type mrsh_range (line 155) | struct mrsh_range
type mrsh_position (line 160) | struct mrsh_position
type mrsh_word (line 175) | struct mrsh_word
type mrsh_parser (line 175) | struct mrsh_parser
type mrsh_array (line 177) | struct mrsh_array
type mrsh_word (line 184) | struct mrsh_word
type mrsh_position (line 190) | struct mrsh_position
type mrsh_buffer (line 191) | struct mrsh_buffer
type mrsh_word_string (line 203) | struct mrsh_word_string
type mrsh_word (line 214) | struct mrsh_word
type mrsh_word_list (line 218) | struct mrsh_word_list
function char_to_parameter_op_val (line 223) | static enum mrsh_word_parameter_op char_to_parameter_op_val(char c) {
function expect_parameter_op (line 238) | static bool expect_parameter_op(struct mrsh_parser *parser,
type mrsh_word_parameter (line 279) | struct mrsh_word_parameter
type mrsh_parser (line 280) | struct mrsh_parser
type mrsh_position (line 281) | struct mrsh_position
type mrsh_word_parameter_op (line 286) | enum mrsh_word_parameter_op
type mrsh_range (line 287) | struct mrsh_range
type mrsh_range (line 301) | struct mrsh_range
type mrsh_word (line 308) | struct mrsh_word
type mrsh_position (line 318) | struct mrsh_position
type mrsh_word_parameter (line 324) | struct mrsh_word_parameter
type mrsh_word_command (line 333) | struct mrsh_word_command
type mrsh_parser (line 334) | struct mrsh_parser
type mrsh_program (line 345) | struct mrsh_program
type mrsh_word_arithmetic (line 363) | struct mrsh_word_arithmetic
type mrsh_parser (line 364) | struct mrsh_parser
type mrsh_word (line 371) | struct mrsh_word
type mrsh_word (line 392) | struct mrsh_word
type mrsh_parser (line 392) | struct mrsh_parser
type mrsh_position (line 393) | struct mrsh_position
type mrsh_word_parameter (line 397) | struct mrsh_word_parameter
type mrsh_word_arithmetic (line 413) | struct mrsh_word_arithmetic
type mrsh_word_command (line 420) | struct mrsh_word_command
type mrsh_range (line 454) | struct mrsh_range
type mrsh_word (line 467) | struct mrsh_word
type mrsh_parser (line 467) | struct mrsh_parser
type mrsh_position (line 468) | struct mrsh_position
type mrsh_buffer (line 473) | struct mrsh_buffer
type mrsh_parser (line 508) | struct mrsh_parser
type mrsh_program (line 512) | struct mrsh_program
type mrsh_word_command (line 524) | struct mrsh_word_command
function push_buffer_word_string (line 539) | static void push_buffer_word_string(struct mrsh_parser *parser,
type mrsh_word (line 558) | struct mrsh_word
type mrsh_parser (line 558) | struct mrsh_parser
type mrsh_position (line 559) | struct mrsh_position
type mrsh_array (line 564) | struct mrsh_array
type mrsh_buffer (line 565) | struct mrsh_buffer
type mrsh_position (line 566) | struct mrsh_position
type mrsh_position (line 567) | struct mrsh_position
type mrsh_word (line 587) | struct mrsh_word
type mrsh_word (line 597) | struct mrsh_word
type mrsh_word_list (line 632) | struct mrsh_word_list
type mrsh_word (line 638) | struct mrsh_word
type mrsh_parser (line 638) | struct mrsh_parser
type mrsh_array (line 649) | struct mrsh_array
type mrsh_buffer (line 650) | struct mrsh_buffer
type mrsh_position (line 651) | struct mrsh_position
type mrsh_word (line 665) | struct mrsh_word
type mrsh_word (line 675) | struct mrsh_word
type mrsh_word (line 686) | struct mrsh_word
type mrsh_word (line 695) | struct mrsh_word
type mrsh_word (line 726) | struct mrsh_word
type mrsh_word_list (line 730) | struct mrsh_word_list
type mrsh_word (line 736) | struct mrsh_word
type mrsh_parser (line 736) | struct mrsh_parser
type mrsh_array (line 749) | struct mrsh_array
type mrsh_buffer (line 750) | struct mrsh_buffer
type mrsh_position (line 751) | struct mrsh_position
type mrsh_word (line 768) | struct mrsh_word
type mrsh_word (line 778) | struct mrsh_word
type mrsh_word (line 789) | struct mrsh_word
type mrsh_word (line 798) | struct mrsh_word
type mrsh_word (line 842) | struct mrsh_word
type mrsh_word_list (line 846) | struct mrsh_word_list
type mrsh_word (line 854) | struct mrsh_word
type mrsh_parser (line 854) | struct mrsh_parser
type mrsh_array (line 855) | struct mrsh_array
type mrsh_buffer (line 856) | struct mrsh_buffer
type mrsh_position (line 857) | struct mrsh_position
type mrsh_word (line 871) | struct mrsh_word
type mrsh_word (line 881) | struct mrsh_word
type mrsh_word (line 910) | struct mrsh_word
type mrsh_word_list (line 914) | struct mrsh_word_list
FILE: shell/arithm.c
function run_variable (line 5) | static bool run_variable(struct mrsh_state *state, const char *name, lon...
function run_arithm_binop (line 27) | static bool run_arithm_binop(struct mrsh_state *state,
function run_arithm_unop (line 105) | static bool run_arithm_unop(struct mrsh_state *state,
function run_arithm_cond (line 128) | static bool run_arithm_cond(struct mrsh_state *state,
function run_arithm_assign_op (line 146) | static long run_arithm_assign_op(enum mrsh_arithm_assign_op op,
function run_arithm_assign (line 194) | static bool run_arithm_assign(struct mrsh_state *state,
function mrsh_run_arithm_expr (line 218) | bool mrsh_run_arithm_expr(struct mrsh_state *state,
FILE: shell/entry.c
type mrsh_state (line 15) | struct mrsh_state
type mrsh_parser (line 16) | struct mrsh_parser
type mrsh_word (line 20) | struct mrsh_word
type mrsh_position (line 22) | struct mrsh_position
type mrsh_state (line 40) | struct mrsh_state
type mrsh_state (line 53) | struct mrsh_state
type mrsh_state (line 64) | struct mrsh_state
type mrsh_state (line 73) | struct mrsh_state
function mrsh_populate_env (line 81) | bool mrsh_populate_env(struct mrsh_state *state, char **environ) {
function source_file (line 118) | static void source_file(struct mrsh_state *state, char *path) {
function mrsh_source_profile (line 126) | void mrsh_source_profile(struct mrsh_state *state) {
function mrsh_source_env (line 147) | void mrsh_source_env(struct mrsh_state *state) {
function mrsh_run_exit_trap (line 166) | bool mrsh_run_exit_trap(struct mrsh_state *state) {
FILE: shell/job.c
function mrsh_set_job_control (line 16) | bool mrsh_set_job_control(struct mrsh_state *state, bool enabled) {
function array_remove (line 66) | static void array_remove(struct mrsh_array *array, size_t i) {
type mrsh_job (line 72) | struct mrsh_job
type mrsh_state (line 72) | struct mrsh_state
type mrsh_node (line 73) | struct mrsh_node
type mrsh_state_priv (line 74) | struct mrsh_state_priv
type mrsh_job (line 78) | struct mrsh_job
type mrsh_job (line 84) | struct mrsh_job
type mrsh_job (line 84) | struct mrsh_job
function job_destroy (line 94) | void job_destroy(struct mrsh_job *job) {
function job_add_process (line 120) | void job_add_process(struct mrsh_job *job, struct mrsh_process *proc) {
function job_queue_notification (line 132) | static void job_queue_notification(struct mrsh_job *job) {
function job_set_foreground (line 143) | bool job_set_foreground(struct mrsh_job *job, bool foreground, bool cont) {
function job_poll (line 197) | int job_poll(struct mrsh_job *job) {
type mrsh_state (line 218) | struct mrsh_state
function _job_wait (line 220) | static bool _job_wait(struct mrsh_state *state, pid_t pid, int options) {
type mrsh_process (line 254) | struct mrsh_process
type mrsh_job (line 254) | struct mrsh_job
type mrsh_process (line 256) | struct mrsh_process
function job_wait (line 264) | int job_wait(struct mrsh_job *job) {
function job_wait_process (line 279) | int job_wait_process(struct mrsh_process *proc) {
function refresh_jobs_status (line 292) | bool refresh_jobs_status(struct mrsh_state *state) {
function init_job_child_process (line 309) | bool init_job_child_process(struct mrsh_state *state) {
function update_job (line 313) | static void update_job(struct mrsh_state *state, pid_t pid, int stat) {
type mrsh_job (line 339) | struct mrsh_job
type mrsh_state (line 339) | struct mrsh_state
type mrsh_state_priv (line 341) | struct mrsh_state_priv
type mrsh_job (line 356) | struct mrsh_job
type mrsh_job (line 362) | struct mrsh_job
type mrsh_job (line 374) | struct mrsh_job
type mrsh_job (line 383) | struct mrsh_job
type mrsh_job (line 409) | struct mrsh_job
type mrsh_job (line 421) | struct mrsh_job
type mrsh_job (line 444) | struct mrsh_job
type mrsh_process (line 453) | struct mrsh_process
type mrsh_process (line 466) | struct mrsh_process
function broadcast_sighup_to_jobs (line 478) | void broadcast_sighup_to_jobs(struct mrsh_state *state) {
FILE: shell/path.c
type mrsh_state (line 12) | struct mrsh_state
type mrsh_buffer (line 71) | struct mrsh_buffer
FILE: shell/process.c
type mrsh_process (line 12) | struct mrsh_process
type mrsh_state (line 12) | struct mrsh_state
type mrsh_state_priv (line 13) | struct mrsh_state_priv
type mrsh_process (line 15) | struct mrsh_process
type mrsh_process (line 15) | struct mrsh_process
function array_remove (line 22) | static void array_remove(struct mrsh_array *array, size_t i) {
function process_destroy (line 28) | void process_destroy(struct mrsh_process *proc) {
function process_poll (line 41) | int process_poll(struct mrsh_process *proc) {
function update_process (line 57) | void update_process(struct mrsh_state *state, pid_t pid, int stat) {
FILE: shell/redir.c
function write_here_document_line (line 11) | static ssize_t write_here_document_line(int fd, struct mrsh_word *line,
function create_here_document_fd (line 40) | static int create_here_document_fd(const struct mrsh_array *lines) {
function parse_fd (line 96) | static int parse_fd(const char *str) {
function process_redir (line 111) | int process_redir(const struct mrsh_io_redirect *redir, int *redir_fd) {
FILE: shell/shell.c
function function_destroy (line 13) | void function_destroy(struct mrsh_function *fn) {
type mrsh_state (line 21) | struct mrsh_state
type mrsh_state_priv (line 22) | struct mrsh_state_priv
type mrsh_state (line 29) | struct mrsh_state
type mrsh_call_frame_priv (line 32) | struct mrsh_call_frame_priv
type mrsh_call_frame_priv (line 33) | struct mrsh_call_frame_priv
type mrsh_state (line 44) | struct mrsh_state
type mrsh_state_priv (line 45) | struct mrsh_state_priv
function mrsh_state_set_parser_alias_func (line 49) | void mrsh_state_set_parser_alias_func(
function state_string_finish_iterator (line 54) | static void state_string_finish_iterator(const char *key, void *value,
function variable_destroy (line 59) | static void variable_destroy(struct mrsh_variable *var) {
function state_var_finish_iterator (line 67) | static void state_var_finish_iterator(const char *key, void *value,
function state_fn_finish_iterator (line 72) | static void state_fn_finish_iterator(const char *key, void *value, void ...
function call_frame_destroy (line 76) | static void call_frame_destroy(struct mrsh_call_frame *frame) {
function mrsh_state_destroy (line 84) | void mrsh_state_destroy(struct mrsh_state *state) {
type mrsh_state_priv (line 116) | struct mrsh_state_priv
type mrsh_state (line 116) | struct mrsh_state
type mrsh_state_priv (line 117) | struct mrsh_state_priv
function mrsh_env_set (line 120) | void mrsh_env_set(struct mrsh_state *state,
function mrsh_env_unset (line 134) | void mrsh_env_unset(struct mrsh_state *state, const char *key) {
type mrsh_state (line 140) | struct mrsh_state
type mrsh_state_priv (line 142) | struct mrsh_state_priv
type mrsh_variable (line 144) | struct mrsh_variable
type mrsh_call_frame_priv (line 151) | struct mrsh_call_frame_priv
type mrsh_call_frame (line 151) | struct mrsh_call_frame
type mrsh_call_frame_priv (line 152) | struct mrsh_call_frame_priv
function push_frame (line 155) | void push_frame(struct mrsh_state *state, int argc, const char *argv[]) {
function pop_frame (line 166) | void pop_frame(struct mrsh_state *state) {
FILE: shell/task/pipeline.c
type mrsh_process (line 12) | struct mrsh_process
type mrsh_context (line 12) | struct mrsh_context
type mrsh_process (line 13) | struct mrsh_process
function run_pipeline (line 24) | int run_pipeline(struct mrsh_context *ctx, struct mrsh_pipeline *pl) {
FILE: shell/task/simple_command.c
function populate_env_iterator (line 18) | static void populate_env_iterator(const char *key, void *_var, void *_) {
type mrsh_process (line 29) | struct mrsh_process
type mrsh_context (line 29) | struct mrsh_context
type mrsh_process (line 30) | struct mrsh_process
function run_process (line 41) | static int run_process(struct mrsh_context *ctx, struct mrsh_simple_comm...
type saved_fd (line 116) | struct saved_fd {
function dup_and_save_fd (line 121) | static bool dup_and_save_fd(int fd, int redir_fd, struct saved_fd *saved) {
function run_builtin (line 145) | static int run_builtin(struct mrsh_context *ctx, struct mrsh_simple_comm...
function run_assignments (line 195) | static int run_assignments(struct mrsh_context *ctx, struct mrsh_array *...
function expand_assignments (line 218) | static int expand_assignments(struct mrsh_context *ctx,
type mrsh_simple_command (line 231) | struct mrsh_simple_command
type mrsh_simple_command (line 232) | struct mrsh_simple_command
type mrsh_command (line 233) | struct mrsh_command
function run_simple_command (line 237) | int run_simple_command(struct mrsh_context *ctx, struct mrsh_simple_comm...
FILE: shell/task/task.c
function run_subshell (line 15) | static int run_subshell(struct mrsh_context *ctx, struct mrsh_array *arr...
function run_if_clause (line 56) | static int run_if_clause(struct mrsh_context *ctx, struct mrsh_if_clause...
function run_loop_clause (line 72) | static int run_loop_clause(struct mrsh_context *ctx, struct mrsh_loop_cl...
function run_for_clause (line 132) | static int run_for_clause(struct mrsh_context *ctx, struct mrsh_for_clau...
function run_case_clause (line 196) | static int run_case_clause(struct mrsh_context *ctx, struct mrsh_case_cl...
function run_function_definition (line 245) | static int run_function_definition(struct mrsh_context *ctx,
function run_command (line 257) | int run_command(struct mrsh_context *ctx, struct mrsh_command *cmd) {
function run_and_or_list (line 289) | int run_and_or_list(struct mrsh_context *ctx, struct mrsh_and_or_list *a...
type mrsh_process (line 318) | struct mrsh_process
type mrsh_context (line 318) | struct mrsh_context
type mrsh_process (line 319) | struct mrsh_process
function run_command_list_array (line 328) | int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *...
function show_job (line 392) | static void show_job(struct mrsh_job *job, struct mrsh_job *current,
function mrsh_destroy_terminated_jobs (line 406) | void mrsh_destroy_terminated_jobs(struct mrsh_state *state) {
function mrsh_run_program (line 434) | int mrsh_run_program(struct mrsh_state *state, struct mrsh_program *prog) {
function mrsh_run_word (line 441) | int mrsh_run_word(struct mrsh_state *state, struct mrsh_word **word) {
FILE: shell/task/word.c
function buffer_read_from (line 19) | static bool buffer_read_from(struct mrsh_buffer *buf, int fd) {
function naive_word_streq (line 39) | static bool naive_word_streq(struct mrsh_word *word, const char *str) {
function is_print_traps (line 54) | static bool is_print_traps(struct mrsh_program *program) {
function swap_words (line 83) | static void swap_words(struct mrsh_word **word_ptr, struct mrsh_word *ne...
function run_word_command (line 88) | static int run_word_command(struct mrsh_context *ctx, struct mrsh_word *...
type mrsh_state (line 156) | struct mrsh_state
type mrsh_state_priv (line 158) | struct mrsh_state_priv
type mrsh_job (line 181) | struct mrsh_job
type mrsh_process (line 185) | struct mrsh_process
type mrsh_word (line 202) | struct mrsh_word
type mrsh_state (line 202) | struct mrsh_state
type mrsh_array (line 212) | struct mrsh_array
type mrsh_word_string (line 216) | struct mrsh_word_string
type mrsh_word_string (line 221) | struct mrsh_word_string
type mrsh_word_list (line 227) | struct mrsh_word_list
function mark_word_split_fields (line 231) | static void mark_word_split_fields(struct mrsh_word *word) {
type mrsh_word (line 248) | struct mrsh_word
type mrsh_word_string (line 249) | struct mrsh_word_string
type mrsh_word (line 253) | struct mrsh_word
type mrsh_word (line 253) | struct mrsh_word
function is_null_word (line 261) | static bool is_null_word(const struct mrsh_word *word) {
function apply_parameter_cond_op (line 280) | static int apply_parameter_cond_op(struct mrsh_context *ctx,
function apply_parameter_str_op (line 402) | static int apply_parameter_str_op(struct mrsh_context *ctx,
function _run_word (line 465) | static int _run_word(struct mrsh_context *ctx, struct mrsh_word **word_ptr,
function run_word (line 669) | int run_word(struct mrsh_context *ctx, struct mrsh_word **word_ptr) {
function expand_word (line 673) | int expand_word(struct mrsh_context *ctx, const struct mrsh_word *_word,
FILE: shell/trap.c
function handle_signal (line 19) | static void handle_signal(int sig) {
function set_trap (line 24) | bool set_trap(struct mrsh_state *state, int sig, enum mrsh_trap_action a...
function set_job_control_traps (line 85) | bool set_job_control_traps(struct mrsh_state *state, bool enabled) {
function reset_caught_traps (line 111) | bool reset_caught_traps(struct mrsh_state *state) {
function run_pending_traps (line 126) | bool run_pending_traps(struct mrsh_state *state) {
function run_exit_trap (line 161) | bool run_exit_trap(struct mrsh_state *state) {
FILE: shell/word.c
function is_logname_char (line 13) | static bool is_logname_char(char c) {
function expand_tilde_at (line 19) | static ssize_t expand_tilde_at(struct mrsh_state *state, const char *str,
function _expand_tilde (line 60) | static void _expand_tilde(struct mrsh_state *state, struct mrsh_word **w...
function expand_tilde (line 133) | void expand_tilde(struct mrsh_state *state, struct mrsh_word **word_ptr,
type split_fields_data (line 138) | struct split_fields_data {
function add_to_cur_field (line 145) | static void add_to_cur_field(struct split_fields_data *data,
function _split_fields (line 154) | static void _split_fields(struct split_fields_data *data,
function split_fields (line 216) | void split_fields(struct mrsh_array *fields, const struct mrsh_word *word,
function get_fields_str (line 245) | void get_fields_str(struct mrsh_array *strs, const struct mrsh_array *fi...
function is_pathname_metachar (line 252) | static bool is_pathname_metachar(char c) {
function needs_pathname_expansion (line 264) | static bool needs_pathname_expansion(const struct mrsh_word *word) {
function _word_to_pattern (line 298) | static void _word_to_pattern(struct mrsh_buffer *buf,
type mrsh_word (line 326) | struct mrsh_word
type mrsh_buffer (line 331) | struct mrsh_buffer
function expand_pathnames (line 337) | bool expand_pathnames(struct mrsh_array *expanded,
Condensed preview — 127 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (419K chars).
[
{
"path": ".builds/alpine.yml",
"chars": 577,
"preview": "image: alpine/edge\npackages:\n - gcc\n - clang\n - meson\n - dash\nsources:\n - https://git.sr.ht/~emersion/mrsh\ntasks:\n "
},
{
"path": ".builds/archlinux.yml",
"chars": 607,
"preview": "image: archlinux\npackages:\n - gcc\n - meson\n - readline\nsources:\n - https://git.sr.ht/~emersion/mrsh\ntasks:\n - setup"
},
{
"path": ".builds/freebsd.yml",
"chars": 400,
"preview": "image: freebsd/latest\npackages:\n - meson\n - libedit\n - pkgconf\nsources:\n - https://git.sr.ht/~emersion/mrsh\ntasks:\n "
},
{
"path": ".editorconfig",
"chars": 191,
"preview": "root = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\ncharset = utf-8\nindent_style = tab\ntrim_trailing_whitespac"
},
{
"path": ".gitignore",
"chars": 473,
"preview": "# Prerequisites\n*.d\n\n# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Linker output\n*.ilk\n*.map\n*.exp\n\n# Precompiled Headers\n*.gch"
},
{
"path": "LICENSE",
"chars": 1065,
"preview": "MIT License\n\nCopyright (c) 2018 emersion\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
},
{
"path": "Makefile",
"chars": 2462,
"preview": ".POSIX:\n.SUFFIXES:\nOUTDIR=.build\ninclude $(OUTDIR)/config.mk\n\nINCLUDE=-Iinclude\n\npublic_includes=\\\n\t\tinclude/mrsh/arithm"
},
{
"path": "README.md",
"chars": 855,
"preview": "# mrsh\n\nA minimal [POSIX] shell.\n\n[](https"
},
{
"path": "arithm.c",
"chars": 4324,
"preview": "#include <assert.h>\n#include <mrsh/arithm.h>\n#include <mrsh/ast.h>\n#include <stdlib.h>\n\nvoid mrsh_arithm_expr_destroy(st"
},
{
"path": "array.c",
"chars": 860,
"preview": "#include <assert.h>\n#include <mrsh/array.h>\n#include <stdlib.h>\n\n#define INITIAL_SIZE 8\n\nbool mrsh_array_reserve(struct "
},
{
"path": "ast.c",
"chars": 39892,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <mrsh/buffer.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#i"
},
{
"path": "ast_print.c",
"chars": 13552,
"preview": "#include <assert.h>\n#include <mrsh/ast.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h"
},
{
"path": "buffer.c",
"chars": 1323,
"preview": "#include <mrsh/buffer.h>\n#include <stdlib.h>\n#include <string.h>\n\nchar *mrsh_buffer_reserve(struct mrsh_buffer *buf, siz"
},
{
"path": "builtin/alias.c",
"chars": 1411,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#in"
},
{
"path": "builtin/bg.c",
"chars": 996,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include \"builtin.h\"\n#include \"mrsh_getopt.h\"\n#include \"shell/job.h\"\n#include \"sh"
},
{
"path": "builtin/break.c",
"chars": 921,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#in"
},
{
"path": "builtin/builtin.c",
"chars": 5170,
"preview": "#include <assert.h>\n#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype"
},
{
"path": "builtin/cd.c",
"chars": 3323,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <mrsh/shell.h>\n#include <stdio.h>\n#include <stdlib.h>\n#inclu"
},
{
"path": "builtin/colon.c",
"chars": 188,
"preview": "#include <mrsh/builtin.h>\n#include <stdlib.h>\n#include \"builtin.h\"\n\nint builtin_colon(struct mrsh_state *state, int argc"
},
{
"path": "builtin/command.c",
"chars": 2937,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include "
},
{
"path": "builtin/dot.c",
"chars": 1423,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <mrsh/builtin.h>\n#inc"
},
{
"path": "builtin/eval.c",
"chars": 1234,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <errno.h>\n#include <mrsh/buffer.h>\n#include <mrsh/builtin.h"
},
{
"path": "builtin/exec.c",
"chars": 821,
"preview": "#include <stdio.h>\n#include <unistd.h>\n#include \"builtin.h\"\n#include \"mrsh_getopt.h\"\n#include \"shell/path.h\"\n\nstatic con"
},
{
"path": "builtin/exit.c",
"chars": 784,
"preview": "#include <errno.h>\n#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include \"builtin.h\"\n#include \"shell"
},
{
"path": "builtin/export.c",
"chars": 1699,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#in"
},
{
"path": "builtin/false.c",
"chars": 149,
"preview": "#include <mrsh/shell.h>\n#include <stdlib.h>\n#include \"builtin.h\"\n\nint builtin_false(struct mrsh_state *state, int argc, "
},
{
"path": "builtin/fg.c",
"chars": 878,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include \"builtin.h\"\n#include \"mrsh_getopt.h\"\n#include \"shell/job.h\"\n#include \"sh"
},
{
"path": "builtin/getopts.c",
"chars": 2640,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <limits.h>\n#include <mrsh/buffer.h>\n#include <mrsh/shell.h>\n"
},
{
"path": "builtin/hash.c",
"chars": 1239,
"preview": "#include <mrsh/builtin.h>\n#include <shell/path.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"b"
},
{
"path": "builtin/jobs.c",
"chars": 2481,
"preview": "#include <assert.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <signal.h>\n#include \"builtin.h\"\n"
},
{
"path": "builtin/pwd.c",
"chars": 771,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <mrsh/shell.h>\n#include <stdio.h>\n#include <stdlib.h>\n#incl"
},
{
"path": "builtin/read.c",
"chars": 2820,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <mrsh/buffer.h>\n#include <mrsh/shell.h>\n#include <shell/word"
},
{
"path": "builtin/return.c",
"chars": 783,
"preview": "#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"builtin.h\"\n#include \"shel"
},
{
"path": "builtin/set.c",
"chars": 6051,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <mrsh/builtin.h>\n#include <mrsh/shell.h>\n#include <stdbool.h"
},
{
"path": "builtin/shift.c",
"chars": 1145,
"preview": "#include <errno.h>\n#include <mrsh/builtin.h>\n#include <mrsh/shell.h>\n#include <stdlib.h>\n#include \"builtin.h\"\n\nstatic co"
},
{
"path": "builtin/times.c",
"chars": 917,
"preview": "#include <errno.h>\n#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ti"
},
{
"path": "builtin/trap.c",
"chars": 4470,
"preview": "#define _XOPEN_SOURCE 1 // for SIGPOLL and SIGVTALRM\n#include <assert.h>\n#include <mrsh/parser.h>\n#include <mrsh/shell.h"
},
{
"path": "builtin/true.c",
"chars": 148,
"preview": "#include <mrsh/shell.h>\n#include <stdlib.h>\n#include \"builtin.h\"\n\nint builtin_true(struct mrsh_state *state, int argc, c"
},
{
"path": "builtin/type.c",
"chars": 1324,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <mrsh/builtin.h>\n#include <shell/path.h>\n#include <stdlib.h>\n#include \"builtin."
},
{
"path": "builtin/ulimit.c",
"chars": 1394,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <inttypes.h>\n#include <limits.h>\n#include <mrsh/shell.h>\n#in"
},
{
"path": "builtin/umask.c",
"chars": 4180,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n"
},
{
"path": "builtin/unalias.c",
"chars": 1229,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#in"
},
{
"path": "builtin/unset.c",
"chars": 1280,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <mrsh/builtin.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#in"
},
{
"path": "builtin/unspecified.c",
"chars": 546,
"preview": "#include <mrsh/shell.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include \"builtin.h\"\n\nint builtin_unspecified(struct mrsh"
},
{
"path": "builtin/wait.c",
"chars": 2620,
"preview": "#define _POSIX_C_SOURCE 200112L\n#include <errno.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include "
},
{
"path": "configure",
"chars": 5618,
"preview": "#!/bin/sh -e\nSOVERSION=0.0.0\n\npkg_config=${PKG_CONFIG:-pkg-config}\noutdir=${OUTDIR:-.build}\nsrcdir=${SRCDIR:-$(dirname \""
},
{
"path": "example/highlight.c",
"chars": 10281,
"preview": "#include <assert.h>\n#include <errno.h>\n#include <mrsh/buffer.h>\n#include <mrsh/parser.h>\n#include <stdio.h>\n#include <st"
},
{
"path": "example/meson.build",
"chars": 117,
"preview": "executable(\n\t'highlight',\n\tfiles('highlight.c'),\n\tdependencies: [mrsh],\n\tbuild_by_default: get_option('examples'),\n)\n"
},
{
"path": "frontend/basic.c",
"chars": 646,
"preview": "/* Basic, strictly POSIX interactive line interface */\n#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <stdi"
},
{
"path": "frontend/readline.c",
"chars": 2105,
"preview": "// readline/editline interactive line interface\n#define _POSIX_C_SOURCE 200809L\n#include <mrsh/parser.h>\n#include <mrsh/"
},
{
"path": "getopt.c",
"chars": 1591,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <stdio.h>\n#include \"mrsh_getopt.h\"\n\nchar *_mrsh_optarg = NU"
},
{
"path": "hashtable.c",
"chars": 2887,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <mrsh/hashtable.h>\n#include <stdlib.h>\n#include <string.h>\n\nstatic unsigned int"
},
{
"path": "include/ast.h",
"chars": 170,
"preview": "#ifndef AST_H\n#define AST_H\n\n#include <mrsh/ast.h>\n\nvoid command_list_array_finish(struct mrsh_array *cmds);\nvoid case_i"
},
{
"path": "include/builtin.h",
"chars": 2749,
"preview": "#ifndef BUILTIN_H\n#define BUILTIN_H\n\n#include <mrsh/builtin.h>\n\nstruct mrsh_state;\n\ntypedef int (*mrsh_builtin_func)(str"
},
{
"path": "include/frontend.h",
"chars": 231,
"preview": "#ifndef FRONTEND_H\n#define FRONTEND_H\n\n#include <stddef.h>\n#include <stdio.h>\n\nvoid interactive_init(struct mrsh_state *"
},
{
"path": "include/mrsh/arithm.h",
"chars": 3398,
"preview": "#ifndef MRSH_AST_ARITHM_H\n#define MRSH_AST_ARITHM_H\n\nenum mrsh_arithm_expr_type {\n\tMRSH_ARITHM_LITERAL,\n\tMRSH_ARITHM_VAR"
},
{
"path": "include/mrsh/array.h",
"chars": 349,
"preview": "#ifndef MRSH_ARRAY_H\n#define MRSH_ARRAY_H\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <sys/types.h>\n\nstruct mrsh_"
},
{
"path": "include/mrsh/ast.h",
"chars": 15489,
"preview": "#ifndef MRSH_AST_H\n#define MRSH_AST_H\n\n#include <mrsh/array.h>\n#include <stdbool.h>\n\n/**\n * Position describes an arbitr"
},
{
"path": "include/mrsh/buffer.h",
"chars": 1197,
"preview": "#ifndef MRSH_BUFFER_H\n#define MRSH_BUFFER_H\n\n#include <stdbool.h>\n#include <stddef.h>\n\nstruct mrsh_buffer {\n\tchar *data;"
},
{
"path": "include/mrsh/builtin.h",
"chars": 427,
"preview": "#ifndef MRSH_BUILTIN_H\n#define MRSH_BUILTIN_H\n\n#include <mrsh/shell.h>\n\nbool mrsh_has_builtin(const char *name);\nbool mr"
},
{
"path": "include/mrsh/entry.h",
"chars": 1337,
"preview": "#ifndef MRSH_ENTRY_H\n#define MRSH_ENTRY_H\n\n#include <mrsh/shell.h>\n#include <stdbool.h>\n\n/**\n * Expands $PS1 or returns "
},
{
"path": "include/mrsh/hashtable.h",
"chars": 1003,
"preview": "#ifndef MRSH_HASHTABLE_H\n#define MRSH_HASHTABLE_H\n\n#define MRSH_HASHTABLE_BUCKETS 256\n\nstruct mrsh_hashtable_entry {\n\tst"
},
{
"path": "include/mrsh/parser.h",
"chars": 1910,
"preview": "#ifndef MRSH_PARSER_H\n#define MRSH_PARSER_H\n\n#include <mrsh/ast.h>\n#include <stdio.h>\n\nstruct mrsh_parser;\nstruct mrsh_b"
},
{
"path": "include/mrsh/shell.h",
"chars": 4084,
"preview": "#ifndef MRSH_SHELL_H\n#define MRSH_SHELL_H\n\n#include <mrsh/arithm.h>\n#include <mrsh/ast.h>\n#include <mrsh/hashtable.h>\n#i"
},
{
"path": "include/mrsh_getopt.h",
"chars": 206,
"preview": "#ifndef MRSH_GETOPT_H\n#define MRSH_GETOPT_H\n\nextern char *_mrsh_optarg;\nextern int _mrsh_opterr, _mrsh_optind, _mrsh_opt"
},
{
"path": "include/parser.h",
"chars": 2714,
"preview": "#ifndef PARSER_H\n#define PARSER_H\n\n#include <stdio.h>\n#include <mrsh/buffer.h>\n#include <mrsh/parser.h>\n\nenum symbol_nam"
},
{
"path": "include/shell/job.h",
"chars": 2999,
"preview": "#ifndef SHELL_JOB_H\n#define SHELL_JOB_H\n\n#include <mrsh/array.h>\n#include <stdbool.h>\n#include <sys/types.h>\n#include <t"
},
{
"path": "include/shell/path.h",
"chars": 592,
"preview": "#ifndef SHELL_PATH_H\n#define SHELL_PATH_H\n\n#include <mrsh/shell.h>\n#include <stdbool.h>\n\n/* Searches $PATH for the reque"
},
{
"path": "include/shell/process.h",
"chars": 1045,
"preview": "#ifndef SHELL_PROCESS_H\n#define SHELL_PROCESS_H\n\n#include <mrsh/shell.h>\n#include <stdbool.h>\n#include <sys/types.h>\n\n/*"
},
{
"path": "include/shell/redir.h",
"chars": 148,
"preview": "#ifndef SHELL_REDIR_H\n#define SHELL_REDIR_H\n\n#include <mrsh/ast.h>\n\nint process_redir(const struct mrsh_io_redirect *red"
},
{
"path": "include/shell/shell.h",
"chars": 1852,
"preview": "#ifndef SHELL_SHELL_H\n#define SHELL_SHELL_H\n\n#include <mrsh/shell.h>\n#include <termios.h>\n#include \"job.h\"\n#include \"pro"
},
{
"path": "include/shell/task.h",
"chars": 1292,
"preview": "#ifndef SHELL_TASK_H\n#define SHELL_TASK_H\n\n#include \"shell/shell.h\"\n#include \"shell/word.h\"\n\n/**\n * The task is waiting "
},
{
"path": "include/shell/trap.h",
"chars": 634,
"preview": "#ifndef SHELL_TRAP_H\n#define SHELL_TRAP_H\n\n#include <stdbool.h>\n\nstruct mrsh_state;\nstruct mrsh_program;\n\n// TODO: find "
},
{
"path": "include/shell/word.h",
"chars": 897,
"preview": "#ifndef SHELL_WORD_H\n#define SHELL_WORD_H\n\n#include <mrsh/shell.h>\n\n/**\n * Performs tilde expansion. It leaves the word "
},
{
"path": "libmrsh.darwin.sym",
"chars": 61,
"preview": "# On Darwin, symbols are prefixed with an underscore\n_mrsh_*\n"
},
{
"path": "libmrsh.gnu.sym",
"chars": 37,
"preview": "{\n\tglobal:\n\t\tmrsh_*;\n\tlocal:\n\t\t*;\n};\n"
},
{
"path": "main.c",
"chars": 3653,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <mrsh/ast.h>\n#include"
},
{
"path": "meson.build",
"chars": 4297,
"preview": "project(\n\t'mrsh',\n\t'c',\n\tversion: '0.0.0',\n\tlicense: 'MIT',\n\tmeson_version: '>=0.47.0',\n\tdefault_options: [\n\t\t'c_std=c99"
},
{
"path": "meson_options.txt",
"chars": 620,
"preview": "option(\n\t'readline',\n\ttype: 'feature',\n\tvalue: 'auto',\n\tdescription: 'Enable improved interactive interface via readline"
},
{
"path": "mkpc",
"chars": 207,
"preview": "#!/bin/sh\ncat <<EOF > $1\nprefix=$PREFIX\nlibdir=\\${prefix}/lib\nincludedir=\\${prefix}/include\n\nName: mrsh\nDescription: POS"
},
{
"path": "parser/arithm.c",
"chars": 13056,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <ctype.h>\n#include <errno.h>\n#include <mrsh/arithm.h>\n#include <stdlib.h>\n#incl"
},
{
"path": "parser/parser.c",
"chars": 7615,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <ctype.h>\n#include <errno.h>\n#include <mrsh/buffer.h>\n#incl"
},
{
"path": "parser/program.c",
"chars": 30639,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <ctype.h>\n#include <mrsh/buffer.h>\n#include <stdbool.h>\n#in"
},
{
"path": "parser/word.c",
"chars": 20918,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <ctype.h>\n#include <mrsh/buffer.h>\n#include <stdbool.h>\n#in"
},
{
"path": "shell/arithm.c",
"chars": 6269,
"preview": "#include <assert.h>\n#include <mrsh/shell.h>\n#include <stdlib.h>\n\nstatic bool run_variable(struct mrsh_state *state, cons"
},
{
"path": "shell/entry.c",
"chars": 4212,
"preview": "#define _XOPEN_SOURCE 700\n#include <mrsh/builtin.h>\n#include <mrsh/entry.h>\n#include <mrsh/shell.h>\n#include <mrsh/parse"
},
{
"path": "shell/job.c",
"chars": 11820,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <errno.h>\n#include <mrsh/array.h>\n#include <signal.h>\n#incl"
},
{
"path": "shell/path.c",
"chars": 1810,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <mrsh/buffer.h>\n#include <mrsh/shell.h>\n#include <string.h>\n"
},
{
"path": "shell/process.c",
"chars": 1863,
"preview": "#define _POSIX_C_SOURCE 1\n#include <assert.h>\n#include <mrsh/array.h>\n#include <signal.h>\n#include <stdbool.h>\n#include "
},
{
"path": "shell/redir.c",
"chars": 3599,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <s"
},
{
"path": "shell/shell.c",
"chars": 4594,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <errno.h>\n#include <mrsh/hashtable.h>\n#include <mrsh/parser"
},
{
"path": "shell/task/pipeline.c",
"chars": 3207,
"preview": "#include <assert.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include \"shell/task."
},
{
"path": "shell/task/simple_command.c",
"chars": 9084,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <mrsh/ast.h>\n#include"
},
{
"path": "shell/task/task.c",
"chars": 10984,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <fnmatch.h>\n#include "
},
{
"path": "shell/task/word.c",
"chars": 18502,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <errno.h>\n#include <fnmatch.h>\n#include <mrsh/buffer.h>\n#in"
},
{
"path": "shell/trap.c",
"chars": 3763,
"preview": "#define _POSIX_C_SOURCE 1\n#include <assert.h>\n#include <signal.h>\n#include \"shell/shell.h\"\n#include \"shell/trap.h\"\n\nstat"
},
{
"path": "shell/word.c",
"chars": 8781,
"preview": "#define _POSIX_C_SOURCE 200809L\n#include <assert.h>\n#include <ctype.h>\n#include <glob.h>\n#include <mrsh/buffer.h>\n#inclu"
},
{
"path": "test/args.sh",
"chars": 64,
"preview": "func() (\n\tgetopts \"abcd\" opt\n)\n\nfunc\nfunc -a\nfunc -ab\nfunc -abc\n"
},
{
"path": "test/arithm.sh",
"chars": 1147,
"preview": "#!/bin/sh -eu\n\necho \"1 =\" $((1))\necho \"2*5 =\" $((2*5))\necho \"2/5 =\" $((2/5))\necho \"2%5 =\" $((2%5))\necho \"2+5 =\" $((2+5))"
},
{
"path": "test/async.sh",
"chars": 449,
"preview": "#!/bin/sh\n\necho >&2 \"Returns immediately\"\n(wait)\n\necho >&2 \"Run asynchronous list and wait\"\necho a &\nwait $!\n\necho >&2 \""
},
{
"path": "test/case.sh",
"chars": 825,
"preview": "#!/bin/sh\n\nx=hello\n\necho \"exact matching with variable expansion\"\ncase \"$x\" in\n\thello)\n\t\techo pass\n\t\t;;\n\tworld)\n\t\techo f"
},
{
"path": "test/command.sh",
"chars": 294,
"preview": "#!/bin/sh\n\nlla () {\n\tls -la\n}\n\nalias ll=\"ls -l\"\n\ncommand -v if\necho \"exists if $?\"\ncommand -v cd\necho \"exits cd $?\"\ncomm"
},
{
"path": "test/conformance/2.2-quoted-characters.sh",
"chars": 1005,
"preview": "# 2.2.1 Escape Character (Backslash)\n# The following must be quoted:\nprintf '%s\\n' \\|\\&\\;\\<\\>\\(\\)\\$\\`\\\\\\\"\\'\n# The follow"
},
{
"path": "test/conformance/2.2-quoted-characters.stdout",
"chars": 250,
"preview": "|&;<>()$`\\\"'\n*?[#˜=%\narg one: with space; arg two: with\ttab\narg one: without; arg two: newline\narg one: withoutwhitespac"
},
{
"path": "test/conformance/2.2.2-nested-single-quotes.fail.sh",
"chars": 85,
"preview": "# 2.2.2 Single quotes\n# Single quotes cannot contain single quotes\nprintf '%s\\n' '''\n"
},
{
"path": "test/conformance/2.2.3-alias-expansion.fail.sh",
"chars": 130,
"preview": "# 2.2.3 Double quotes\n# Should NOT apply alias expansion rules when finding the )\nalias myalias=\"echo )\"\nvar=\"$(myalias "
},
{
"path": "test/conformance/2.2.3-backquote-nonterminated-dquote.undefined.sh",
"chars": 30,
"preview": "printf '%s\\n' \"cmd: `echo \"`\"\n"
},
{
"path": "test/conformance/2.2.3-backquote-nonterminated-squote.undefined.sh",
"chars": 30,
"preview": "printf '%s\\n' \"cmd: `echo '`\"\n"
},
{
"path": "test/conformance/2.2.3-dquote-nonterminated-backquote.undefined.sh",
"chars": 22,
"preview": "printf '%s\\n' \"`echo\"\n"
},
{
"path": "test/conformance/README",
"chars": 188,
"preview": "Tests in this directory test each specified behavior of a feature in the POSIX\nspecification. If mrsh passes such a test"
},
{
"path": "test/conformance/harness.sh",
"chars": 353,
"preview": "#!/bin/sh\ndir=$(dirname \"$0\")\ntestcase=\"$1\"\nstdout=\"${testcase%%.sh}.stdout\"\n\n# Set TEST_SHELL to quickly compare the co"
},
{
"path": "test/conformance/meson.build",
"chars": 1073,
"preview": "harness = find_program('./harness.sh')\n\ntest_files = [\n\t'2.2-quoted-characters.sh',\n]\n\nfailures = [\n\t'2.2.2-nested-singl"
},
{
"path": "test/for.sh",
"chars": 562,
"preview": "#!/bin/sh\n\necho \"Simple for loop\"\nfor i in 1 2 3; do\n\techo $i\ndone\n\necho \"Word expansion in for loop\"\ntwo=2\nfor i in 1 $"
},
{
"path": "test/function.sh",
"chars": 328,
"preview": "#!/bin/sh -e\nfunc_a() {\n\techo func_a\n}\n\nfunc_b() {\n\techo func_b\n}\n\nfunc_b() {\n\techo func_b revised\n}\n\nfunc_c() {\n\techo f"
},
{
"path": "test/harness.sh",
"chars": 461,
"preview": "#!/bin/sh\ndir=$(dirname \"$0\")\ntestcase=\"$1\"\n\necho \"Running with mrsh\"\nmrsh_out=$(\"$MRSH\" \"$testcase\")\nmrsh_ret=$?\necho \""
},
{
"path": "test/if.sh",
"chars": 1381,
"preview": "#!/bin/sh\n# Reference stdout:\n# pass\n# pass\n# pass\n# pass\n# pass\n# pass\n\necho \"-> if..fi with true condition\"\nif true\nth"
},
{
"path": "test/loop.sh",
"chars": 582,
"preview": "#!/bin/sh\n\necho \"basic while loop\"\nn=asdf\necho start\nwhile [ \"$n\" != \"fdsa\" ]; do\n\techo \"n: $n\"\n\tn=\"fdsa\"\n\techo \"n: $n\"\n"
},
{
"path": "test/meson.build",
"chars": 627,
"preview": "harness = find_program('./harness.sh')\nref_sh = find_program(get_option('reference-shell'), required: false)\n\ntest_files"
},
{
"path": "test/pipeline.sh",
"chars": 705,
"preview": "#!/bin/sh\n\necho \"Pipeline with 1 command\"\necho \"a b c d\"\n\necho \"Pipeline with 2 commands\"\necho \"a b c d\" | sed s/b/B/\n\ne"
},
{
"path": "test/read.sh",
"chars": 196,
"preview": "#!/bin/sh -eu\n\n\nprintf | read a\necho $?\n\ni=0\n\nprintf \"a\\nb\\nc\\n\" | while read line; do\n printf \"%s\\n\" \"${line:-blank}\"\n"
},
{
"path": "test/readonly.sh",
"chars": 230,
"preview": "#!/bin/sh\n\necho \"Print read-only parameters after setting one\"\nreadonly mrsh_readonly_param=b\nreadonly -p | grep mrsh_re"
},
{
"path": "test/redir.sh",
"chars": 136,
"preview": "#!/bin/sh\n\necho \"to stdout\"\nuname\n\necho >&2 \"stdout to stderr\"\nuname >&2\n\necho 2>&1 \"stderr to stdout\"\nuname 2>&1\n#(echo"
},
{
"path": "test/return.sh",
"chars": 305,
"preview": "#!/bin/sh -e\nfunc_a() {\n\techo func a\n\treturn\n\techo func a post-return\n}\n\nfunc_b() {\n\techo func b\n\treturn 1\n\techo func b "
},
{
"path": "test/subshell.sh",
"chars": 230,
"preview": "#!/bin/sh\n\necho \"Simple subshell\"\n(echo hi)\n\necho \"Subshell assignment\"\na=a\n(a=b)\necho $a\n\necho \"Subshell status\"\n(exit "
},
{
"path": "test/syntax.sh",
"chars": 143,
"preview": "#!/bin/sh\n\n# This is a comment\n\necho a b \t c\necho d # e f\necho 'a\tb c'\necho \"a\tb c\"\n\necho a\"*\"b'c\"'\necho \"a\\\"b\\\\\" \"'h"
},
{
"path": "test/ulimit.sh",
"chars": 316,
"preview": "#!/bin/sh -e\n\nmrsh_limits=`ulimit`\n[ \"$mrsh_limits\" = \"unlimited\" ]\nif [ -e /proc/self/limits ]\nthen\n\tgrep \"Max file siz"
},
{
"path": "test/word.sh",
"chars": 1847,
"preview": "#!/bin/sh\n\necho \"\"\necho \"Tilde Expansion\"\necho ~ ~/stuff ~/\"stuff\" \"~/stuff\"\necho '~/stuff' ~\"/stuff\" \"/\"~ \"a\"~\"a\"\necho "
}
]
About this extraction
This page contains the full source code of the emersion/mrsh GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 127 files (367.3 KB), approximately 115.2k tokens, and a symbol index with 1479 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.