kiwmi/lit/syrup.lit
2018-10-01 13:08:31 +02:00

222 lines
4.3 KiB
Text

@code_type c .c
@comment_type /* %s */
@title syrup
@s Introduction
Using syrup is rather straight forward. The syntax is as follows: `syrup COMMAND`.
The program is also simple. Here is an outline.
--- syrup.c
@{copyright notice}
@{c headers}
@{posix headers}
@{helper function}
int
main(int argc, char *argv[])
{
@{variables local to main}
if (argc < 2) {
die("%s: not enough arguments\n", argv[0]);
}
@{open socket}
@{send message}
@{receive and print reply}
@{cleanup}
}
---
As we can already see we will need to include `stdio.h` and `stdlib.h`.
--- c headers
#include <stdio.h>
#include <stdlib.h>
---
@s Copyright
This project is licensed under the Mozilla Public License Version 2.0. Please read LICENSE for more details.
--- copyright notice
/* Copyright (c), Niclas Meyer <niclas@countingsort.com>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
---
@s Creating the socket object
This is rather straight forward. We setup all the things in our socket object and then connect.
We're going to store the file descriptor and the `sockaddr` local to main.
--- variables local to main
int sock_fd;
struct sockaddr_un sock_addr;
---
This requires including the following headers.
--- posix headers
#include <sys/socket.h>
#include <sys/un.h>
---
Now it's finally time to fill stuff into our `sockaddr`.
--- open socket
if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
die("%s: failed to open socket\n", argv[0]);
}
sock_addr.sun_family = AF_UNIX;
@{get socket path}
if (connect(sock_fd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) {
die("%s: failed to connect to socket\n", argv[0]);
}
---
@s Get the socket path
It's important that we open the correct socket.
This might be set via an environment variable to allow maximum flexibility, while still keeping a trivial interface.
We're going to store the path in `sock_path`, local to main.
--- variables local to main +=
const char *sock_path;
---
We check if `WMAFFLE_SOCKET` is set, and use it, or default to `/tmp/wmaffle.sock`.
--- get socket path
sock_path = getenv("WMAFFLE_SOCKET");
if (sock_path) {
strncpy(sock_addr.sun_path, sock_path, sizeof(sock_addr.sun_path));
} else {
strncpy(sock_addr.sun_path, "/tmp/wmaffle.sock", sizeof(sock_addr.sun_path));
}
---
For this we need `string.h`.
--- c headers +=
#include <string.h>
---
@s Send our message
This actually consists of two steps.
--- send message
@{generate message from argv}
@{send generated message}
---
@s Generate message
Pretty straight forward. We need a buffer and then we just append all arguments, beginning with `argv[1]`, separated by spaces.
--- variables local to main +=
char msg[BUFSIZ];
size_t msg_len;
---
--- generate message from argv
msg_len = 0;
for (int i = 1; i < argc; i++) {
msg_len += (size_t)snprintf(msg + msg_len, sizeof(msg), "%s ", argv[i]);
}
msg_len -= 1; // Remove trailing space
---
@s Actually send the message
This is straight forward enough. We just `send` the message.
--- send generated message
if (send(sock_fd, msg, msg_len, 0) < 0) {
die("%s: failed to send message\n", argv[0]);
}
---
@s Handling the reply
Some commands return a string to print. If there's none, we'll just receive a `\0`.
This is pretty much stolen from baskerville.
--- receive and print reply
struct pollfd fds[] = {
{ sock_fd, POLLIN, 0 },
{ STDOUT_FILENO, POLLHUP, 0 },
};
while (poll(fds, 2, -1) > 0) {
if (fds[1].revents & (POLLERR | POLLHUP)) {
break;
}
if (fds[0].revents & POLLIN) {
if ((msg_len = recv(sock_fd, msg, sizeof(msg) - 1, 0)) > 0) {
msg[msg_len] = '\0';
if (msg_len == 1) {
break;
}
printf("%s", msg);
fflush(stdout);
} else {
break;
}
}
}
---
This requires us to include `poll.h` and `unistd.h`.
--- posix headers +=
#include <poll.h>
#include <unistd.h>
---
@s Cleanup
Not much to do here. Just close the socket.
--- cleanup
close(sock_fd);
---
@s Helper function
We used `die` the whole time. Time to make it a thing already.
--- helper function
static void
die(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
---
Note that this requires the `stdarg.h` header.
--- c headers +=
#include <stdarg.h>
---