README.md
1# Coding suggestions for CliFM
2
3**NOTE**: To keep a consintent style, run `clang-format` over all source files, including header files, using the `_clang-format` file (in `/src`) as the formatting model:
4
5```sh
6clang-format -i -style=file *.[hc]
7```
8
9This command will reformat all C source and header files (`*.[hc]`) in place (`-i`) using the `_clang-format` file as model (`-style=file`).
10
11Once this is done, you might want to check and modify a few things from the resulting files, specially long `printf`'s and multi-line statements. Automatic formating in these cases is still not optimal.
12
13## 1) Coding style
14
15### C source
16
17BSD compatibility: We try to keep CliFM working on BSD systems (at the very least). So, when calling a function make sure it exists on BSD systems, and, if possible, make sure it is POSIX. Check its syntax as well: GNU functions are not always identical to BSD ones.
18
19Generally, try to stick as closely as possible to the `Linux kernel coding style`. See https://www.kernel.org/doc/html/v4.10/process/coding-style.html
20
21Indentation: TABS (I use a width of 4, but you can use 8 if you like)
22
23Naming convention: Linux kernel style
24
25Comments: C style only. Ex:
26
27```c
28/* This is a single line comment */
29
30/* This is a muti-line
31* comment */
32```
33
34Non-function statement blocks: Ex:
35```c
36if (condition) {
37 ...
38} else {
39 ...
40}
41```
42
43Functions definition:
44
45```c
46return type
47function_name(argument, ... n)
48{
49 ...
50}
51```
52
53**NOTE**: Writing the function name and arguments in a separate line makes it more easily searchable
54
55Assignements and comparissons (spaces around equal sign):
56
57```c
58x = y
59```
60
61Proper casting. For example, do not write:
62
63```c
64return NULL;
65```
66
67But,
68
69```c
70return (char *)NULL;
71```
72
73Prefer ASCII instead of Hex: Ex: `'\0'` instead of `0x00`
74
75Spacing: Write easily readable code. Generally, use blank lines between code blocks (this, however, depends on the code written so far). Just make it readable (the code won't be better for being more tightly written)
76
77Max line legnth: `80 characters/columns`. If an statement exceeds this number, split it into multiple lines as follows:
78
79```c
80if (condition)
81 printk(KERN_WARNING "Warning this is a long printk with "
82 "3 parameters a: %u b: %u "
83 "c: %u \n", a, b, c);
84```
85
86Make sure blank/empty lines do not contains TABS or spaces. In the same way, remove ending TABS and spaces.
87
88## 2) Code quality
89
90A program should not only provide working features, but also well written, performant, and easily understandable code:
91
92**1)** Use the proper tool (and in the proper way) for the job: be as simple as possible and do not waste resources (they are highly valuable). For example: `strcmp(3)` is a standard and quite useful function. However, it is a good idea to prevent calling this function (possibily hundreds of times in a loop) if not needed. Before calling `strcmp` compare the first byte of the strings to be compared. If they do not match, there is no need to call the function:
93
94```c
95if (*str == *str2 && strcmp(str, str2) == 0)
96```
97
98In the same way, and for the same reason, use the cheapest function. If you do not need formatting, prefer `write(3)` or `fputs(3)` over `printf(3)`: they're faster.
99
100Use pointers whenever possible: this is one of the greatest advantages of C. For instance, if you need to get the basename of a directory, there is no need to copy the string in any way, neither manually nor via some other function. Just get a pointer to the last slash in the original string using `strrchr(3)`:
101
102```c
103char *ret = strrchr(str, '/');
104if (ret && *(++ret))
105 /* We have the directory basename */
106```
107
108Always perform bound checks. Either make sure the destination buffer is big enough to hold the source string or truncate the source string to fit your buffer via some of the `n` functions (`strncpy(3)`, `strncat(3)`, `snprintf(3)`, etc):
109
110```c
111char *buf = (char *)xnmalloc(strlen(src) + 1, sizeof(char));
112strcpy(buf, src);
113```
114
115or (using a safe version of `strncpy(3)`)
116```c
117buf[PATH_MAX];
118xstrsncpy(buf, src, PATH_MAX);
119```
120
121**Note**: Both `xstrsncpy` and `xnmalloc` are safe implementations of `strcpy(3)` and `malloc(3)` respectively and are provided by CliFM itself.
122
123These are just a few examples. There are plenty of resources out there on how to write secure code.
124
125**2)** Manual memory management is another of the greatest (dis)advantages of C. Use a tool like `valgrind` to make sure your code is not leaking memory. Free `malloc`'ed memory as soon as you don't need it any more.
126
127**3)** Static analysis tools are an invaluable resource: use them! **Always** check your code via tools like `cppcheck`, `scan-build` or `splint`. Use them all if necessary. Once done, fix all warnings and errors (provided they are not false positives, of course).
128
129When it comes to plugins, we mostly use `POSIX shell scripts`. In this case, always use `shellcheck` to check your plugins.
130
131**4**) Submitted code must be compiled without any warning/error using the following compiler flags:
132
133```sh
134-Wall -Wextra -Werror -Wpedantic -Wshadow -Wformat=2 -Wformat-security -Wconversion -Wsign-conversion -fstack-protector-strong -fstack-clash-protection -fcf-protection -Wvla -std=c99
135```
136
137To make sure your structs are properly aligned, add `-Wpadded` to detect misalignments and correct them by adding some padding if necessary.
138
139**5**) If not obvious, comment what your code is trying to achieve: there is no good software without good documentation.
140
141## 3) CliFM's general code structure
142
143CliFM source code consists of multiple C source files, being `main.c` the starting point and `helpers.h` the main header file. In `main.c` you'll find:
144
145**A)** Initialization stuff, like loading config files (see `config.c`), command line options (parsed by the `external_arguments()` function, in `init.c`), readline and keybindings initialization (see `readline.c` and `keybindings.c`), bookmarks, workspaces, history, and the like.
146
147**B)** Once everything is correctly initialized, an infinite loop, structured as a basic shell, takes place:
1481) Take input
149
1502) Parse input
151
1523) Execute command
153 And take more input...
154
155**C)** The last step above (3) calls the `exec_cmd()` function (`in exec.c`) to find out what needs to be done based on the user's input. The structure of the `exec_cmd` function is a big if-else chain: if the command is internal, that is, one of CliFM's built-in commands, the corresponding function will be called and executed; if not, if it is rather an external command, it will be executed by the system shell (via `launch_execle()`, also in `exec.c`).
156
157**D)** Listing
158
159This is the basic structure of CliFM: generally speaking, it is just a shell. In between, however, lots of things happen. Leaving aside the above mentioned functions, the most important one is `listdir()`, defined in `listing.c`. Everything related to listing files happens here: reading files in the current directory (via **readdir**(3)), getting file information (via the dirent struct returned by **readdir**(3) itself and **stat**(3)), sorting files (via **qsort**(3)), and storing all these information in a global struct (`file_info`) for future access, for example, to get file properties of a given entry.
160
161**E)** Whatever happens later, is just some function or operation invoked by the user and happening on top of the steps described above: opening a file or directory (via the `open_function()` and `cd_function()` functions, in `file_operations.c` and `navigation.c` respectivelly), opening a bookmark (`bookmarks.c`), operating on files (`file_operations.c`), switching to a different profile (`profiles.c`), trashing a file (`trash.c`), searching for a file (`search.c`), running a plugin (`actions.c`), and so on.
162
163## 4) Hacking
164**Work in progress**
165
166* Default settings: `settings.h`
167
168* Add a new command: `exec.c`
169
170* Add a new prompt feature: `prompt.c`
171
172* Modify/add keybindings: `keybindings.c`
173
174* Icons: `icons.h` and `listing.c`. Consult the [customizing icons](https://github.com/leo-arch/clifm/wiki/Advanced#customizing-icons) section
175
176* TAB completion: `readline.c`, `tabcomp.c`
177
178* Interface: `listing.c`
179
180* Directory jumper: `jump.c`
181
182* Suggestions: `suggestions.c` and `keybinds.c` (see the `rl_accept_suggestion` function)
183
184* Syntax highlighting: `highlight.c` (see also `readline.c` and `keybinds.c`)
185
186* Autocommands: `autocmds.c`
187
188* File names cleaner(`bleach`): `name_cleaner.c` and `cleaner_table.h`
189
190## 5) Compilation
191
192**Note**: For the list of dependencies, see the [installation page](https://github.com/leo-arch/clifm/wiki/Introduction#installation).
193
194CliFM is compiled using `(g)cc` (`clang` and `tcc` work as well) as follows:
195
1961) _Linux_:
197```sh
198gcc -O3 -s -fstack-protector-strong -march=native -Wall -o clifm *.c -lreadline -lcap -lacl -lmagic
199```
200
2012) _FreeBSD_:
202
203```sh
204gcc -O3 -s -fstack-protector-strong -march=native -Wall -o clifm *.c -lreadline -lintl -lmagic
205```
206
2073) _NetBSD_:
208
209```sh
210gcc -O3 -s -fstack-protector-strong -march=native -Wall -o clifm *.c -I/usr/pkg/include -L/usr/pkg/lib -Wl,-R/usr/pkg/lib -lintl -lreadline -lmagic
211```
212
2134) _OpenBSD_:
214
215```sh
216cc -O3 -s -fstack-protector-strong -march=native -Wall -o clifm *.c -I/usr/local/include -L/usr/local/lib -lereadline -lintl -lmagic
217```
218
2195) _Haiku_:
220
221```sh
222gcc -o clifm *.c -lreadline -lintl -lmagic
223```
224
225**NOTE**: Since compiling in this way only produces a binary file, it is necessary to manually copy the remaining files. See the `install` block of the [Makefile](https://github.com/leo-arch/clifm/blob/master/Makefile).
226
227**NOTE 2**: You can drop `-lmagic` if compiling with `_NOMAGIC`. In the same way, you can drop `-lintl` if compiling with `_NO_GETTEXT`. See below.
228
229**NOTE 3**: If the binary size is an issue, it is recommended to use [upx(1)](https://linux.die.net/man/1/upx) to significantly reduce (50-70%) the size of the executable file (at the expense of some performance):
230
231```sh
232upx clifm
233```
234
235### Compiling features in/out
236
237CliFM allows you to enable or disable some features at compile time. If for whatever reason you don't plan to use a certain feature, it is better to remove this feature from the resulting binary: you'll get a (bit) faster and smaller executable. To do this, pass one or more of the following options to the compiler using the `-D` parameter. For example, to get a POSIX compliant executable without icons support:
238```sh
239clang ... -D_BE_POSIX -D_NO_ICONS ...
240```
241
242| Option | Description |
243| --- | --- |
244| `_BE_POSIX` | Build a fully `POSIX.1-2008` compliant executable<sup>1</sup> |
245| `_NERD` | Enable Nerdfont support for icons |
246| `_TOURBIN_QSORT` | Use Alexey Tourbin faster [qsort implementation](https://github.com/svpv/qsort) instead of [qsort(3)](https://www.man7.org/linux/man-pages/man3/qsort.3.html) |
247| `_NO_ARCHIVING` | Disable archiving support |
248| `_NO_BLEACH` | Disable support for `Bleach`, the built-in file names cleaner |
249| `_NO_GETTEXT` | Disable translations support (via `gettext`) |
250| `_NO_HIGHLIGHT`| Disable syntax highlighting support |
251| `_NO_ICONS` | Disable icons support |
252| `_NO_LIRA` | Disable [Lira](https://github.com/leo-arch/clifm/wiki/Specifics#resource-opener) support |
253| `_NO_MAGIC` | Allow compilation without `libmagic` dependency<sup>2</sup> |
254| `_NO_SUGGESTIONS` | Disable suggestions support |
255| `_NO_TRASH` | Disable trash support |
256
257<sup>1</sup> Only two features are lost:
2581) Files birth time: We get this information via [statx(2)](https://man7.org/linux/man-pages/man2/statx.2.html), which is Linux specific.
2592) Version sort: We use here [versionsort](https://man7.org/linux/man-pages/man3/scandir.3.html), a GNU extension.
260
261<sup>2</sup> Without `libmagic`, querying files MIME type implies grabing the output of the [file(1)](https://www.man7.org/linux/man-pages/man1/file.1.html) command, which of course is not as optimal as directly querying the `libmagic` database itself (we need to run the command, redirect its output to a file, open the file, read it, close it, and then delete it). Though perhaps unnoticiable, this is an important difference.
262
263## 6) Plugins
264
265CliFM plugins, that is, commands or set of commands executed by CliFM, could be any executable file, be it a shell script, a binary file (C, Python, Go, Rust, or whatever programming language you like). See the [plugins section](https://github.com/leo-arch/clifm/wiki/Advanced#plugins).
266