#sframe — CLAUDE.md
#Project
sframe (Simple Frame) is a lightweight command-line tool written in C
following the suckless philosophy. It extracts unique frames from video
files for research purposes, using perceptual hashing to eliminate
duplicates and embedding timestamps in filenames.
#Coding Standards — Suckless C Style
All code in this project MUST follow the suckless.org coding style:
#Language
- C99 (ISO/IEC 9899:1999), no extensions
- POSIX.1-2008 (
_POSIX_C_SOURCE 200809L)
#Indentation & Whitespace
- Tabs for indentation (1 tab = 1 level)
- Spaces for alignment only, never for indentation
- No tabs except at the beginning of a line
- Maximum line length: 79 characters
- Use
/* */ only, never //
- Comment fallthrough cases in switch statements
#Variables
- All declarations at the top of the block
- Pointer
* adjacent to variable name: char *p, not char* p
- No C99
bool; use int (0/1)
- Global/static variables not used outside TU must be
static
#Functions
- Return type on its own line
- Function name at column 0 on next line (enables
grep ^funcname)
- Opening
{ on its own line for functions
- Functions not used outside their file:
static
static void
usage(void)
{
fprintf(stderr, "usage: sframe [-t thresh] [-f fmt] video\n");
exit(1);
}
#Braces
- Opening
{ on same line for control flow (if, for, while, switch)
- Closing
} on its own line unless continuing (else, do-while)
- Use braces even for single statements when sibling branches use them
#Naming
- lowercase_with_underscores for functions and variables
- UPPERCASE for macros and constants
- CamelCase for typedef'd struct types
- No
_t suffix (reserved by POSIX)
- Prefix module functions:
dec_open(), diff_hash(), util_die()
#Control Flow
- Space after
if, for, while, switch
- No space after
( or before )
- Use
goto for cleanup/unwind, not nested ifs
- Return/exit early on failure
- Test against 0, not -1:
if (func() < 0)
#Error Handling
- All allocation checked; goto cleanup on failure
die() for fatal errors (prints message, exits)
warn() for recoverable errors (prints, continues)
#File Organization Order
- License header
- System includes (alphabetical)
- Local includes
- Macros
- Type definitions
- Function declarations
- Global variables
- Function definitions (same order as declarations)
- System headers first, alphabetical
- Local headers after blank line
- No cyclic dependencies
- Include only what is needed
#Architecture Rules
- No global mutable state. Pass context structs explicitly.
- No
system() calls. Use fork()/execvp() or popen().
- No hardcoded paths except sensible defaults (
~/frames).
- Separate compilation. Every .c file compiles independently.
#Module Prefixes
| Module |
Prefix |
File |
| Main/CLI |
— |
main.c |
| Decode |
dec_ |
decode.c |
| Diff |
diff_ |
diff.c |
| Utilities |
die(), warn(), ecalloc() |
util.c |
#Build
make # build sframe binary
make clean # remove build artifacts
make install PREFIX=/usr/local # install
#Dependencies
- FFmpeg libraries: libavformat, libavcodec, libavutil, libswscale
#Git Conventions
- No
Co-Authored-By: Claude lines
- Commit messages: imperative, <72 chars, no period
- One logical change per commit
#CRITICAL: No Building on Moirai
NEVER run make, sudo make install, or any build command unless
Kris explicitly says "build" in his message.