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