From f470fbf714952b9e4ac691eb8f3da85844b17170 Mon Sep 17 00:00:00 2001 From: melogtm Date: Sat, 11 Jul 2026 11:43:52 -0300 Subject: [PATCH] clean: trim .gitignore to project-relevant patterns, add CLAUDE.md Removes 100+ lines of irrelevant patterns (kernel modules, JetBrains/ IntelliJ, Android Studio, Gradle, etc.) and adds project-specific entries: build directories, CMake generated files, editor/OS noise. Also adds CLAUDE.md (AI assistant instructions) to version control. --- .gitignore | 150 +++++++++++------------------------------------------ CLAUDE.md | 100 +++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 121 deletions(-) create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index f34bbb6..44560e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,143 +1,51 @@ -# Prerequisites -*.d +# Build directories +cmake-build-*/ +build/ # 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 +*.lib *.so *.so.* *.dylib +*.dll # Executables *.exe *.out *.app -*.i*86 -*.x86_64 -*.hex + +# Precompiled headers +*.gch +*.pch # Debug files *.dSYM/ -*.su -*.idb *.pdb +*.idb +*.ilk -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf - -# debug information files -*.dwo - -# Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ -.idea/sonarlint.xml # see https://community.sonarsource.com/t/is-the-file-idea-idea-idea-sonarlint-xml-intended-to-be-under-source-control/121119 - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based HTTP Client -.idea/httpRequests -http-client.private.env.json - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -# Apifox Helper cache -.idea/.cache/.Apifox_Helper -.idea/ApifoxUploaderProjectSetting.xml - -# Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215 -.idea/**/copilot.data.migration.*.xml - +# Editor / IDE +*.swp +*.swo +*~ +.vscode/ +.idea/ + +# OS +.DS_Store +Thumbs.db + +# CMake generated +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +Makefile +CTestTestfile.cmake +compile_commands.json diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0ac4f50 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,100 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project overview + +Trigger is a small POSIX shell written in C (C11), originally adapted from Brennan +Baker's "Write a Shell in C" tutorial and built with CMake. It now supports quoting, +pipelines, I/O redirection, and glob expansion on top of the tutorial's core loop. + +## Build, run, test + +Build: + cmake -B cmake-build-debug -S . + cmake --build cmake-build-debug + +Run the shell: + ./cmake-build-debug/shell + +Run the full test suite: + ./run_tests.sh + # equivalent to: cmake -B/--build, then `cd cmake-build-debug && ctest --output-on-failure` + +Run a single test module (CTest test names: InputTests, BuiltinsTests, ExecuteTests, +PipelineTests, GlobTests): + cd cmake-build-debug && ctest -R PipelineTests --output-on-failure + +Or run a test binary directly, e.g.: + ./cmake-build-debug/test_pipeline + +Lint (also enforced in CI as a separate `lint` job, see below): + clang-format --dry-run --Werror $(find src include tests -name '*.c' -o -name '*.h') + cmake -B cmake-build-debug -S . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + clang-tidy -p cmake-build-debug -warnings-as-errors=* $(find src include -name '*.c') + +CI (`.github/workflows/ci.yml`) runs on every PR and push to `main` with two jobs: +`build-and-test` (`./run_tests.sh`) and `lint` (clang-format + clang-tidy, both +warnings-as-errors). Formatting follows `.clang-format` (LLVM base, 4-space indent, +100 col limit); tidy checks are configured in `.clang-tidy`. A tag matching `v*.*.*` +triggers `.github/workflows/release.yml`, which builds a Release binary and attaches +it to a GitHub release. + +## Architecture + +Data flow: `src/main.c` runs `trigger_loop()`, a read → split → execute → free REPL +loop. Each iteration reads a line, tokenizes it via `trigger_split_line_ex()` +(which also produces a parallel `glob_eligible` array), and hands both to +`trigger_execute()`. + +- **Input/lexer** (`src/input.c`, `src/utils/utils.c`): `trigger_read_line()` reads + a raw line via `getline`. `trigger_split_line_ex()` delegates to + `parse_line_with_quotes()` in `src/utils/utils.c`, a hand-written state-machine + tokenizer (`ParserState`: NORMAL / IN_SINGLE_QUOTE / IN_DOUBLE_QUOTE / ESCAPED) + that handles single/double/backslash quoting and grows token/array buffers + dynamically. This is the most complex and previously-buggy part of the codebase — + trace through `process_character()` in `utils.c` before touching quote-parsing + behavior. On unclosed quotes it returns `NULL` rather than crashing; callers must + handle that. Alongside each token, it fills a parallel `glob_eligible` int array: + a token is glob-ineligible if any part of it came from inside quotes or from a + backslash escape, so `echo "*.c"` and `echo \*.c` are never expanded even though + `echo *.c` is. Downstream code must keep `tokens[i]` and `glob_eligible[i]` in + lockstep — they're always the same length and freed/reallocated together. +- **Glob expansion** (`src/glob.c`): `expand_globs()` runs after tokenizing and + before pipeline/operator parsing. It only expands tokens marked glob-eligible and + containing `*`, `?`, or `[`, using libc `glob()` with `GLOB_NOCHECK` (an unmatched + pattern is passed through literally, shell-style). It rebuilds both the argv and + glob_eligible arrays since one input token can expand to N output tokens. +- **Executor** (`src/execute.c`): `trigger_execute()` expands globs first, then + scans the (glob-eligible-aware) token stream for `|`, `<`, `>`, `>>`. If any are + present as real operators (not inside quotes), it hands off to + `trigger_parse_pipeline()` / `trigger_execute_pipeline()` in `src/pipeline.c`. + Otherwise it checks `args[0]` against the builtins table directly and falls + through to `trigger_launch()` (fork() + execvp() + waitpid()) for external + commands. A bare command with no operators never touches pipeline.c. +- **Pipelines & redirection** (`src/pipeline.c`): `trigger_parse_pipeline()` splits + the token stream on `|` into `PipelineStage` structs (`argv`, `infile`, `outfile`, + `append`), consuming `<`/`>`/`>>` and their filename arguments out of each stage's + argv. `trigger_execute_pipeline()` has a fast path for a single stage with no + redirection (checks builtins, else `trigger_launch()`), a path for a single + builtin stage *with* redirection (saves/restores fds 0/1 around the builtin call + via `dup`/`dup2` since builtins run in-process), and a general N-stage path that + forks one child per stage, wires up `pipe()` fds between adjacent stages, and + waits on all children. In the N-stage path, builtins run inside the forked child + and `exit()` immediately after — only the single-stage paths run a builtin + in-process without forking. +- **Builtins** (`src/builtins.c`): dispatch is two parallel arrays kept in lockstep + by hand — `builtin_str[]` (names: cd, help, exit, pwd, echo, export, unset) and + `builtin_func[]` (matching function pointers), iterated together by index in + `trigger_execute()`, `trigger_execute_pipeline()`, and `trigger_help()`. Adding a + builtin means appending to *both* arrays in the same position — there's no + struct/table enforcing this, so mismatches are easy to introduce. + +Headers in `include/` mirror `src/` (`input.h`, `execute.h`, `builtins.h`, +`pipeline.h`, `glob_expand.h`), except `utils/utils.h`, which lives next to its +`.c` file in `src/utils/` rather than in `include/`. + +Tests in `tests/` are per-module (`test_input.c`, `test_builtins.c`, +`test_execute.c`, `test_pipeline.c`, `test_glob.c`) and link directly against the +relevant `.c` files rather than a shared static library, using a small custom +assertion framework in `tests/test_framework.h` (no external test dependency).