commit 38d2379f1e66e81e88f5fe3aab668112bb158b82
parent 01c2df7ff42473ae05cddd2f06b80b5d739c0499
Author: Ed van Bruggen <edvb54@gmail.com>
Date: Sun, 6 Aug 2017 01:48:59 -0700
Update libsl post
Diffstat:
1 file changed, 138 insertions(+), 99 deletions(-)
diff --git a/_posts/2017-06-04-libsl.md b/_posts/2017-06-04-libsl.md
@@ -6,48 +6,48 @@ categories: tech
![suckless](/assets/img/posts/sl-header.png)
-[libsl](http://git.suckless.org/libsl) is an extremely small "library" used by
-the [suckless](http://suckless.org) community to write better and simpler C
-programs. It is used by simply coping any of the files you want to use into
-your projects source tree and including the necessary header files in any `.c`
-file.
-
-Suckless is a small community of C developers creating small and simple
-programs for linux which are designed to be lighter, configurable and easier to
-maintain the many other unnecessarily bloated
-[alternatives](http://suckless.org/sucks). You can read more about their
-[philosophy](http://suckless.org/philosophy) on their website.
+[libsl][8] is an extremely small library used to write better and clearer C
+programs. The code was created by the [suckless][9] community, a small group of
+C hackers and developers creating powerful yet simple programs for Linux
+designed to be lightweight, configurable, and easier to maintain then the many
+other unnecessarily bloated [alternatives][10]. You can read more about their
+[philosophy][11] on their website. Some of their most notable projects include
+[dwm][12], [dmenu][13], [st][14], and [surf][15].
+
+libsl is composed of tiny C99 source and headers files designed to be simply
+copied into a project's source tree. This allows the library to be easily used
+and easily modified to fit every project's need.
-## arg.h
+All of libsl is protected under the MIT license, allowing you to use it in your
+own projects with little strings attached. See [LICENSE][6] in the source tree
+for more.
-One of the most utilized and most useful files in libsl is [arg.h][1], a header
-file designed to easily interpret short options from the command line in order
-to run code per option. This removes the necessity to copy complex boiler plate
-or huge libraries, such as `getopt`, to simply run see if an option has been
-supplied by the user. While `arg.h` does not support long options (`--option`
-vs `-o`) at the moment, it is still a simple and lightweight way to quickly add
-short option interpretation to your C, and possibly C++, code.
+## arg.h
-This header also defines a string, `argv0`, which is set to the program's name,
-equivalent to `argv[0]`, but global allowing it to be used throughout your
-code.
+One of the most utilized and most important files in libsl is [arg.h][1], a
+C header designed to easily interpret short options from the command line.
+This removes the necessity to copy complex boiler plate or clunky libraries to
+simply see if an option has been supplied by the user. While `arg.h` does not
+support long options (`--option` vs `-o`), if your project's CLI is relatively
+straight forward it is a great, simple, and lightweight method to quickly parse
+short options in your C code.
To start reading short options simply use the macro `ARGBEGIN` followed by a
-`{` in your C program's `main` function, don't forget to include the file with
-`#include "arg.h"` at the top of the file. Note that `ARGBEGIN` assumes that
-main is defined as `int main(int argc, char *argv[])`. This macro loops through
-all the command line arguments and finds the short option (beginning with a
-single `-`) and allows you to select one in a switch statement by using `case
-'a':`, where `a` can be any character for the desired option.
+`{` in your C program's `main` function, don't forget to `#include "arg.h"` at
+the top of the file. This macro loops through all the command line arguments
+and finds the short option (beginning with a single `-`) and allows you to
+select one in a switch statement by using `case 'a':`, where `a` can be any
+character for the desired option.
-`ARGC()` is a macro defined to `arg.h`'s internal variable `argc_` which is the
-character for the short option that the switch iterates through.
+`arg.h` also defines other macros to read data in the `ARGBEGIN` block. One
+example is `ARGC()` which is defined to `ARGBEGIN`'s internal variable `argc_`,
+the character for the short option currently being interpreted.
`ARGF()` is another useful macro to get an argument after an option. For
example if a program `prog` is run as `prog -s string` then `ARGF()` in the `s`
case will return `string` as a C string. This can also be converted to a number
-through a function such as `atoi`. A similar macro `EARGF(x)` can also be run
-the given function upon an error. It is common to define a `usage()` function
+through a function such as `atoi`. A similar macro `EARGF(x)` can also run the
+supplied function upon an error. It is common to define a `usage()` function
in suckless projects such as the one below.
```c
@@ -58,14 +58,20 @@ usage(void)
}
```
-`die()` in a function in libsl defined in `util.c`, see the [section](#utilc)
-below for more. In this example `EARGF(usage())` would be used to display usage
-info on an error.
+`die()` is a function in libsl defined in `util.c`, see the [section](#utilc)
+below for more.
+
+`arg.h` also defines a string, `argv0`, which is set to the program's name,
+equivalent to `argv[0]`, but global allowing it to be used throughout your
+code, like in the usage message above.
+
+In this example `EARGF(usage())` would display usage info on an error reading
+the next string.
While `ARGF()` with `atoi()` can be used to get a number from given arguments,
-it is often easier to use the `-NUM` format (eg `prog -4` to get the number 4
-instead of `prog -n 4`). In order to achieve this I have my added my own macros
-to `arg.h` that can be used, the additions are shown below.
+it is often faster to use the `-NUM` format (eg `prog -4` to get the number `4`
+instead of `prog -n 4`). In order to achieve this I have added my own macros
+to `arg.h` that can be used.
```c
/* handles -NUM syntax */
@@ -91,10 +97,10 @@ supplied by the user. These macros, while only needing to be defined for 1-9,
work on any number over 10 as well.
After running through all the options you want---remembering to use `break`
-after each one, possibly including a `default` fall back in case an argument
-given is not supported---the curly bracket and `ARGBEGIN` need to be closed via
-`} ARGEND`. `ARGEND` is essentially just closing all the brackets `ARGBEGIN`
-opened, not counting the one we placed.
+after each one, possibly including a `default` in case an argument given is not
+supported---`ARGBEGIN` and the curly bracket we opened need to be
+closed via `} ARGEND`. `ARGEND` is essentially just closing all the brackets
+`ARGBEGIN` opened, not counting the one we placed.
A complete `ARG` block example is shown below, don't forget that it needs to be
placed in inside the `int main(int argc, char *argv[])` function.
@@ -102,135 +108,168 @@ placed in inside the `int main(int argc, char *argv[])` function.
```c
ARGBEGIN {
case 'a':
- prinf("option a selected\n");
+ printf("option a selected\n");
case 'b':
- prinf("option b also runs\n");
+ printf("option b also runs if your not careful\n");
break;
case 'c':
- printf("options can also have arguments: %s", ARGF());
+ printf("options can also have arguments: %s\n", ARGF());
break;
case 'd':
- printf("support for error messages: %s", EARGF(usage()));
+ printf("support for error messages: %s\n", EARGF(usage()));
break;
case 'n':
- printf("they can also be numbers: %s", atoi(EARGF(usage())));
+ printf("they can also be numbers: %d\n", atoi(EARGF(usage())));
break;
ARGNUM:
- printf("or in this format: %s", ARGNUMF());
+ printf("or in this format: %d\n", ARGNUMF());
+ break;
+case 'v':
+ verbose++; /* define before ARGBEGIN block */
+ printf("options can also be repeated: %d\n", verbose);
break;
default:
usage();
} ARGEND;
```
+Note that `arg.h` modifies `argv` and `argc`. If, for example, after parsing
+the arguments you now want to read any strings after, such as file names,
+`argv[0]` is the first argument after the valid options. So if `prog -s option
+filename anotherfile` is used, the `s` case in `ARGBEGIN` is run with the
+argument `option`, then `argv[0]` will be equal to `filename`, `argv[1]` to
+`anotherfile`, etc.
+
## util.c
-The next file in libsl which is commonly used through the suckless community is
-[util.c][2]. This file defines many general purpose C functions used
-throughout their different projects.
+The next file in libsl is [util.c][2]. This file defines many general purpose C
+functions used throughout different projects.
-One function defined in here is `die()`. This function takes a formatted
-string, adding a newline at the end, and prints it to `stderr` before
-`exit()`ing with a return value of `1`. This is useful for writing simple and
-clean error messages which need to force quit the program. `die()` is used
-above in the `usage()` example as well as the quick examples below.
+One function found here which is common in many C projects, including
+[Linux][7], is `die()`. This utility takes a formatted string, adds a newline
+at the end, and prints it to `stderr` before `exit()`ing with a return value of
+`1`. This is useful for writing simple and clean error messages which need to
+quit the program. `die()` is used above in the `usage()` example as well as the
+quick examples below.
```c
+struct MyData data;
data->str = init_str();
-if (!data->str || strcmp(data->str, "") == 0)
- die("error: could not init data->str");
+if (!data->str || !strcmp(data->str, ""))
+ die("%s: error: could not init data->str", argv0);
```
```c
-char *str;
int strlen = 20;
+char *str;
if (!(str = calloc(strlen, sizeof(char))))
die("fatal: could not calloc() %u bytes", strlen * sizeof(char));
```
+As well as formatting the output similar to `printf()`, libsl's `die()` also
+supports automatic error messages for functions which set the C global `errno`.
+If the string given ends in a colon (`:`), `die()` will append a space and the
+error message thrown by a called function with `errno` support. Thus the above
+`calloc` example can be replaced with the more adaptive:
+
```c
-errno = 0;
-if ((pw = getpwuid(getuid())) == NULL) {
- if (errno)
- die("%s: getpwuid: %s", argv0, strerror(errno));
- else
- die("%s: who are you?", argv0);
-}
+int strlen = 20;
+char *str;
+if (!(str = calloc(strlen, sizeof(char))))
+ die("calloc:"); /* example error: "calloc: out of memory" */
```
-Another useful function `util` defines is `ecalloc()` which is a wrapper for
-the C function `calloc()`, using the previous `die()` function. This new
-function checks if the pointer allocated by `calloc` is readable and did not
-return a error or NULL. Using this new wrapper we can replace the second
-example about with the following.
+The above example is the bases for another useful function `util` defines,
+`ecalloc()`. This new function makes sure the pointer allocated by `calloc` is
+not `NULL`, which indicates an error occurred. Using this new wrapper we can
+replace the example above with the following.
```c
int strlen = 20;
-char *str = calloc(strlen, sizeof(char));
+char *str = ecalloc(strlen, sizeof(char));
```
This function can also be duplicated for other similar functions such as
-`realloc`, `malloc`, `strdup`. A new `erealloc` function is displayed below.
+`realloc()`, `malloc()`, `strdup()`, etc. A new example `erealloc()` function
+is displayed below.
```c
void *
erealloc(void *p, size_t size)
{
if (!(p = realloc(p, size)))
- die("realloc: out of memory");
+ die("realloc:");
return p;
}
```
-It might be beneficial for you to modify these error messages to show the
-`size` which could not be allocated.
-
### util.h
-In the [header][3] as well as defining the previous functions so they can
-utilized in other files it defines some useful macros.
+The [header][3], as well as defining the previous functions, creates some
+useful macros.
-Two macros which are often copy and pasted between projects and files is `MAX`
-and `MIN`. They are fairly simple, two integers are given as arguments and the
-biggest or smallest one is returned respectively.
+Two macros which are often copy and pasted between projects and files is
+`MAX()` and `MIN()`. They are fairly simple, two integers are given as
+arguments and the biggest or smallest one is returned respectively.
```c
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
```
-In a similar vain `BETWEEN` returns whether the first integer supplied is
+```c
+MAX(4, 8); /* => 8 */
+MIN(-38, -18); /* => -38 */
+MAX(10, -47); /* => 10 */
+```
+
+In a similar vain `BETWEEN()` returns whether the first integer supplied is
between the next two given integers.
```c
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
```
-## drw.c
+```c
+BETWEEN(4, -8, 12); /* => 1 (true) */
+BETWEEN(9, 20, 67); /* => 0 (flase) */
+```
-[drw.c][4] and its header [drw.h][5] are used as a X11 interfaced for making
-basic X programs, for example [dmenu](http://tools.suckless.org/dmenu). An in
-depth overview of the many functions and `typedef`s offered in this file is
-coming soon as its own post.
+`LEN()` is also often defined here, it is used to return the size of an array.
-## license
+```c
+#define LEN(X) (sizeof(X) / sizeof((X)[0]))
+```
-All of libsl is protected under the MIT license, allowing you to use it in your
-own projects with little strings attached. See [LICENSE][6] it the source tree
-for more.
+## drw.c
+
+[drw.c][4] and its header [drw.h][5] are used as an X interfaced for making
+basic graphical programs, for example [dmenu](http://tools.suckless.org/dmenu).
+An in depth overview of the many functions and `typedef`s offered in this file
+is coming soon as its own post.
## conclusion
I hope this post has be beneficial and will help you create more elegant and
cleaner code in C. For other projects putting this philosophy into practice
checkout some of my [projects](/projects), the [suckless git
-repo](http://git.suckless.org) and their other [recommended
+repo](http://git.suckless.org), and their other [recommended
projects](http://suckless.org/rocks) page.
-[1]: http://git.suckless.org/libsl/tree/arg.h
-[2]: http://git.suckless.org/libsl/tree/util.c
-[3]: http://git.suckless.org/libsl/tree/util.h
-[4]: http://git.suckless.org/libsl/tree/drw.c
-[5]: http://git.suckless.org/libsl/tree/drw.h
-[6]: http://git.suckless.org/libsl/tree/LICENSE
+[1]: http://git.suckless.org/libsl/tree/arg.h
+[2]: http://git.suckless.org/libsl/tree/util.c
+[3]: http://git.suckless.org/libsl/tree/util.h
+[4]: http://git.suckless.org/libsl/tree/drw.c
+[5]: http://git.suckless.org/libsl/tree/drw.h
+[6]: http://git.suckless.org/libsl/tree/LICENSE
+[7]: https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/arch/mn10300/boot/tools/build.c#L47
+[8]: http://git.suckless.org/libsl
+[9]: http://suckless.org
+[10]: http://suckless.org/sucks
+[11]: http://suckless.org/philosophy
+[12]: http://dwm.suckless.org/
+[13]: http://tools.suckless.org/dmenu
+[14]: http://st.suckless.org/
+[15]: http://surf.suckless.org/
+