summaryrefslogtreecommitdiff
path: root/docs/handbook/cgit/code-style.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/handbook/cgit/code-style.md')
-rw-r--r--docs/handbook/cgit/code-style.md356
1 files changed, 356 insertions, 0 deletions
diff --git a/docs/handbook/cgit/code-style.md b/docs/handbook/cgit/code-style.md
new file mode 100644
index 0000000000..d4059391dc
--- /dev/null
+++ b/docs/handbook/cgit/code-style.md
@@ -0,0 +1,356 @@
+# cgit — Code Style and Conventions
+
+## Overview
+
+cgit follows C99 conventions with a style influenced by the Linux kernel and
+Git project coding standards. This document describes the patterns, naming
+conventions, and idioms used throughout the codebase.
+
+## Language Standard
+
+cgit is written in C99, compiled with:
+
+```makefile
+CGIT_CFLAGS += -std=c99
+```
+
+No C11 or GNU extensions are required, though some platform-specific features
+(like `sendfile()` on Linux) are conditionally compiled.
+
+## Formatting
+
+### Indentation
+
+- Tabs for indentation (1 tab = 8 spaces display width, consistent with
+ Linux kernel/Git style)
+- No spaces for indentation alignment
+
+### Braces
+
+K&R style (opening brace on same line):
+
+```c
+if (condition) {
+ /* body */
+} else {
+ /* body */
+}
+
+static void function_name(int arg)
+{
+ /* function body */
+}
+```
+
+Functions place the opening brace on its own line. Control structures
+(`if`, `for`, `while`, `switch`) keep it on the same line.
+
+### Line Length
+
+No strict limit, but lines generally stay under 80 characters. Long function
+calls are broken across lines.
+
+## Naming Conventions
+
+### Functions
+
+Public API functions use the `cgit_` prefix:
+
+```c
+void cgit_print_commit(const char *rev, const char *prefix);
+void cgit_print_diff(const char *new_rev, const char *old_rev, ...);
+struct cgit_repo *cgit_add_repo(const char *url);
+struct cgit_repo *cgit_get_repoinfo(const char *url);
+int cgit_parse_snapshots_mask(const char *str);
+```
+
+Static (file-local) functions use descriptive names without prefix:
+
+```c
+static void config_cb(const char *name, const char *value);
+static void querystring_cb(const char *name, const char *value);
+static void process_request(void);
+static int open_slot(struct cache_slot *slot);
+```
+
+### Types
+
+Struct types use `cgit_` prefix with snake_case:
+
+```c
+struct cgit_context;
+struct cgit_repo;
+struct cgit_config;
+struct cgit_query;
+struct cgit_page;
+struct cgit_environment;
+struct cgit_cmd;
+struct cgit_filter;
+struct cgit_snapshot_format;
+```
+
+### Macros and Constants
+
+Uppercase with underscores:
+
+```c
+#define ABOUT_FILTER 0
+#define COMMIT_FILTER 1
+#define SOURCE_FILTER 2
+#define EMAIL_FILTER 3
+#define AUTH_FILTER 4
+#define DIFF_UNIFIED 0
+#define DIFF_SSDIFF 1
+#define DIFF_STATONLY 2
+#define FMT_BUFS 8
+#define FMT_SIZE 8192
+```
+
+### Variables
+
+Global variables use descriptive names:
+
+```c
+struct cgit_context ctx;
+struct cgit_repolist cgit_repolist;
+const char *cgit_version;
+```
+
+## File Organization
+
+### Header Files
+
+Each module has a corresponding header file with include guards:
+
+```c
+#ifndef UI_DIFF_H
+#define UI_DIFF_H
+
+extern void cgit_print_diff(const char *new_rev, const char *old_rev,
+ const char *prefix, int show_ctrls, int raw);
+extern void cgit_print_diffstat(const struct object_id *old,
+ const struct object_id *new,
+ const char *prefix);
+
+#endif /* UI_DIFF_H */
+```
+
+### Source Files
+
+Typical source file structure:
+
+1. License header (if present)
+2. Include directives
+3. Static (file-local) variables
+4. Static helper functions
+5. Public API functions
+
+### Module Pattern
+
+UI modules follow a consistent pattern with `ui-*.c` / `ui-*.h` pairs:
+
+```c
+/* ui-example.c */
+#include "cgit.h"
+#include "ui-example.h"
+#include "html.h"
+#include "ui-shared.h"
+
+static void helper_function(void)
+{
+ /* ... */
+}
+
+void cgit_print_example(void)
+{
+ /* main entry point */
+}
+```
+
+## Common Patterns
+
+### Global Context
+
+cgit uses a single global `struct cgit_context ctx` variable that holds all
+request state. Functions access it directly rather than passing it as a
+parameter:
+
+```c
+/* Access global context directly */
+if (ctx.repo && ctx.repo->enable_blame)
+ cgit_print_blame();
+
+/* Not: cgit_print_blame(&ctx) */
+```
+
+### Callback Functions
+
+Configuration and query parsing use callback function pointers:
+
+```c
+typedef void (*configfile_value_fn)(const char *name, const char *value);
+typedef void (*filepair_fn)(struct diff_filepair *pair);
+typedef void (*linediff_fn)(char *line, int len);
+typedef void (*cache_fill_fn)(void *cbdata);
+```
+
+### String Formatting
+
+The `fmt()` ring buffer is used for temporary string construction:
+
+```c
+const char *url = fmt("%s/%s/", ctx.cfg.virtual_root, repo->url);
+html_attr(url);
+```
+
+Never store `fmt()` results long-term — use `fmtalloc()` or `xstrdup()`.
+
+### NULL Checks
+
+Functions generally check for NULL pointers at the start:
+
+```c
+void cgit_print_blob(const char *hex, const char *path,
+ const char *head, int file_only)
+{
+ if (!hex && !path) {
+ cgit_print_error_page(400, "Bad request",
+ "Need either hex or path");
+ return;
+ }
+ /* ... */
+}
+```
+
+### Memory Management
+
+cgit uses Git's `xmalloc` / `xstrdup` / `xrealloc` wrappers that die on
+allocation failure:
+
+```c
+char *name = xstrdup(value);
+repo = xrealloc(repo, new_size);
+```
+
+No explicit `free()` calls in most paths — the CGI process exits after each
+request, and the OS reclaims all memory.
+
+### Boolean as Int
+
+Boolean values are represented as `int` (0 or 1), consistent with C99
+convention before `_Bool`:
+
+```c
+int enable_blame;
+int enable_commit_graph;
+int binary;
+int match;
+```
+
+### Typedef Avoidance
+
+Structs are generally not typedef'd — they use the `struct` keyword
+explicitly:
+
+```c
+struct cgit_repo *repo;
+struct cache_slot slot;
+```
+
+Exception: function pointer typedefs are used for callbacks:
+
+```c
+typedef void (*configfile_value_fn)(const char *name, const char *value);
+```
+
+## Error Handling
+
+### `die()` for Fatal Errors
+
+Unrecoverable errors use Git's `die()`:
+
+```c
+if (!ctx.repo)
+ die("no repository");
+```
+
+### Error Pages for User Errors
+
+User-facing errors use the error page function:
+
+```c
+cgit_print_error_page(404, "Not Found",
+ "No repository found for '%s'",
+ ctx.qry.repo);
+```
+
+### Return Codes
+
+Functions that can fail return int (0 = success, non-zero = error):
+
+```c
+static int open_slot(struct cache_slot *slot)
+{
+ slot->cache_fd = open(slot->cache_name, O_RDONLY);
+ if (slot->cache_fd == -1)
+ return errno;
+ return 0;
+}
+```
+
+## Preprocessor Usage
+
+Conditional compilation for platform features:
+
+```c
+#ifdef HAVE_LINUX_SENDFILE
+ sendfile(STDOUT_FILENO, slot->cache_fd, &off, size);
+#else
+ /* read/write fallback */
+#endif
+
+#ifdef HAVE_LUA
+ /* Lua filter support */
+#endif
+```
+
+## Git Library Integration
+
+cgit includes Git as a library. It uses Git's internal APIs directly:
+
+```c
+#include "git/cache.h"
+#include "git/object.h"
+#include "git/commit.h"
+#include "git/diff.h"
+#include "git/revision.h"
+#include "git/archive.h"
+```
+
+Functions from Git's library are called without wrapper layers:
+
+```c
+struct commit *commit = lookup_commit_reference(&oid);
+struct tree *tree = parse_tree_indirect(&oid);
+init_revisions(&rev, NULL);
+```
+
+## Documentation
+
+- Code comments are used sparingly, mainly for non-obvious logic
+- No Doxygen or similar documentation generators are used
+- Function documentation is in the header files as prototypes with
+ descriptive parameter names
+- The `cgitrc.5.txt` file provides user-facing documentation in
+ man page format
+
+## Commit Messages
+
+Commit messages follow the standard Git format:
+
+```
+subject: brief description (50 chars or less)
+
+Extended description wrapping at 72 characters. Explain what and why,
+not how.
+```