Browse Source

Move to liberty

Přemysl Janouch 3 years ago
parent
commit
3748bc5d14
7 changed files with 16 additions and 749 deletions
  1. 3
    0
      .gitmodules
  2. 2
    3
      CMakeLists.txt
  3. 3
    2
      README.rst
  4. 5
    30
      autistdraw.c
  5. 2
    3
      config.h.in
  6. 1
    0
      liberty
  7. 0
    711
      utils.c

+ 3
- 0
.gitmodules View File

@@ -1,3 +1,6 @@
1 1
 [submodule "termo"]
2 2
 	path = termo
3 3
 	url = git://github.com/pjanouch/termo.git
4
+[submodule "liberty"]
5
+	path = liberty
6
+	url = git://github.com/pjanouch/liberty.git

+ 2
- 3
CMakeLists.txt View File

@@ -3,9 +3,8 @@ cmake_minimum_required (VERSION 2.8.5)
3 3
 
4 4
 # Moar warnings
5 5
 if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
6
-	set (CMAKE_C_FLAGS "-std=gnu99")
7
-	set (CMAKE_C_FLAGS_DEBUG
8
-		"${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra -Wno-missing-field-initializers")
6
+	# -Wunused-function is pretty annoying here, as everything is static
7
+	set (CMAKE_C_FLAGS "-std=c99 -Wall -Wextra -Wno-unused-function")
9 8
 endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
10 9
 
11 10
 # Build options

+ 3
- 2
README.rst View File

@@ -7,14 +7,15 @@ autistdraw
7 7
 
8 8
 Building and Running
9 9
 --------------------
10
-Build dependencies: CMake, pkg-config, ncursesw, libev, termo (included)::
10
+Build dependencies: CMake, pkg-config, liberty (included), termo (included)::
11
+Runtime dependencies: ncursesw, libev::
11 12
 
12 13
  $ git clone https://github.com/pjanouch/autistdraw.git
13 14
  $ git submodule init
14 15
  $ git submodule update
15 16
  $ mkdir build
16 17
  $ cd build
17
- $ cmake .. -DCMAKE_BUILD_TYPE=Debug
18
+ $ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug
18 19
  $ make
19 20
 
20 21
 Now, for standalone mode you can run::

+ 5
- 30
autistdraw.c View File

@@ -18,32 +18,11 @@
18 18
  *
19 19
  */
20 20
 
21
-#include <stdio.h>
22
-#include <stdlib.h>
23
-#include <locale.h>
24
-#include <stdarg.h>
25
-#include <stdint.h>
26
-#include <stdbool.h>
27
-#include <string.h>
28
-#include <errno.h>
29
-
30
-#include <unistd.h>
31
-#include <fcntl.h>
32
-#include <signal.h>
33
-
34
-#include <sys/socket.h>
35
-#include <sys/types.h>
36
-#include <netinet/in.h>
37
-#include <netdb.h>
38
-
39
-#ifndef NI_MAXHOST
40
-#define NI_MAXHOST 1025
41
-#endif // ! NI_MAXHOST
42
-
43
-#ifndef NI_MAXSERV
44
-#define NI_MAXSERV 32
45
-#endif // ! NI_MAXSERV
21
+#include "config.h"
22
+#include "liberty/liberty.c"
23
+#include "termo.h"
46 24
 
25
+#include <locale.h>
47 26
 #include <termios.h>
48 27
 #ifndef TIOCGWINSZ
49 28
 #include <sys/ioctl.h>
@@ -51,10 +30,6 @@
51 30
 
52 31
 #include <ev.h>
53 32
 #include <curses.h>
54
-#include "termo.h"
55
-
56
-#include "config.h"
57
-#include "utils.c"
58 33
 
59 34
 #define PALETTE_WIDTH       9           ///< Width of the palette
60 35
 #define TOP_BAR_CUTOFF      3           ///< Height of the top bar
@@ -1464,7 +1439,7 @@ parse_program_arguments (app_options_t *options, int argc, char **argv)
1464 1439
 		opt_handler_usage (&oh, stdout);
1465 1440
 		exit (EXIT_SUCCESS);
1466 1441
 	case 'V':
1467
-		printf (PROJECT_NAME " " PROJECT_VERSION "\n");
1442
+		printf (PROGRAM_NAME " " PROGRAM_VERSION "\n");
1468 1443
 		exit (EXIT_SUCCESS);
