libsl.md (9730B)
1 --- 2 title: "Overview of libsl" 3 date: 2017-06-04 4 tags: tech c library suckless 5 categories: tech 6 --- 7 8 ![suckless](/img/posts/sl-header.png) 9 10 [libsl][8] is an extremely small library used to write better and clearer C 11 programs. The code was created by the [suckless][9] community, a small group of 12 C hackers and developers creating powerful yet simple programs for Linux 13 designed to be lightweight, configurable, and easier to maintain then the many 14 other unnecessarily bloated [alternatives][10]. You can read more about their 15 [philosophy][11] on their website. Some of their most notable projects include 16 [dwm][12], [dmenu][13], [st][14], and [surf][15]. 17 18 libsl is composed of tiny C99 source and headers files designed to be simply 19 copied into a project's source tree. This allows the library to be easily used 20 and easily modified to fit every project's need. 21 22 All of libsl is protected under the MIT license, allowing you to use it in your 23 own projects with little strings attached. See [LICENSE][6] in the source tree 24 for more. 25 26 ## arg.h 27 28 One of the most utilized and most important files in libsl is [arg.h][1], a 29 C header designed to easily interpret short options from the command line. 30 This removes the necessity to copy complex boiler plate or clunky libraries to 31 simply see if an option has been supplied by the user. While `arg.h` does not 32 support long options (`--option` vs `-o`), if your project's CLI is relatively 33 straight forward it is a great, simple, and lightweight method to quickly parse 34 short options in your C code. 35 36 To start reading short options simply use the macro `ARGBEGIN` followed by a 37 `{` in your C program's `main` function, don't forget to `#include "arg.h"` at 38 the top of the file. This macro loops through all the command line arguments 39 and finds the short option (beginning with a single `-`) and allows you to 40 select one in a switch statement by using `case 'a':`, where `a` can be any 41 character for the desired option. 42 43 `arg.h` also defines other macros to read data in the `ARGBEGIN` block. One 44 example is `ARGC()` which is defined to `ARGBEGIN`'s internal variable `argc_`, 45 the character for the short option currently being interpreted. 46 47 `ARGF()` is another useful macro to get an argument after an option. For 48 example if a program `prog` is run as `prog -s string` then `ARGF()` in the `s` 49 case will return `string` as a C string. This can also be converted to a number 50 through a function such as `atoi`. A similar macro `EARGF(x)` can also run the 51 supplied function upon an error. It is common to define a `usage()` function 52 in suckless projects such as the one below. 53 54 ```c 55 void 56 usage(void) 57 { 58 die("usage: %s [-ab] [-c STR] [-d STR] [-n NUM | -NUM]", argv0); 59 } 60 ``` 61 62 `die()` is a function in libsl defined in `util.c`, see the [section](#utilc) 63 below for more. 64 65 `arg.h` also defines a string, `argv0`, which is set to the program's name, 66 equivalent to `argv[0]`, but global allowing it to be used throughout your 67 code, like in the usage message above. 68 69 In this example `EARGF(usage())` would display usage info on an error reading 70 the next string. 71 72 While `ARGF()` with `atoi()` can be used to get a number from given arguments, 73 it is often faster to use the `-NUM` format (eg `prog -4` to get the number `4` 74 instead of `prog -n 4`). In order to achieve this I have added my own macros 75 to `arg.h` that can be used. 76 77 ```c 78 /* handles -NUM syntax */ 79 #define ARGNUM case '0':\ 80 case '1':\ 81 case '2':\ 82 case '3':\ 83 case '4':\ 84 case '5':\ 85 case '6':\ 86 case '7':\ 87 case '8':\ 88 case '9' 89 90 /* get option number */ 91 #define ARGNUMF() (brk_ = 1, atoi(argv[0])) 92 ``` 93 94 The first is `ARGNUM` which is used in replace of a case statement in the 95 `ARG` block (eg `case 'n':` becomes `ARGNUM:`). Inside of this case 96 replacement `ARGNUMF()` is used to get the number given by the argument 97 supplied by the user. These macros, while only needing to be defined for 1-9, 98 work on any number over 10 as well. 99 100 After running through all the options you want---remembering to use `break` 101 after each one, possibly including a `default` in case an argument given is not 102 supported---`ARGBEGIN` and the curly bracket we opened need to be 103 closed via `} ARGEND`. `ARGEND` is essentially just closing all the brackets 104 `ARGBEGIN` opened, not counting the one we placed. 105 106 A complete `ARG` block example is shown below, don't forget that it needs to be 107 placed in inside the `int main(int argc, char *argv[])` function. 108 109 ```c 110 ARGBEGIN { 111 case 'a': 112 printf("option a selected\n"); 113 case 'b': 114 printf("option b also runs if your not careful\n"); 115 break; 116 case 'c': 117 printf("options can also have arguments: %s\n", ARGF()); 118 break; 119 case 'd': 120 printf("support for error messages: %s\n", EARGF(usage())); 121 break; 122 case 'n': 123 printf("they can also be numbers: %d\n", atoi(EARGF(usage()))); 124 break; 125 ARGNUM: 126 printf("or in this format: %d\n", ARGNUMF()); 127 break; 128 case 'v': 129 verbose++; /* define before ARGBEGIN block */ 130 printf("options can also be repeated: %d\n", verbose); 131 break; 132 default: 133 usage(); 134 } ARGEND; 135 ``` 136 137 Note that `arg.h` modifies `argv` and `argc`. If, for example, after parsing 138 the arguments you now want to read any strings after, such as file names, 139 `argv[0]` is the first argument after the valid options. So if `prog -s option 140 filename anotherfile` is used, the `s` case in `ARGBEGIN` is run with the 141 argument `option`, then `argv[0]` will be equal to `filename`, `argv[1]` to 142 `anotherfile`, etc. 143 144 ## util.c 145 146 The next file in libsl is [util.c][2]. This file defines many general purpose C 147 functions used throughout different projects. 148 149 One function found here which is common in many C projects, including 150 [Linux][7], is `die()`. This utility takes a formatted string, adds a newline 151 at the end, and prints it to `stderr` before `exit()`ing with a return value of 152 `1`. This is useful for writing simple and clean error messages which need to 153 quit the program. `die()` is used above in the `usage()` example as well as the 154 quick examples below. 155 156 ```c 157 struct MyData data; 158 data->str = init_str(); 159 if (!data->str || !strcmp(data->str, "")) 160 die("%s: error: could not init data->str", argv0); 161 ``` 162 163 ```c 164 int strlen = 20; 165 char *str; 166 if (!(str = calloc(strlen, sizeof(char)))) 167 die("fatal: could not calloc() %u bytes", strlen * sizeof(char)); 168 ``` 169 170 As well as formatting the output similar to `printf()`, libsl's `die()` also 171 supports automatic error messages for functions which set the C global `errno`. 172 If the string given ends in a colon (`:`), `die()` will append a space and the 173 error message thrown by a called function with `errno` support. Thus the above 174 `calloc` example can be replaced with the more adaptive: 175 176 ```c 177 int strlen = 20; 178 char *str; 179 if (!(str = calloc(strlen, sizeof(char)))) 180 die("calloc:"); /* example error: "calloc: out of memory" */ 181 ``` 182 183 The above example is the bases for another useful function `util` defines, 184 `ecalloc()`. This new function makes sure the pointer allocated by `calloc` is 185 not `NULL`, which indicates an error occurred. Using this new wrapper we can 186 replace the example above with the following. 187 188 ```c 189 int strlen = 20; 190 char *str = ecalloc(strlen, sizeof(char)); 191 ``` 192 193 This function can also be duplicated for other similar functions such as 194 `realloc()`, `malloc()`, `strdup()`, etc. A new example `erealloc()` function 195 is displayed below. 196 197 ```c 198 void * 199 erealloc(void *p, size_t size) 200 { 201 if (!(p = realloc(p, size))) 202 die("realloc:"); 203 204 return p; 205 } 206 ``` 207 208 ### util.h 209 210 The [header][3], as well as defining the previous functions, creates some 211 useful macros. 212 213 Two macros which are often copy and pasted between projects and files is 214 `MAX()` and `MIN()`. They are fairly simple, two integers are given as 215 arguments and the biggest or smallest one is returned respectively. 216 217 ```c 218 #define MAX(A, B) ((A) > (B) ? (A) : (B)) 219 #define MIN(A, B) ((A) < (B) ? (A) : (B)) 220 ``` 221 222 ```c 223 MAX(4, 8); /* => 8 */ 224 MIN(-38, -18); /* => -38 */ 225 MAX(10, -47); /* => 10 */ 226 ``` 227 228 In a similar vain `BETWEEN()` returns whether the first integer supplied is 229 between the next two given integers. 230 231 ```c 232 #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 233 ``` 234 235 ```c 236 BETWEEN(4, -8, 12); /* => 1 (true) */ 237 BETWEEN(9, 20, 67); /* => 0 (flase) */ 238 ``` 239 240 `LEN()` is also often defined here, it is used to return the size of an array. 241 242 ```c 243 #define LEN(X) (sizeof(X) / sizeof((X)[0])) 244 ``` 245 246 ## drw.c 247 248 [drw.c][4] and its header [drw.h][5] are used as an X interfaced for making 249 basic graphical programs, for example [dmenu](http://tools.suckless.org/dmenu). 250 An in depth overview of the many functions and `typedef`s offered in this file 251 is coming soon as its own post. 252 253 ## conclusion 254 255 I hope this post has be beneficial and will help you create more elegant and 256 cleaner code in C. For other projects putting this philosophy into practice 257 checkout some of my [projects](/projects), the [suckless git 258 repo](http://git.suckless.org), and their other [recommended 259 projects](http://suckless.org/rocks) page. 260 261 [1]: http://git.suckless.org/libsl/tree/arg.h 262 [2]: http://git.suckless.org/libsl/tree/util.c 263 [3]: http://git.suckless.org/libsl/tree/util.h 264 [4]: http://git.suckless.org/libsl/tree/drw.c 265 [5]: http://git.suckless.org/libsl/tree/drw.h 266 [6]: http://git.suckless.org/libsl/tree/LICENSE 267 [7]: https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/arch/mn10300/boot/tools/build.c#L47 268 [8]: http://git.suckless.org/libsl 269 [9]: http://suckless.org 270 [10]: http://suckless.org/sucks 271 [11]: http://suckless.org/philosophy 272 [12]: http://dwm.suckless.org/ 273 [13]: http://tools.suckless.org/dmenu 274 [14]: http://st.suckless.org/ 275 [15]: http://surf.suckless.org/ 276