edryd.org

some of my neat stuff
git clone git://edryd.org/edryd.org
Log | Files | Refs | LICENSE

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