1469 1444
 	case 's':
1470 1445
 		if (options->server_address)

+ 2
- 3
config.h.in View File

@@ -1,9 +1,8 @@
1 1
 #ifndef CONFIG_H
2 2
 #define CONFIG_H
3 3
 
4
-#define PROJECT_NAME "${CMAKE_PROJECT_NAME}"
5
-#define PROJECT_VERSION "${project_VERSION}"
6
-#define PROJECT_URL "${project_URL}"
4
+#define PROGRAM_NAME "${CMAKE_PROJECT_NAME}"
5
+#define PROGRAM_VERSION "${project_VERSION}"
7 6
 
8 7
 #cmakedefine HAVE_RESIZETERM
9 8
 

+ 1
- 0
liberty

@@ -0,0 +1 @@
1
+Subproject commit 087645848baec5e59e4296817850bd5dd240cbb2

+ 0
- 711
utils.c View File

@@ -1,711 +0,0 @@
1
-/*
2
- * utils.c: utilities
3
- *
4
- * Copyright (c) 2014, Přemysl Janouch <p.janouch@gmail.com>
5
- * All rights reserved.
6
- *
7
- * Permission to use, copy, modify, and/or distribute this software for any
8
- * purpose with or without fee is hereby granted, provided that the above
9
- * copyright notice and this permission notice appear in all copies.
10
- *
11
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
14
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
16
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
17
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
- *
19
- */
20
-
21
-#include <getopt.h>
22
-
23
-#if defined __GNUC__
24
-#define ATTRIBUTE_PRINTF(x, y) __attribute__ ((format (printf, x, y)))
25
-#else // ! __GNUC__
26
-#define ATTRIBUTE_PRINTF(x, y)
27
-#endif // ! __GNUC__
28
-
29
-#if defined __GNUC__ && __GNUC__ >= 4
30
-#define ATTRIBUTE_SENTINEL __attribute__ ((sentinel))
31
-#else // ! __GNUC__ || __GNUC__ < 4
32
-#define ATTRIBUTE_SENTINEL
33
-#endif // ! __GNUC__ || __GNUC__ < 4
34
-
35
-#define N_ELEMENTS(a) (sizeof (a) / sizeof ((a)[0]))
36
-
37
-#define BLOCK_START  do {
38
-#define BLOCK_END    } while (0)
39
-
40
-// --- Safe memory management --------------------------------------------------
41
-
42
-// When a memory allocation fails and we need the memory, we're usually pretty
43
-// much fucked.  Use the non-prefixed versions when there's a legitimate
44
-// worry that an unrealistic amount of memory may be requested for allocation.
45
-
46
-static void *
47
-xmalloc (size_t n)
48
-{
49
-	void *p = malloc (n);
50
-	if (!p)
51
-	{
52
-		perror ("malloc");
53
-		exit (EXIT_FAILURE);
54
-	}
55
-	return p;
56
-}
57
-
58
-static void *
59
-xcalloc (size_t n, size_t m)
60
-{
61
-	void *p = calloc (n, m);
62
-	if (!p && n && m)
63
-	{
64
-		perror ("calloc");
65
-		exit (EXIT_FAILURE);
66
-	}
67
-	return p;
68
-}
69
-
70
-static void *
71
-xrealloc (void *o, size_t n)
72
-{
73
-	void *p = realloc (o, n);
74
-	if (!p && n)
75
-	{
76
-		perror ("realloc");
77
-		exit (EXIT_FAILURE);
78
-	}
79
-	return p;
80
-}
81
-
82
-// --- Double-linked list helpers ----------------------------------------------
83
-
84
-#define LIST_HEADER(type)                                                      \
85
-	type *next;                                                                \
86
-	type *prev;
87
-
88
-#define LIST_PREPEND(head, link)                                               \
89
-	BLOCK_START                                                                \
90
-		(link)->prev = NULL;                                                   \
91
-		(link)->next = (head);                                                 \
92
-		if ((link)->next)                                                      \
93
-			(link)->next->prev = (link);                                       \
94
-		(head) = (link);                                                       \
95
-	BLOCK_END
96
-
97
-#define LIST_UNLINK(head, link)                                                \
98
-	BLOCK_START                                                                \
99
-		if ((link)->prev)                                                      \
100
-			(link)->prev->next = (link)->next;                                 \
101
-		else                                                                   \
102
-			(head) = (link)->next;                                             \
103
-		if ((link)->next)                                                      \
104
-			(link)->next->prev = (link)->prev;                                 \
105
-	BLOCK_END
106
-
107
-#define LIST_APPEND_WITH_TAIL(head, tail, link)                                \
108
-	BLOCK_START                                                                \
109
-		(link)->prev = (tail);                                                 \
110
-		(link)->next = NULL;                                                   \
111
-		if ((link)->prev)                                                      \
112
-			(link)->prev->next = (link);                                       \
113
-		else                                                                   \
114
-			(head) = (link);                                                   \
115
-		(tail) = (link);                                                       \
116
-	BLOCK_END
117
-
118
-#define LIST_UNLINK_WITH_TAIL(head, tail, link)                                \
119
-	BLOCK_START                                                                \
120
-		if ((tail) == (link))                                                  \
121
-			(tail) = (link)->prev;                                             \
122
-		LIST_UNLINK ((head), (link));                                          \
123
-	BLOCK_END
124
-
125
-// --- Dynamically allocated strings -------------------------------------------
126
-
127
-// Basically a string builder to abstract away manual memory management.
128
-
129
-struct str
130
-{
131
-	char *str;                          ///< String data, null terminated
132
-	size_t alloc;                       ///< How many bytes are allocated
133
-	size_t len;                         ///< How long the string actually is
134
-};
135
-
136
-/// We don't care about allocations that are way too large for the content, as
137
-/// long as the allocation is below the given threshold.  (Trivial heuristics.)
138
-#define STR_SHRINK_THRESHOLD (1 << 20)
139
-
140
-static void
141
-str_init (struct str *self)
142
-{
143
-	self->alloc = 16;
144
-	self->len = 0;
145
-	self->str = strcpy (xmalloc (self->alloc), "");
146
-}
147
-
148
-static void
149
-str_free (struct str *self)
150
-{
151
-	free (self->str);
152
-	self->str = NULL;
153
-	self->alloc = 0;
154
-	self->len = 0;
155
-}
156
-
157
-static char *
158
-str_steal (struct str *self)
159
-{
160
-	char *str = self->str;
161
-	self->str = NULL;
162
-	str_free (self);
163
-	return str;
164
-}
165
-
166
-static void
167
-str_ensure_space (struct str *self, size_t n)
168
-{
169
-	// We allocate at least one more byte for the terminating null character
170
-	size_t new_alloc = self->alloc;
171
-	while (new_alloc <= self->len + n)
172
-		new_alloc <<= 1;
173
-	if (new_alloc != self->alloc)
174
-		self->str = xrealloc (self->str, (self->alloc = new_alloc));
175
-}
176
-
177
-static void
178
-str_append_data (struct str *self, const void *data, size_t n)
179
-{
180
-	str_ensure_space (self, n);
181
-	memcpy (self->str + self->len, data, n);
182
-	self->len += n;
183
-	self->str[self->len] = '\0';
184
-}
185
-
186
-static void
187
-str_append_c (struct str *self, char c)
188
-{
189
-	str_append_data (self, &c, 1);
190
-}
191
-
192
-static void
193
-str_append (struct str *self, const char *s)
194
-{
195
-	str_append_data (self, s, strlen (s));
196
-}
197
-
198
-static int
199
-str_append_vprintf (struct str *self, const char *fmt, va_list va)
200
-{
201
-	va_list ap;
202
-	int size;
203
-
204
-	va_copy (ap, va);
205
-	size = vsnprintf (NULL, 0, fmt, ap);
206
-	va_end (ap);
207
-
208
-	if (size < 0)
209
-		return -1;
210
-
211
-	va_copy (ap, va);
212
-	str_ensure_space (self, size);
213
-	size = vsnprintf (self->str + self->len, self->alloc - self->len, fmt, ap);
214
-	va_end (ap);
215
-
216
-	if (size > 0)
217
-		self->len += size;
218
-
219
-	return size;
220
-}
221
-
222
-static int
223
-str_append_printf (struct str *self, const char *fmt, ...)
224
-	ATTRIBUTE_PRINTF (2, 3);
225
-
226
-static int
227
-str_append_printf (struct str *self, const char *fmt, ...)
228
-{
229
-	va_list ap;
230
-
231
-	va_start (ap, fmt);
232
-	int size = str_append_vprintf (self, fmt, ap);
233
-	va_end (ap);
234
-	return size;
235
-}
236
-
237
-static void
238
-str_remove_slice (struct str *self, size_t start, size_t length)
239
-{
240
-	size_t end = start + length;
241
-	if (end > self->len)
242
-		end = self->len;
243
-	memmove (self->str + start, self->str + end, self->len - end);
244
-	self->str[self->len -= length] = '\0';
245
-
246
-	// Shrink the string if the allocation becomes way too large
247
-	if (self->alloc >= STR_SHRINK_THRESHOLD && self->len < (self->alloc >> 2))
248
-		self->str = xrealloc (self->str, self->alloc >>= 2);
249
-}
250
-
251
-// --- Utilities ---------------------------------------------------------------
252
-
253
-static bool
254
-set_blocking (int fd, bool blocking)
255
-{
256
-	int flags = fcntl (fd, F_GETFL);
257
-
258
-	bool prev = !(flags & O_NONBLOCK);
259
-	if (blocking)
260
-		flags &= ~O_NONBLOCK;
261
-	else
262
-		flags |=  O_NONBLOCK;
263
-
264
-	(void) fcntl (fd, F_SETFL, flags);
265
-	return prev;
266
-}
267
-
268
-static void
269
-xclose (int fd)
270
-{
271
-	while (close (fd) == -1)
272
-		if (errno != EINTR)
273
-			break;
274
-}
275
-
276
-static char *xstrdup_printf (const char *, ...) ATTRIBUTE_PRINTF (1, 2);
277
-
278
-static char *
279
-xstrdup_printf (const char *format, ...)
280
-{
281
-	va_list ap;
282
-	struct str tmp;
283
-	str_init (&tmp);
284
-	va_start (ap, format);
285
-	str_append_vprintf (&tmp, format, ap);
286
-	va_end (ap);
287
-	return str_steal (&tmp);
288
-}
289
-
290
-static char *
291
-format_host_port_pair (const char *host, const char *port)
292
-{
293
-	// IPv6 addresses mess with the "colon notation"; let's go with RFC 2732
294
-	if (strchr (host, ':'))
295
-		return xstrdup_printf ("[%s]:%s", host, port);
296
-	return xstrdup_printf ("%s:%s", host, port);
297
-}
298
-
299
-static bool
300
-xstrtoul (unsigned long *out, const char *s, int base)
301
-{
302
-	char *end;
303
-	errno = 0;
304
-	*out = strtoul (s, &end, base);
305
-	return errno == 0 && !*end && end != s;
306
-}
307
-
308
-// --- libuv-style write adaptor -----------------------------------------------
309
-
310
-// Makes it possible to use iovec to write multiple data chunks at once.
311
-
312
-typedef struct write_req write_req_t;
313
-struct write_req
314
-{
315
-	LIST_HEADER (write_req_t)
316
-	struct iovec data;                  ///< Data to be written
317
-};
318
-
319
-typedef struct write_queue write_queue_t;
320
-struct write_queue
321
-{
322
-	write_req_t *head;                  ///< The head of the queue
323
-	write_req_t *tail;                  ///< The tail of the queue
324
-	size_t head_offset;                 ///< Offset into the head
325
-	size_t len;
326
-};
327
-
328
-static void
329
-write_queue_init (struct write_queue *self)
330
-{
331
-	self->head = self->tail = NULL;
332
-	self->head_offset = 0;
333
-	self->len = 0;
334
-}
335
-
336
-static void
337
-write_queue_free (struct write_queue *self)
338
-{
339
-	for (write_req_t *iter = self->head, *next; iter; iter = next)
340
-	{
341
-		next = iter->next;
342
-		free (iter->data.iov_base);
343
-		free (iter);
344
-	}
345
-}
346
-
347
-static void
348
-write_queue_add (struct write_queue *self, write_req_t *req)
349
-{
350
-	LIST_APPEND_WITH_TAIL (self->head, self->tail, req);
351
-	self->len++;
352
-}
353
-
354
-static void
355
-write_queue_processed (struct write_queue *self, size_t len)
356
-{
357
-	while (self->head
358
-		&& self->head_offset + len >= self->head->data.iov_len)
359
-	{
360
-		write_req_t *head = self->head;
361
-		len -= (head->data.iov_len - self->head_offset);
362
-		self->head_offset = 0;
363
-
364
-		LIST_UNLINK_WITH_TAIL (self->head, self->tail, head);
365
-		self->len--;
366
-		free (head->data.iov_base);
367
-		free (head);
368
-	}
369
-	self->head_offset += len;
370
-}
371
-
372
-static bool
373
-write_queue_is_empty (struct write_queue *self)
374
-{
375
-	return self->head == NULL;
376
-}
377
-
378
-// --- Message reader ----------------------------------------------------------
379
-
380
-struct msg_reader
381
-{
382
-	struct str buf;                     ///< Input buffer
383
-	uint64_t offset;                    ///< Current offset in the buffer
384
-};
385
-
386
-static void
387
-msg_reader_init (struct msg_reader *self)
388
-{
389
-	str_init (&self->buf);
390
-	self->offset = 0;
391
-}
392
-
393
-static void
394
-msg_reader_free (struct msg_reader *self)
395
-{
396
-	str_free (&self->buf);
397
-}
398
-
399
-static void
400
-msg_reader_compact (struct msg_reader *self)
401
-{
402
-	str_remove_slice (&self->buf, 0, self->offset);
403
-	self->offset = 0;
404
-}
405
-
406
-static void
407
-msg_reader_feed (struct msg_reader *self, const void *data, size_t len)
408
-{
409
-	// TODO: have some mechanism to prevent flooding
410
-	msg_reader_compact (self);
411
-	str_append_data (&self->buf, data, len);
412
-}
413
-
414
-static void *
415
-msg_reader_get (struct msg_reader *self, size_t *len)
416
-{
417
-	// Try to read in the length of the message
418
-	if (self->offset + sizeof (uint64_t) > self->buf.len)
419
-		return NULL;
420
-
421
-	uint8_t *x = (uint8_t *) self->buf.str + self->offset;
422
-	uint64_t msg_len
423
-		= (uint64_t) x[0] << 56 | (uint64_t) x[1] << 48
424
-		| (uint64_t) x[2] << 40 | (uint64_t) x[3] << 32
425
-		| (uint64_t) x[4] << 24 | (uint64_t) x[5] << 16
426
-		| (uint64_t) x[6] << 8  | (uint64_t) x[7];
427
-
428
-	if (msg_len < sizeof msg_len)
429
-	{
430
-		// The message is shorter than its header
431
-		// TODO: have some mechanism to report errors
432
-		return NULL;
433
-	}
434
-
435
-	if (self->offset + msg_len < self->offset)
436
-	{
437
-		// Trying to read an insane amount of data but whatever
438
-		msg_reader_compact (self);
439
-		return NULL;
440
-	}
441
-
442
-	// Check if we've got the full message in the buffer and return it
443
-	if (self->offset + msg_len > self->buf.len)
444
-		return NULL;
445
-
446
-	// We have to subtract the header from the reported length
447
-	void *data = self->buf.str + self->offset + sizeof msg_len;
448
-	self->offset += msg_len;
449
-	*len = msg_len - sizeof msg_len;
450
-	return data;
451
-}
452
-
453
-// --- Message unpacker --------------------------------------------------------
454
-
455
-struct msg_unpacker
456
-{
457
-	const char *data;
458
-	size_t offset;
459
-	size_t len;
460
-};
461
-
462
-static void
463
-msg_unpacker_init (struct msg_unpacker *self, const void *data, size_t len)
464
-{
465
-	self->data = data;
466
-	self->len = len;
467
-	self->offset = 0;
468
-}
469
-
470
-static size_t
471
-msg_unpacker_get_available (struct msg_unpacker *self)
472
-{
473
-	return self->len - self->offset;
474
-}
475
-
476
-#define UNPACKER_INT_BEGIN                                                     \
477
-	if (self->len - self->offset < sizeof *value)                              \
478
-		return false;                                                          \
479
-	uint8_t *x = (uint8_t *) self->data + self->offset;                        \
480
-	self->offset += sizeof *value;
481
-
482
-static bool
483
-msg_unpacker_u8 (struct msg_unpacker *self, uint8_t *value)
484
-{
485
-	UNPACKER_INT_BEGIN
486
-	*value = x[0];
487
-	return true;
488
-}
489
-
490
-static bool
491
-msg_unpacker_i32 (struct msg_unpacker *self, int32_t *value)
492
-{
493
-	UNPACKER_INT_BEGIN
494
-	*value
495
-		= (uint32_t) x[0] << 24 | (uint32_t) x[1] << 16
496
-		| (uint32_t) x[2] << 8  | (uint32_t) x[3];
497
-	return true;
498
-}
499
-
500
-static bool
501
-msg_unpacker_u64 (struct msg_unpacker *self, uint64_t *value)
502
-{
503
-	UNPACKER_INT_BEGIN
504
-	*value
505
-		= (uint64_t) x[0] << 56 | (uint64_t) x[1] << 48
506
-		| (uint64_t) x[2] << 40 | (uint64_t) x[3] << 32
507
-		| (uint64_t) x[4] << 24 | (uint64_t) x[5] << 16
508
-		| (uint64_t) x[6] << 8  | (uint64_t) x[7];
509
-	return true;
510
-}
511
-
512
-#undef UNPACKER_INT_BEGIN
513
-
514
-// --- Message packer and writer -----------------------------------------------
515
-
516
-struct msg_writer
517
-{
518
-	struct str buf;                     ///< Holds the message data
519
-};
520
-
521
-static void
522
-msg_writer_init (struct msg_writer *self)
523
-{
524
-	str_init (&self->buf);
525
-	// Placeholder for message length
526
-	str_append_data (&self->buf, "\x00\x00\x00\x00" "\x00\x00\x00\x00", 8);
527
-}
528
-
529
-static void
530
-msg_writer_u8 (struct msg_writer *self, uint8_t x)
531
-{
532
-	str_append_data (&self->buf, &x, 1);
533
-}
534
-
535
-static void
536
-msg_writer_i32 (struct msg_writer *self, int32_t x)
537
-{
538
-	uint32_t u = x;
539
-	uint8_t tmp[4] = { u >> 24, u >> 16, u >> 8, u };
540
-	str_append_data (&self->buf, tmp, sizeof tmp);
541
-}
542
-
543
-static void
544
-msg_writer_u64 (struct msg_writer *self, uint64_t x)
545
-{
546
-	uint8_t tmp[8] =
547
-		{ x >> 56, x >> 48, x >> 40, x >> 32, x >> 24, x >> 16, x >> 8, x };
548
-	str_append_data (&self->buf, tmp, sizeof tmp);
549
-}
550
-
551
-static void *
552
-msg_writer_flush (struct msg_writer *self, size_t *len)
553
-{
554
-	// Update the message length
555
-	uint64_t x = self->buf.len;
556
-	uint8_t tmp[8] =
557
-		{ x >> 56, x >> 48, x >> 40, x >> 32, x >> 24, x >> 16, x >> 8, x };
558
-	memcpy (self->buf.str, tmp, sizeof tmp);
559
-
560
-	*len = x;
561
-	return str_steal (&self->buf);
562
-}
563
-
564
-// --- Option handler ----------------------------------------------------------
565
-
566
-// Simple wrapper for the getopt_long API to make it easier to use and maintain.
567
-
568
-#define OPT_USAGE_ALIGNMENT_COLUMN 30   ///< Alignment for option descriptions
569
-
570
-enum
571
-{
572
-	OPT_OPTIONAL_ARG  = (1 << 0),       ///< The argument is optional
573
-	OPT_LONG_ONLY     = (1 << 1)        ///< Ignore the short name in opt_string
574
-};
575
-
576
-// All options need to have both a short name, and a long name.  The short name
577
-// is what is returned from opt_handler_get().  It is possible to define a value
578
-// completely out of the character range combined with the OPT_LONG_ONLY flag.
579
-//
580
-// When `arg_hint' is defined, the option is assumed to have an argument.
581
-
582
-struct opt
583
-{
584
-	int short_name;                     ///< The single-letter name
585
-	const char *long_name;              ///< The long name
586
-	const char *arg_hint;               ///< Option argument hint
587
-	int flags;                          ///< Option flags
588
-	const char *description;            ///< Option description
589
-};
590
-
591
-struct opt_handler
592
-{
593
-	int argc;                           ///< The number of program arguments
594
-	char **argv;                        ///< Program arguments
595
-
596
-	const char *arg_hint;               ///< Program arguments hint
597
-	const char *description;            ///< Description of the program
598
-
599
-	const struct opt *opts;             ///< The list of options
600
-	size_t opts_len;                    ///< The length of the option array
601
-
602
-	struct option *options;             ///< The list of options for getopt
603
-	char *opt_string;                   ///< The `optstring' for getopt
604
-};
605
-
606
-static void
607
-opt_handler_free (struct opt_handler *self)
608
-{
609
-	free (self->options);
610
-	free (self->opt_string);
611
-}
612
-
613
-static void
614
-opt_handler_init (struct opt_handler *self, int argc, char **argv,
615
-	const struct opt *opts, const char *arg_hint, const char *description)
616
-{
617
-	memset (self, 0, sizeof *self);
618
-	self->argc = argc;
619
-	self->argv = argv;
620
-	self->arg_hint = arg_hint;
621
-	self->description = description;
622
-
623
-	size_t len = 0;
624
-	for (const struct opt *iter = opts; iter->long_name; iter++)
625
-		len++;
626
-
627
-	self->opts = opts;
628
-	self->opts_len = len;
629
-	self->options = xcalloc (len + 1, sizeof *self->options);
630
-
631
-	struct str opt_string;
632
-	str_init (&opt_string);
633
-
634
-	for (size_t i = 0; i < len; i++)
635
-	{
636
-		const struct opt *opt = opts + i;
637
-		struct option *mapped = self->options + i;
638
-
639
-		mapped->name = opt->long_name;
640
-		if (!opt->arg_hint)
641
-			mapped->has_arg = no_argument;
642
-		else if (opt->flags & OPT_OPTIONAL_ARG)
643
-			mapped->has_arg = optional_argument;
644
-		else
645
-			mapped->has_arg = required_argument;
646
-		mapped->val = opt->short_name;
647
-
648
-		if (opt->flags & OPT_LONG_ONLY)
649
-			continue;
650
-
651
-		str_append_c (&opt_string, opt->short_name);
652
-		if (opt->arg_hint)
653
-		{
654
-			str_append_c (&opt_string, ':');
655
-			if (opt->flags & OPT_OPTIONAL_ARG)
656
-				str_append_c (&opt_string, ':');
657
-		}
658
-	}
659
-
660
-	self->opt_string = str_steal (&opt_string);
661
-}
662
-
663
-static void
664
-opt_handler_usage (struct opt_handler *self, FILE *stream)
665
-{
666
-	struct str usage;
667
-	str_init (&usage);
668
-
669
-	str_append_printf (&usage, "Usage: %s [OPTION]... %s\n",
670
-		self->argv[0], self->arg_hint ? self->arg_hint : "");
671
-	str_append_printf (&usage, "%s\n\n", self->description);
672
-
673
-	for (size_t i = 0; i < self->opts_len; i++)
674
-	{
675
-		struct str row;
676
-		str_init (&row);
677
-
678
-		const struct opt *opt = self->opts + i;
679
-		if (!(opt->flags & OPT_LONG_ONLY))
680
-			str_append_printf (&row, "  -%c, ", opt->short_name);
681
-		else
682
-			str_append (&row, "      ");
683
-		str_append_printf (&row, "--%s", opt->long_name);
684
-		if (opt->arg_hint)
685
-			str_append_printf (&row, (opt->flags & OPT_OPTIONAL_ARG)
686
-				? " [%s]" : " %s", opt->arg_hint);
687
-
688
-		// TODO: keep the indent if there are multiple lines
689
-		if (row.len + 2 <= OPT_USAGE_ALIGNMENT_COLUMN)
690
-		{
691
-			str_append (&row, "  ");
692
-			str_append_printf (&usage, "%-*s%s\n",
693
-				OPT_USAGE_ALIGNMENT_COLUMN, row.str, opt->description);
694
-		}
695
-		else
696
-			str_append_printf (&usage, "%s\n%-*s%s\n", row.str,
697
-				OPT_USAGE_ALIGNMENT_COLUMN, "", opt->description);
698
-
699
-		str_free (&row);
700
-	}
701
-
702
-	fputs (usage.str, stream);
703
-	str_free (&usage);
704
-}
705
-
706
-static int
707
-opt_handler_get (struct opt_handler *self)
708
-{
709
-	return getopt_long (self->argc, self->argv,
710
-		self->opt_string, self->options, NULL);
711
-}

Loading…
Cancel
Save