first commit
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Dávid Ali
|
||||
|
||||
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.
|
||||
101
README.md
Normal file
101
README.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# cflat (c♭)
|
||||
|
||||
**Make your C++ project fall flat.**
|
||||
|
||||
## What is this?
|
||||
|
||||
`cflat` takes your complex directory structure and exports it into a single text file. It merges your entire C/C++ project into one massive stream of code.
|
||||
|
||||
**Note:** It does not actually delete your folders. It just ignores them in the output. Your architecture is safe.
|
||||
|
||||
## Why?
|
||||
|
||||
Civilized people keep files separate. You, apparently, need them together.
|
||||
|
||||
* **AI Fodder:** Feed your entire codebase to an LLM so it can hallucinate a fix for that segfault you've been ignoring.
|
||||
* **Code Reviews:** Send a single 50,000-line file to your reviewer. Assert dominance.
|
||||
* **Grepping:** Search for a string across the project instantly because you still haven't memorized how to use `grep -r`.
|
||||
* **Spite:** Annoy your lead architect by rendering their carefully crafted folder structure irrelevant.
|
||||
|
||||
## Features
|
||||
|
||||
* **Recursive:** It hunts down source files in every nested folder. Deep sub-directories are no longer safe places to hide bad code.
|
||||
* **Fake Elegance:** It runs `clang-format` (if installed) so your spaghetti code at least *looks* professional in the export.
|
||||
* **Hygiene:** It ignores `obj`, `bin`, `.git`, and other binary garbage that nobody wants to read.
|
||||
* **Self-Preservation:** It automatically adds itself and the output file to `.gitignore`. This prevents you from accidentally committing a 12MB text file.
|
||||
* **Flexible:** Run it on the current folder, a target folder, or exclude specific shameful directories.
|
||||
|
||||
## Installation
|
||||
|
||||
Trusting random scripts from the internet is a key part of the developer experience.
|
||||
|
||||
```bash
|
||||
curl -O https://git.alidavid.hu/david/cflat/raw/branch/main/cflat
|
||||
```
|
||||
|
||||
Make it executable:
|
||||
|
||||
```bash
|
||||
chmod +x cflat
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
**Default Behavior:**
|
||||
Run this in your project root to flatten everything.
|
||||
|
||||
```bash
|
||||
./cflat
|
||||
```
|
||||
|
||||
**Target a specific folder:**
|
||||
|
||||
```bash
|
||||
./cflat ../my-other-project/
|
||||
```
|
||||
|
||||
**The "Denial" option (Exclusions):**
|
||||
Exclude folders you don't want the world (or the AI) to see.
|
||||
|
||||
```bash
|
||||
./cflat -e build -e node_modules -e "legacy_code"
|
||||
```
|
||||
|
||||
### ⚠️ Wildcards (Important)
|
||||
|
||||
Your shell expands `*` before the script sees it. If you don't use quotes, the script will break.
|
||||
|
||||
```bash
|
||||
# Good:
|
||||
./cflat -e "*.tmp"
|
||||
|
||||
# Bad (The shell expands this and confuses the script):
|
||||
./cflat -e *.tmp
|
||||
```
|
||||
|
||||
**Need help?**
|
||||
|
||||
```bash
|
||||
./cflat -?
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
The script generates a file named `cflat-export-YYYY-MM-DD.txt`. It contains:
|
||||
|
||||
1. **Project Stats:** How many lines of code you are dealing with.
|
||||
2. **The Tree:** A visual map of the directory structure.
|
||||
3. **The Cast:** A list of all classes and structs found.
|
||||
4. **The Blob:** The full content of every source file, stitched together.
|
||||
|
||||
## Requirements
|
||||
|
||||
* **OS:** Linux or macOS. (Windows users: please use WSL).
|
||||
* **Shell:** Bash.
|
||||
* **Optional:**
|
||||
* `tree`: For structure visualization.
|
||||
* `clang-format`: For code cleaning.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
147
cflat
Executable file
147
cflat
Executable file
@@ -0,0 +1,147 @@
|
||||
#!/bin/bash
|
||||
|
||||
# --- cflat (c♭) ---
|
||||
# Flattens C/C++ projects into a single text file.
|
||||
# https://git.alidavid.hu/david/cflat
|
||||
|
||||
VERSION="1.0.0"
|
||||
SCRIPT_NAME=$(basename "$0")
|
||||
|
||||
# 1. HELP FUNCTION
|
||||
function show_help {
|
||||
echo "cflat (c♭) v$VERSION"
|
||||
echo "Flattens C/C++ projects into a single text file."
|
||||
echo "https://git.alidavid.hu/david/cflat"
|
||||
echo ""
|
||||
echo "Usage: ./$SCRIPT_NAME [DIRECTORY] [-e PATTERN]..."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -e, --exclude Exclude exact path relative to the target directory."
|
||||
echo " 'aaa.a' -> Excludes ./aaa.a (Target root only)"
|
||||
echo " 'src/lib' -> Excludes ./src/lib (and its contents)"
|
||||
echo " '*.txt' -> Excludes ./*.txt (Target root only)"
|
||||
echo " '*/*.txt' -> Excludes ./*/*.txt (Recursive)"
|
||||
echo " -h, --help, -? Show this message"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 2. ARGUMENT PARSING
|
||||
TARGET_DIR="."
|
||||
EXCLUDE_ARGS=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h|--help|-\?) show_help ;;
|
||||
-e|--exclude)
|
||||
if [[ -n "$2" && "$2" != -* ]]; then
|
||||
# Clean trailing slash from pattern to match find output safely
|
||||
CLEAN_PAT="${2%/}"
|
||||
EXCLUDE_ARGS+=("$CLEAN_PAT")
|
||||
shift 2
|
||||
else
|
||||
echo "Error: --exclude requires an argument." >&2; exit 1
|
||||
fi
|
||||
;;
|
||||
-*) echo "Error: Unknown option $1" >&2; exit 1 ;;
|
||||
*) TARGET_DIR="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Clean target directory (remove trailing slash)
|
||||
TARGET_DIR=${TARGET_DIR%/}
|
||||
|
||||
# 3. BUILD FIND COMMAND
|
||||
# We anchor every exclusion to the TARGET_DIR.
|
||||
# This ensures that exclusions are strict (e.g., 'aaa.a' only matches the root file).
|
||||
|
||||
# Default ignores (Hidden files, obj, bin, build, and our own output)
|
||||
BASE_IGNORES=(
|
||||
-path '*/.*' -o
|
||||
-path '*/obj' -o
|
||||
-path '*/bin' -o
|
||||
-path '*/build' -o
|
||||
-name "cflat-*.txt" -o
|
||||
-name "$SCRIPT_NAME"
|
||||
)
|
||||
|
||||
# Build User Excludes
|
||||
USER_PRUNE_ARGS=()
|
||||
if [[ ${#EXCLUDE_ARGS[@]} -gt 0 ]]; then
|
||||
for pattern in "${EXCLUDE_ARGS[@]}"; do
|
||||
# Construct the strict path match anchored to TARGET_DIR
|
||||
# e.g., if target is "." and pattern is "file", we match "./file"
|
||||
MATCH_PATH="$TARGET_DIR/$pattern"
|
||||
|
||||
USER_PRUNE_ARGS+=("-path" "$MATCH_PATH" "-o")
|
||||
done
|
||||
fi
|
||||
|
||||
# Combine logic: ( UserExcludes OR BaseIgnores ) -prune
|
||||
FINAL_PRUNE_ARGS=("(" "${USER_PRUNE_ARGS[@]}" "${BASE_IGNORES[@]}" ")" "-prune")
|
||||
|
||||
# 4. CONFIGURATION
|
||||
TIMESTAMP=$(date +"%Y-%m-%dT%H-%M-%S")
|
||||
OUTPUT="cflat-export-$TIMESTAMP.txt"
|
||||
MAX_SIZE_KB=100
|
||||
HAS_CLANG=$(command -v clang-format)
|
||||
|
||||
# 5. HEADER
|
||||
echo "Generating export for: $TARGET_DIR"
|
||||
echo "C-FLAT EXPORT" > "$OUTPUT"
|
||||
echo "Generated on: $(date)" >> "$OUTPUT"
|
||||
echo "----------------------------------------" >> "$OUTPUT"
|
||||
|
||||
# 6. STATISTICS
|
||||
echo "PROJECT STATISTICS:" >> "$OUTPUT"
|
||||
find "$TARGET_DIR" "${FINAL_PRUNE_ARGS[@]}" -o -type f \( -name "*.cpp" -o -name "*.hpp" -o -name "*.c" -o -name "*.h" \) -print0 | xargs -0 -r wc -l | tail -n 1 >> "$OUTPUT"
|
||||
|
||||
# 7. KEY DEFINITIONS
|
||||
echo -e "\nKEY DEFINITIONS FOUND:" >> "$OUTPUT"
|
||||
find "$TARGET_DIR" "${FINAL_PRUNE_ARGS[@]}" -o -type f -exec grep -nE "class [A-Za-z0-9_]+|struct [A-Za-z0-9_]+" {} + >> "$OUTPUT"
|
||||
|
||||
# 8. PROJECT STRUCTURE
|
||||
echo -e "\nPROJECT STRUCTURE:" >> "$OUTPUT"
|
||||
# Print all non-pruned paths for the tree visualization
|
||||
find "$TARGET_DIR" "${FINAL_PRUNE_ARGS[@]}" -o -print | sort | sed -e "s;[^/]*/;|____;g;s;____|; |;g" >> "$OUTPUT"
|
||||
|
||||
echo -e "\n--- FILE CONTENTS ---\n" >> "$OUTPUT"
|
||||
|
||||
# 9. CONTENT EXPORT
|
||||
find "$TARGET_DIR" "${FINAL_PRUNE_ARGS[@]}" -o -type f -print | while read -r file; do
|
||||
|
||||
if [[ "$file" =~ \.(cpp|hpp|h|c|cc|cxx)$ ]]; then
|
||||
echo "--- START OF SOURCE FILE: $file ---" >> "$OUTPUT"
|
||||
if [ -n "$HAS_CLANG" ]; then
|
||||
clang-format --style=LLVM "$file" >> "$OUTPUT"
|
||||
else
|
||||
cat "$file" >> "$OUTPUT"
|
||||
fi
|
||||
echo -e "\n--- END OF SOURCE FILE: $file ---\n" >> "$OUTPUT"
|
||||
|
||||
elif [[ "$file" == *"Makefile"* ]] || [[ "$file" =~ \.(txt|py|sh|md|cmake)$ ]]; then
|
||||
echo "--- START OF CONFIG/SCRIPT: $file ---" >> "$OUTPUT"
|
||||
cat "$file" >> "$OUTPUT"
|
||||
echo -e "\n--- END OF CONFIG/SCRIPT: $file ---\n" >> "$OUTPUT"
|
||||
|
||||
elif grep -qI . "$file"; then
|
||||
FILE_SIZE=$(du -k "$file" | cut -f1)
|
||||
if [ "$FILE_SIZE" -lt "$MAX_SIZE_KB" ]; then
|
||||
echo "--- START OF ASSET FILE: $file ---" >> "$OUTPUT"
|
||||
cat "$file" >> "$OUTPUT"
|
||||
echo -e "\n--- END OF ASSET FILE: $file ---\n" >> "$OUTPUT"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 10. AUTO-GITIGNORE
|
||||
GITIGNORE="$TARGET_DIR/.gitignore"
|
||||
if [ -f "$GITIGNORE" ]; then
|
||||
if ! grep -qxF "$SCRIPT_NAME" "$GITIGNORE"; then
|
||||
echo -e "\n# C-Flat ignores\n$SCRIPT_NAME" >> "$GITIGNORE"
|
||||
fi
|
||||
if ! grep -qF "cflat-*.txt" "$GITIGNORE"; then
|
||||
echo "cflat-*.txt" >> "$GITIGNORE"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Done: Output saved to $OUTPUT"
|
||||
Reference in New Issue
Block a user