From 247e469f82a00cdb76da81a46b5f1d52b63460c3 Mon Sep 17 00:00:00 2001 From: Emma Tebibyte Date: Thu, 3 Apr 2025 16:45:22 -0600 Subject: [PATCH 1/2] CONTRIBUTING, STYLE: merge into CONTRIBUTING --- CONTRIBUTING | 265 ++++++++++++++++++++++++++++++++++++++++++++++++--- STYLE | 254 ------------------------------------------------ 2 files changed, 253 insertions(+), 266 deletions(-) delete mode 100644 STYLE diff --git a/CONTRIBUTING b/CONTRIBUTING index 72094c3..b768bf0 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -99,17 +99,247 @@ notice: Style ===== -Make sure lines never exceed 80 columns in width when using four-character -indentation steps. This helps contributors with smaller screens, those using -side-by-side editor windows or panes, and those who have no text wrapping in -their editor or terminal. +“Everyone knows that debugging is twice as hard as writing a program in the +first place. So if you’re as clever as you can be when you write it, how +will you ever debug it?” + – Brian Kernighan, The Elements of Programming Style -For usage text and help messages, do not implement a -h option. Instead, print -usage information when any erroneous option is specified. Follow the NetBSD -style guide for the usage text’s output format [0]. +The following guidelines are conducive to clear and readable code that is +consistent with the style of the rest of the Bonsai Computer System. + +Use: + + 0. A single line for control flow statements short enough to be easily + understood at a glance: + + if !(argc < 0) { usage(program_name); } + + This applies to C switch statements and cases and Rust match statements, as + well: + + switch (value) { /* aligning stuff to make it easier to read is fine */ + case possibility: variable = foo; break; + default: variable = NULL; break; + } + + 1. Switch cases in C and match arms in Rust should start another level of + indentation: + + switch (value) { + case possibility: + statement; + break; + default: + statement; + break; + } + + match result { + Ok(n) => variable = n, + Err(e) => error = e, + } + + 2. Braces in control flow where their inclusion is left optional in C: + + if (condition) { statement; } + + 3. Empty lines between different kinds of statements: + + int t; + + assert(io->bufuse > 0); + assert(io->bufuse <= io->bs); + + if ((t = write(io->fd, io->buf, io->bufuse)) < 0) { + io->error = errno; + t = 0; + } else if (t > 0) { + memmove(io->buf, &(io->buf)[t], (io->bufuse -= t)); + } + + io->bytes += t; + io->prec += (t > 0 && io->bufuse > 0); + io->rec += (t > 0 && io->bufuse == 0); + + return io; + + 4. Compiler options that yield the most useful warnings, such as -Wpedantic in + a lot of C compilers. Fix the warnings, too [0]. + + 5. One more level of indentation and one argument per line when a function + call or statement header is too long to fit on one line: + let usage = format!( + "Usage: {} [-d delimiter] index command [args...]", + argv[0], + ); + + 6. One more level of indentation than the keyword that initiated a multi-line + block. + + if (condition) { + statement; + statement; + } + + 7. The return value of all non-void functions, or explicitly ignore them (like + casting to void in C) [0]: + + if ((a = malloc(sizeof char)) == NULL) { /* handle this error */ + (void)fprintf(stderr, "oh noes!"); /* explicitly ignore this one */ + return EX_OSERR; /* ...because the program is exiting anyway */ + } + + 8. The smallest possible scope for data [0]. + + 9. Comments noting all the symbols and macros used from a C header file, next + to its include macro: + + #include /* close(2), getopt(3), lseek(2), read(2), write(2), + (space-aligned) * optarg, optind, STDIN_FILENO, STDOUT_FILENO */ + + 10. Spaces in control flow statements, after the keyword and before the + opening brace: + + for (i = 2; i < argc; ++i) { + + + 11. In Rust, a trailing comma on all arguments or fields that are on their own + lines: + + return Err(EvaluationError { + message: format!("{}: Invalid token", i), + code: EX_DATAERR, + }) + + 12. In Rust, place extern statements after use statements that include standard + library crates. Group like statements: + + use std::fs::Path; + + extern crate strerror; + extern crate sysexits; + + use strerror::StrError; + use sysexits::{ EX_OSERR, EX_USAGE }; + + 13. If text is on the same line as a brace, spaces after an opening brace and + before a closing one: + + use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE }; + + 14. Alphabetic sorting, where applicable: + + use std::io::{ BufWriter, Read, Write, stderr, stdin, stdout } + + 15. In Rust, use the to_owned() method on string types (str, OsStr, CStr, etc.) + and the to_string() method on other types. + + +Avoid: + + 16. Unbounded loops [0]. + + 17. Function pointers [0]. + + 18. Heap memory allocation [0]. + + 19. Using too much nested logic (within reason). + + 20. Too many levels of dereferences [0]: + + /* do not do this */ + for (size_t i = 0; i < sizeof a / sizeof *a; ++i) { + if (a[i].id == MATCH) { a[i].val = 0; } + } + + /* do this */ + for (struct MadeUp *s = &a[0]; *s != NULL; s = &s[1]) { + if (s->id == MATCH) { s->val = 0; } + } + + 21. Using C preprocessor macros; the fewer, the better [0]. + + 22. The exit(3p) and std::process::exit() functions; returning from the main + function skips a system call. + + +Do not use: + + 23. More than the length of one printed page for a function [0]. + + 24. Recursion, as it’s complex and can unexpectedly overflow the stack [0]. + + 25. Any functionality not in the POSIX C specification and language features not + in C99. + + 26. Do-while loops, as they’re unique to C and confusing for casual programmers. + + 27. Labels and goto statements; use sensible flow control [0]. + + 28. Pointer arithmetic, as it tends to be confusing and unnecessary; use + index-reference patterns like &p[1] instead of p + 1. &p[n] is the address at + p + sizeof p * n, not p + n, like pointer arithmetic suggests. + + 29. C struct bitfields in unions, to access certain bits of bigger data types, + as it’s poorly defined in the C standards; use bit arithmetic. + + 30. C trigraphs. + + 31. Inclusions in C header files, to prevent multiple file inclusions. + + 32. C preprocessor variables to prevent multiple inclusions of the same file, + such as: + + #ifdef _FILE + #define _FILE + /* file body */ + #endif /* ifdef _FILE */ + + Instead, take the time to ensure other files aren’t including any files twice. + + 33. The gets(3p) function from , as it’s impossible to prevent buffer + overflows when it's used; use fgets(3p) from . + + 34. The scanf(3p) function from [1]. + + 35. Any functionality not described in the latest POSIX make(1) specification. + + 36. Macros which panic on failure in Rust (such as the print!() and println!() + macros). Use a function and handle any errors. However, do use the eprintln!() + macro for error messages. Handling an error for writing an error message is + redundant. + + 37. A -h option for help text. Instead, print usage information when any + erroneous option is specified. See the Usage Text section below. + + 38. Lines which exceed 80 columns in width when using four-column indentation + steps. This helps contributors with smaller screens, those using side-by-side + editor windows or panes, and those who have no text wrapping in their editor or + terminal. + + +Usage Text +========== + +This section is adapted from the NetBSD style guide [2]. + +When programs are invoked incorrectly and in the synopsis of manual pages, uasge +text should be provided to the user. The following is the format used by this +project for this purpose: + +All optional arguments are to be placed in square brackets (U+005B, U+005D). +Mutually exclusive arguments can be separated by a vertical line (U+007C). +Groups of arguments should be specified in alphabetical order in most cases. The +order of arguments and an example of these rules follows: + + 0. Options with no option arguments. + 1. Options with option arguments. Arguments should be specified inside the same + square brackets as the options. + 3. Non-option arguments. + + "usage: f [-aDde] [-b b_arg] [-m m_arg] req1 req2 [opt1 [opt2]]\n" + "usage: f [-a | -b] [-c [-de] [-n number]]\n" -If committing a new utility, please include tests and documentation (see -tests/ and docs/) for the new tool. Committing ========== @@ -117,6 +347,10 @@ Committing When contributing to Bonsai, please sign your commit with a PGP key and create the commit with an identity which can be easily contacted. +If committing a new utility, please include tests and documentation (see +tests/ and docs/) for the new tool. + + Format commit messages following these guidelines: $ git commit -m 'tool(1): add feature x' @@ -147,9 +381,16 @@ Commit messages should be written in the present tense. References ========== -[0] +[0] +[1] +[2] -- -This work © 2023–2024 by Emma Tebibyte is licensed under CC BY-SA 4.0. To view a -copy of this license, visit + +Copyright © 2023–2024 Emma Tebibyte +Copyright © 2024 DTB +Copyright © Wikipedia contributors + +This work is licensed under CC BY-SA 4.0. To view a copy of this license, visit +. diff --git a/STYLE b/STYLE deleted file mode 100644 index 1e6dadb..0000000 --- a/STYLE +++ /dev/null @@ -1,254 +0,0 @@ -“Everyone knows that debugging is twice as hard as writing a program in the -first place. So if you’re as clever as you can be when you write it, how -will you ever debug it?” - – Brian Kernighan, The Elements of Programming Style - - -The following guidelines are conducive to clear and readable code that is -consistent with the style of the rest of the Bonsai Computer System. - - -Use -=== - - 0. A single line for control flow statements short enough to be easily - understood at a glance: - - if !(argc < 0) { usage(program_name); } - - This applies to C switch statements and cases and Rust match statements, as - well: - - switch (value) { /* aligning stuff to make it easier to read is fine */ - case possibility: variable = foo; break; - default: variable = NULL; break; - } - - 1. Switch cases in C and match arms in Rust should start another level of - indentation: - - switch (value) { - case possibility: - statement; - break; - default: - statement; - break; - } - - match result { - Ok(n) => variable = n, - Err(e) => error = e, - } - - 2. Braces in control flow where their inclusion is left optional in C: - - if (condition) { statement; } - - 3. Empty lines between different kinds of statements: - - int t; - - assert(io->bufuse > 0); - assert(io->bufuse <= io->bs); - - if ((t = write(io->fd, io->buf, io->bufuse)) < 0) { - io->error = errno; - t = 0; - } else if (t > 0) { - memmove(io->buf, &(io->buf)[t], (io->bufuse -= t)); - } - - io->bytes += t; - io->prec += (t > 0 && io->bufuse > 0); - io->rec += (t > 0 && io->bufuse == 0); - - return io; - - 4. Compiler options that yield the most useful warnings, such as -Wpedantic in - a lot of C compilers. Fix the warnings, too [0]. - - 5. One more level of indentation and one argument per line when a function - call or statement header is too long to fit on one line: - - let usage = format!( - "Usage: {} [-d delimiter] index command [args...]", - argv[0], - ); - - 6. One more level of indentation than the keyword that initiated a multi-line - block. - - if (condition) { - statement; - statement; - } - - 7. The return value of all non-void functions, or explicitly ignore them (like - casting to void in C) [0]: - - if ((a = malloc(sizeof char)) == NULL) { /* handle this error */ - (void)fprintf(stderr, "oh noes!"); /* explicitly ignore this one */ - return EX_OSERR; /* ...because the program is exiting anyway */ - } - - 8. The smallest possible scope for data [0]. - - 9. Comments noting all the symbols and macros used from a C header file, next - to its include macro: - - #include /* close(2), getopt(3), lseek(2), read(2), write(2), - (space-aligned) * optarg, optind, STDIN_FILENO, STDOUT_FILENO */ - - 10. Spaces in control flow statements, after the keyword and before the - opening brace: - - for (i = 2; i < argc; ++i) { - - - 11. In Rust, a trailing comma on all arguments or fields that are on their own - lines: - - return Err(EvaluationError { - message: format!("{}: Invalid token", i), - code: EX_DATAERR, - }) - - 12. In Rust, place extern statements after use statements that include standard - library crates. Group like statements: - - use std::fs::Path; - - extern crate strerror; - extern crate sysexits; - - use strerror::StrError; - use sysexits::{ EX_OSERR, EX_USAGE }; - - 13. If text is on the same line as a brace, spaces after an opening brace and - before a closing one: - - use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE }; - - 14. Alphabetic sorting, where applicable: - - use std::io::{ BufWriter, Read, Write, stderr, stdin, stdout } - - 15. In Rust, use the to_owned() method on string types (str, OsStr, CStr, etc.) - and the to_string() method on other types. - - -Avoid -===== - - 16. Unbounded loops [0]. - - 17. Function pointers [0]. - - 18. Heap memory allocation [0]. - - 19. Using too much nested logic (within reason). - - 20. Too many levels of dereferences [0]: - - /* do not do this */ - for (size_t i = 0; i < sizeof a / sizeof *a; ++i) { - if (a[i].id == MATCH) { a[i].val = 0; } - } - - /* do this */ - for (struct MadeUp *s = &a[0]; *s != NULL; s = &s[1]) { - if (s->id == MATCH) { s->val = 0; } - } - - 21. Using C preprocessor macros; the fewer, the better [0]. - - 22. The exit(3p) and std::process::exit() functions; returning from the main - function skips a system call. - - -Do Not Use -========== - - 23. More than the length of one printed page for a function [0]. - - 24. Recursion, as it’s complex and can unexpectedly overflow the stack [0]. - - 25. Any functionality not in the POSIX C specification and language features not - in C99. - - 26. Do-while loops, as they’re unique to C and confusing for casual programmers. - - 27. Labels and goto statements; use sensible flow control [0]. - - 28. Pointer arithmetic, as it tends to be confusing and unnecessary; use - index-reference patterns like &p[1] instead of p + 1. &p[n] is the address at - p + sizeof p * n, not p + n, like pointer arithmetic suggests. - - 29. C struct bitfields in unions, to access certain bits of bigger data types, - as it’s poorly defined in the C standards; use bit arithmetic. - - 30. C trigraphs. - - 31. Inclusions in C header files, to prevent multiple file inclusions. - - 32. C preprocessor variables to prevent multiple inclusions of the same file, - such as: - - #ifdef _FILE - #define _FILE - /* file body */ - #endif /* ifdef _FILE */ - - Instead, take the time to ensure other files aren’t including any files twice. - - 33. The gets(3p) function from , as it’s impossible to prevent buffer - overflows when it's used; use fgets(3p) from . - - 34. The scanf(3p) function from [1]. - - 35. Any functionality not described in the latest POSIX make(1) specification. - - 36. Macros which panic on failure in Rust (such as the print!() and println!() - macros). Use a function and handle any errors. However, do use the eprintln!() - macro for error messages. Handling an error for writing an error message is - redundant. - - -Usage Text -========== - -This section is adapted from the NetBSD style guide [2]. - -When programs are invoked incorrectly and in the synopsis of manual pages, uasge -text should be provided to the user. The following is the format used by this -project for this purpose: - -All optional arguments are to be placed in square brackets (U+005B, U+005D). -Mutually exclusive arguments can be separated by a vertical line (U+007C). -Groups of arguments should be specified in alphabetical order in most cases. The -order of arguments and an example of these rules follows: - - 0. Options with no option arguments. - 1. Options with option arguments. Arguments should be specified inside the same - square brackets as the options. - 3. Non-option arguments. - - "usage: f [-aDde] [-b b_arg] [-m m_arg] req1 req2 [opt1 [opt2]]\n" - "usage: f [-a | -b] [-c [-de] [-n number]]\n" - - -References -========== - -[0] -[1] -[2] - --- -Copyright © 2024 Emma Tebibyte -Copyright © 2024 DTB -Copyright © Wikipedia contributors - -This work is licensed under CC BY-SA 4.0. To view a copy of this license, visit -. -- 2.46.1 From ac0f9e40197c072587b7dfbf41584259e3e40691 Mon Sep 17 00:00:00 2001 From: Emma Tebibyte Date: Thu, 3 Apr 2025 16:46:50 -0600 Subject: [PATCH 2/2] README: add CONTRIBUTING mention --- README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README b/README index 0c01d5e..053f696 100644 --- a/README +++ b/README @@ -18,7 +18,8 @@ anywhere. Other utility sets aim to provide a number of fully-featured programs to be used individually, Harakit utilities are meant to be easily composable and work together in pipelines. -See docs/ for more on the specific utilities currently implemented. +See docs/ for more on the specific utilities currently implemented and see +CONTRIBUTING for guidelines for contributions. Building -- 2.46.1