ntdisp 

ntdisp Git Source Tree

Root/src/lib/ntdisp-utils.c

1/* ntdisp - NtD In System Programming.
2 *
3 * Copyright (C) 2012 Nicola Fontana <ntd at entidi.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20/**
21 * SECTION:ntdisp-utils
22 * @Section_Id:utilities
23 * @title: Utilities
24 * @short_description: Assorted macros and functions
25 *
26 * Collection of macros and functions that do not fit inside any other topic.
27 *
28 * Since: 1.0
29 **/
30
31
32#include <glib.h>
33#include <glib-object.h>
34#include <string.h>
35#include <stdio.h>
36
37#include "../config.h"
38#include "ntdisp-intl.h"
39#include "ntdisp-utils.h"
40
41
42/**
43 * ntd_empty:
44 * @string: char pointer
45 *
46 * Checks wheter @string is empty, that is if @string is %NULL
47 * or if its first char is '\0'.
48 *
49 * Returns: %TRUE is @string is %NULL or if @string[0] is '\0',
50 * %FALSE otherwise.
51 **/
52
53/**
54 * NtdSystemError:
55 * @NTD_SYSTEM_ERROR_NONE: No error present
56 * @NTD_SYSTEM_ERROR_FAILED: Generic failure
57 * @NTD_SYSTEM_ERROR_PERM: Operation not permitted
58 * @NTD_SYSTEM_ERROR_NOENT: No such file or directory
59 * @NTD_SYSTEM_ERROR_SRCH: No such process
60 * @NTD_SYSTEM_ERROR_INTR: Interrupted system call
61 * @NTD_SYSTEM_ERROR_IO: I/O error
62 * @NTD_SYSTEM_ERROR_NXIO: No such device or address
63 * @NTD_SYSTEM_ERROR_2BIG: Argument list too long
64 * @NTD_SYSTEM_ERROR_NOEXEC: Exec format error
65 * @NTD_SYSTEM_ERROR_BADF: Bad file number
66 * @NTD_SYSTEM_ERROR_CHILD: No child processes
67 * @NTD_SYSTEM_ERROR_AGAIN: Try again
68 * @NTD_SYSTEM_ERROR_NOMEM: Out of memory
69 * @NTD_SYSTEM_ERROR_ACCES: Permission denied
70 * @NTD_SYSTEM_ERROR_FAULT: Bad address
71 * @NTD_SYSTEM_ERROR_BUSY: Device or resource busy
72 * @NTD_SYSTEM_ERROR_EXIST: File exists
73 * @NTD_SYSTEM_ERROR_XDEV: Cross-device link
74 * @NTD_SYSTEM_ERROR_NODEV: No such device
75 * @NTD_SYSTEM_ERROR_NOTDIR: Not a directory
76 * @NTD_SYSTEM_ERROR_ISDIR: Is a directory
77 * @NTD_SYSTEM_ERROR_INVAL: Invalid argument
78 * @NTD_SYSTEM_ERROR_NFILE: File table overflow
79 * @NTD_SYSTEM_ERROR_MFILE: Too many open files
80 * @NTD_SYSTEM_ERROR_NOTTY: Not a typewriter
81 * @NTD_SYSTEM_ERROR_TXTBSY: Text file busy
82 * @NTD_SYSTEM_ERROR_FBIG: File too large
83 * @NTD_SYSTEM_ERROR_NOSPC: No space left on device
84 * @NTD_SYSTEM_ERROR_SPIPE: Illegal seek
85 * @NTD_SYSTEM_ERROR_ROFS: Read-only file system
86 * @NTD_SYSTEM_ERROR_MLINK: Too many links
87 * @NTD_SYSTEM_ERROR_PIPE: Broken pipe
88 * @NTD_SYSTEM_ERROR_DOM: Math argument out of domain of func
89 * @NTD_SYSTEM_ERROR_RANGE: Math result not representable
90 * @NTD_SYSTEM_ERROR_DEADLK: Resource deadlock would occur
91 * @NTD_SYSTEM_ERROR_NAMETOOLONG: File name too long
92 * @NTD_SYSTEM_ERROR_NOLCK: No record locks available
93 * @NTD_SYSTEM_ERROR_NOSYS: Function not implemented
94 * @NTD_SYSTEM_ERROR_NOTEMPTY: Directory not empty
95 * @NTD_SYSTEM_ERROR_LOOP: Too many symbolic links encountered
96 * @NTD_SYSTEM_ERROR_NOMSG: No message of desired type
97 * @NTD_SYSTEM_ERROR_IDRM: Identifier removed
98 * @NTD_SYSTEM_ERROR_DEADLOCKEDEADLK
99 * @NTD_SYSTEM_ERROR_NOSTR: Device not a stream
100 * @NTD_SYSTEM_ERROR_NODATA: No data available
101 * @NTD_SYSTEM_ERROR_TIME: Timer expired
102 * @NTD_SYSTEM_ERROR_NOSR: Out of streams resources
103 * @NTD_SYSTEM_ERROR_PROTO: Protocol error
104 * @NTD_SYSTEM_ERROR_BADMSG: Not a data message
105 * @NTD_SYSTEM_ERROR_OVERFLOW: Value too large for defined data type
106 * @NTD_SYSTEM_ERROR_ILSEQ: Illegal byte sequence
107 * @NTD_SYSTEM_ERROR_NOTSOCK: Socket operation on non-socket
108 * @NTD_SYSTEM_ERROR_DESTADDRREQ: Destination address required
109 * @NTD_SYSTEM_ERROR_MSGSIZE: Message too long
110 * @NTD_SYSTEM_ERROR_PROTOTYPE: Protocol wrong type for socket
111 * @NTD_SYSTEM_ERROR_NOPROTOOPT: Protocol not available
112 * @NTD_SYSTEM_ERROR_PROTONOSUPPORT: Protocol not supported
113 * @NTD_SYSTEM_ERROR_OPNOTSUPP: Operation not supported on transport endpoint
114 * @NTD_SYSTEM_ERROR_NOTSUP: Operation not supported on transport endpoint
115 * @NTD_SYSTEM_ERROR_AFNOSUPPORT: Address family not supported by protocol
116 * @NTD_SYSTEM_ERROR_ADDRINUSE: Address already in use
117 * @NTD_SYSTEM_ERROR_ADDRNOTAVAIL: Cannot assign requested address
118 * @NTD_SYSTEM_ERROR_NETDOWN: Network is down
119 * @NTD_SYSTEM_ERROR_NETUNREACH: Network is unreachable
120 * @NTD_SYSTEM_ERROR_NETRESET: Network dropped connection because of reset
121 * @NTD_SYSTEM_ERROR_NOTCONN: Transport endpoint is not connected
122 * @NTD_SYSTEM_ERROR_CONNABORTED: Software caused connection abort
123 * @NTD_SYSTEM_ERROR_CONNRESET: Connection reset by peer
124 * @NTD_SYSTEM_ERROR_NOBUFS: No buffer space available
125 * @NTD_SYSTEM_ERROR_ISCONN: Transport endpoint is already connected
126 * @NTD_SYSTEM_ERROR_TIMEDOUT: Connection timed out
127 * @NTD_SYSTEM_ERROR_CONNREFUSED: Connection refused
128 * @NTD_SYSTEM_ERROR_HOSTUNREACH: No route to host
129 * @NTD_SYSTEM_ERROR_ALREADY: Operation already in progress
130 * @NTD_SYSTEM_ERROR_INPROGRESS: Operation now in progress
131 * @NTD_SYSTEM_ERROR_CANCELED: Operation Canceled
132 *
133 * Abstraction of the platform dependent error codes. This
134 * implementation should be in-line with POSIX.1-2001.
135 **/
136
137
138GQuark
139ntd_system_error_quark(void)
140{
141 static GQuark error_quark = 0;
142
143 if (G_UNLIKELY(! error_quark))
144 error_quark = g_quark_from_static_string("NtdSystemError");
145
146 return error_quark;
147}
148
149
150static void
151print_handler(const gchar *log_domain, GLogLevelFlags log_level,
152 const gchar *message, gpointer user_data)
153{
154 g_print("%s\n", message);
155}
156
157
158/**
159 * ntd_system_set_errorv_with_code:
160 * @error: a pointer to the destination #GError*.
161 * @error_code: the specific system error code to set.
162 * @format: printf-style format string.
163 * @args: parameters for format string.
164 *
165 * Wrapper to ntd_system_set_error_with_code() with #va_list argument.
166 **/
167void
168ntd_system_set_errorv_with_code(GError **error,
169 NtdSystemError error_code,
170 const gchar *format,
171 va_list args)
172{
173 gchar *message;
174
175 g_return_if_fail(error);
176
177 if (error_code == NTD_SYSTEM_ERROR_NONE) {
178 g_clear_error(error);
179 return;
180 }
181
182 ntd_return_on_pileup(*error);
183
184 /* Retrieving the system error description */
185 message = ntd_system_get_error_message(error_code);
186 if (! message)
187 message = g_strdup_printf(_("Unknown system error `%d'"), error_code);
188
189 /* Prepending the custom message */
190 if (format && format[0]) {
191 gchar *new_message;
192 gchar *custom_message;
193
194 custom_message = g_strdup_vprintf(format, args);
195 new_message = g_strconcat(custom_message, ": ", message, NULL);
196
197 g_free(custom_message);
198 g_free(message);
199
200 message = new_message;
201 }
202
203 g_set_error(error, NTD_SYSTEM_ERROR, error_code, "%s", message);
204
205 g_free(message);
206}
207
208/**
209 * ntd_system_set_error_with_code:
210 * @error: a pointer to the destination #GError*.
211 * @error_code: the specific system error code to set.
212 * @format: printf-style format string.
213 * @...: parameters for format string.
214 *
215 * Sets @error to the specified system error, exactly
216 * in the same way the ntd_system_set_error() function does.
217 *
218 * Of course, in this case the @error_code is not system
219 * dependent, so no ntd_system_clear_error() call is performed
220 * and nothing is returned.
221 *
222 * Calling this function with #NTD_SYSTEM_ERROR_NONE as the
223 * @error_code it is equivalent to call g_clear_error() on @error.
224 */
225void
226ntd_system_set_error_with_code(GError **error,
227 NtdSystemError error_code,
228 const gchar *format,
229 ...)
230{
231 va_list args;
232
233 va_start(args, format);
234 ntd_system_set_errorv_with_code(error, error_code, format, args);
235 va_end(args);
236}
237
238/**
239 * ntd_system_set_errorv:
240 * @error: a pointer to the destination #GError*.
241 * @format: printf-style format string.
242 * @args: parameters for format string.
243 *
244 * Wrapper to ntd_system_set_error() with #va_list argument.
245 *
246 * Returns: %TRUE if there is a system error, %FALSE otherwise.
247 **/
248gboolean
249ntd_system_set_errorv(GError **error,
250 const gchar *format,
251 va_list args)
252{
253 NtdSystemError error_code;
254
255 error_code = ntd_system_get_error();
256 if (error_code == NTD_SYSTEM_ERROR_NONE)
257 return FALSE;
258
259 ntd_system_clear_error();
260 ntd_system_set_errorv_with_code(error, error_code, format, args);
261 return TRUE;
262}
263
264/**
265 * ntd_system_set_error:
266 * @error: a pointer to the destination #GError*.
267 * @format: printf-style format string.
268 * @...: parameters for format string.
269 *
270 * Sets @error to the properly system error, if there is
271 * one active. The optional format string is appended
272 * to the standard system error message.
273 * If there are no system errors, this function does
274 * nothing and return %FALSE.
275 *
276 * After the error is set, a ntd_system_clear_error() call
277 * is performed to clear the system error.
278 *
279 * Returns: %TRUE if there is a system error, %FALSE otherwise.
280 */
281gboolean
282ntd_system_set_error(GError **error,
283 const gchar *format,
284 ...)
285{
286 gboolean result;
287 va_list args;
288
289 va_start(args, format);
290 result = ntd_system_set_errorv(error, format, args);
291 va_end(args);
292
293 return result;
294}
295
296/**
297 * ntd_set_verbose_mode:
298 * @new_status: new verbosity status
299 *
300 * Enables (if @new_status is %TRUE) or disables (if @new_status
301 * is %FALSE) the verbosity mode. In verbose mode, data sent to
302 * or received from the device is output to stdout.
303 **/
304void
305ntd_set_verbose_mode(gboolean new_status)
306{
307 static guint handler = 0;
308
309 if (new_status && handler == 0) {
310 handler = g_log_set_handler(G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
311 print_handler, NULL);
312 } else if (! new_status && handler != 0) {
313 g_log_remove_handler(G_LOG_DOMAIN, handler);
314 handler = 0;
315 }
316}
317
318/**
319 * ntd_escape:
320 * @buffer: a buffer
321 * @nbytes: size of the buffer
322 *
323 * Generates a printable representation of the first @nbytes
324 * bytes of @buffer. This is done by escaping all non-printable
325 * chars.
326 *
327 * The buffer is split on the '\0' delimiters. Every segment in
328 * the resulting list of strings is then escaped with
329 * g_strescape(). The final list is then joined with the "\\000"
330 * string (octal representation of '\0'): at the end, all non-printable
331 * characters (NUL bytes included) are properly escaped.
332 *
333 * Returns: a newly allocated string: free with g_free()
334 * when no longer needed.
335 **/
336gchar *
337ntd_escape(gconstpointer buffer, gsize nbytes)
338{
339 const gchar *p1, *p2;
340 GSList *slist, *p_slist;;
341 gint n;
342 gchar **array;
343 gchar *tmp_str;
344
345 g_return_val_if_fail(buffer != NULL, NULL);
346
347 if (nbytes == 0)
348 return g_strdup("");
349
350 p1 = buffer;
351 slist = NULL;
352 n = 0;
353 for (p1 = buffer; (p2 = memchr(p1, '\0', nbytes)) != NULL; p1 = p2 + 1) {
354 slist = g_slist_prepend(slist, g_strescape(p1, NULL));
355 nbytes -= p2 - p1 + 1;
356 p1 = p2 + 1;
357 ++n;
358 }
359
360 /* The last string is never NUL terminated. If the buffer coincidentally
361 * ends with '\0', a new (empty) string must be added anyway. */
362 tmp_str = g_malloc(nbytes + 1);
363 memcpy(tmp_str, p1, nbytes);
364 tmp_str[nbytes] = '\0';
365 slist = g_slist_prepend(slist, g_strescape(tmp_str, NULL));
366 g_free(tmp_str);
367 ++n;
368
369 /* Create the string array from the GSList */
370 array = g_new(gchar*, n + 1);
371 array[n--] = NULL;
372 for (p_slist = slist; p_slist != NULL; p_slist = p_slist->next)
373 array[n--] = p_slist->data;
374 g_slist_free(slist);
375
376 tmp_str = g_strjoinv("\\000", array);
377 g_strfreev(array);
378 return tmp_str;
379}
380
381/**
382 * ntd_checksum:
383 * @data: binary buffer
384 * @nbytes: size (in bytes) of the data
385 *
386 * Sums the first @nbytes bytes of @data and returns the result.
387 * No checks against arithmetic overflows are performed.
388 *
389 * Returns: the computed checksum
390 **/
391gulong
392ntd_checksum(gconstpointer data, gsize nbytes)
393{
394 gulong result;
395 const guchar *ptr;
396
397 g_return_val_if_fail(data != NULL, 0);
398 g_return_val_if_fail(nbytes > 0, 0);
399
400 result = 0;
401 ptr = data;
402 while (nbytes --) {
403 result += *ptr;
404 ++ ptr;
405 }
406
407 return result;
408}
409
410/**
411 * ntd_find_file:
412 * @file: the file to search
413 * @...: a NULL terminated list of paths where to look for
414 * file existence.
415 *
416 * Searches @file in the provided paths and returns the full
417 * path to the first existing match. The check is performed
418 * using g_file_test() with the G_FILE_TEST_EXISTS test.
419 *
420 * Returns: a newly allocated string containing the path
421 * or NULL on errors. Free it with g_free() when
422 * no longer needed.
423 **/
424gchar *
425ntd_find_file(const gchar *file, ...)
426{
427 va_list var_args;
428 gchar *path;
429 const gchar *base;
430
431 va_start(var_args, file);
432
433 while ((base = va_arg(var_args, const gchar *)) != NULL) {
434 path = g_build_filename(base, file, NULL);
435 if (g_file_test(path, G_FILE_TEST_EXISTS))
436 return path;
437 g_free(path);
438 }
439
440 return NULL;
441}
442
443/**
444 * ntd_string_free:
445 * @string: a #GString
446 * @p_buffer: destination of the buffer or %NULL
447 * @p_len: destionation of the buffer length or %NULL
448 *
449 * A g_string_free() version that returns either the buffer in
450 * whatever @p_buffer and its length in @p_len. Both of them
451 * can be %NULL, in which case the data is not returned (but
452 * it is freed anyway).
453 *
454 * Furthermore, the case where @string is %NULL is handled
455 * gracefully: @p_buffer and @p_len are set to %NULL and 0
456 * respectively (if possible).
457 **/
458void
459ntd_string_free(GString *string, gpointer *p_buffer, gsize *p_len)
460{
461 gsize len = string != NULL ? string->len : 0;
462
463 if (p_len != NULL)
464 *p_len = len;
465
466 if (p_buffer != NULL) {
467 if (len > 0) {
468 /* Ownership of buffer->str changed:
469 * free_segment must be FALSE */
470 *p_buffer = string->str;
471 g_string_free(string, FALSE);
472 string = NULL;
473 } else {
474 *p_buffer = NULL;
475 }
476 }
477
478 if (string != NULL)
479 g_string_free(string, TRUE);
480}
481
482/**
483 * ntd_free_and_nullify:
484 * @mem: pointer to the pointer to free.
485 *
486 * Convenient function to free @mem with g_free() and set
487 * it to %NULL.
488 **/
489void
490ntd_free_and_nullify(gpointer *mem)
491{
492 if (*mem) {
493 g_free(*mem);
494 *mem = NULL;
495 }
496}
497
498static guchar
499ENCODE(guchar ch)
500{
501 return ch == 0 ? 96 : ch+32;
502}
503
504/**
505 * ntd_uuencode:
506 * @data: original binary buffer
507 * @nbytes: size (in bytes) of the data to convert
508 *
509 * Converts @nbytes bytes from @data to their uu-encoded version.
510 *
511 * Returns: the uu-encoded '\0' terminated result of the conversion
512 * or %NULL on errors. Free the returned string with g_free()
513 * when no longer needed.
514 **/
515gchar *
516ntd_uuencode(gconstpointer data, gsize nbytes)
517{
518 GString *encoded;
519 const guchar *ptr;
520 gssize n;
521 guchar ch1, ch2, ch3;
522
523 g_return_val_if_fail(data != NULL, NULL);
524
525 encoded = g_string_sized_new(nbytes);
526 g_string_assign(encoded, "");
527 ptr = data;
528
529 while (nbytes > 0) {
530 n = MIN(45, nbytes);
531 nbytes -= n;
532 g_string_append_c(encoded, ENCODE(n));
533
534 while (n > 0) {
535 ch1 = *ptr++; --n;
536 ch2 = n > 0 ? *ptr++ : 0; --n;
537 ch3 = n > 0 ? *ptr++ : 0; --n;
538 g_string_append_c(encoded, ENCODE(ch1 >> 2));
539 g_string_append_c(encoded, ENCODE(((ch1 & 0x3) << 4) + (ch2 >> 4)));
540 g_string_append_c(encoded, ENCODE(((ch2 & 0xf) << 2) + (ch3 >> 6)));
541 g_string_append_c(encoded, ENCODE(ch3 & 0x3f));
542 }
543
544 g_string_append_c(encoded, '\n');
545 }
546
547 return g_string_free(encoded, FALSE);
548}
549
550static gint
551DECODE(guchar ch)
552{
553 if (ch > 96 || ch < 32)
554 return -1;
555 return ch == 96 ? 0 : ch - 32;
556}
557
558static const gchar *
559ntd_uudecode_row(const gchar *row, GString *dst)
560{
561 gint n, ch1, ch2, ch3, ch4;
562
563 n = DECODE(*row++);
564 if (n < 0)
565 return NULL;
566
567 while (n > 0) {
568 ch1 = DECODE(*row++);
569 ch2 = DECODE(*row++);
570 ch3 = DECODE(*row++);
571 ch4 = DECODE(*row++);
572 if (ch1 == -1 || ch2 == -1 || ch3 == -1 || ch4 == -1)
573 return NULL;
574
575 g_string_append_c(dst, (ch1 << 2) + (ch2 >> 4));
576 --n;
577 if (n > 0) {
578 g_string_append_c(dst, ((ch2 & 0xf) << 4) + (ch3 >> 2));
579 --n;
580 }
581 if (n > 0) {
582 g_string_append_c(dst, ((ch3 & 0x3) << 6) + ch4);
583 --n;
584 }
585 }
586
587 return row;
588}
589
590/**
591 * ntd_uudecode:
592 * @encoded: '\0' terminated string of UU-encoded data
593 * @p_data: pointer to the destination buffer to allocate
594 * @p_nbytes: pointer to the destination size (in bytes) of the data
595 *
596 * Decodes the @encoded string and returns the decoded result
597 * in @p_nbytes and @p_data. Either @p_nbytes and @p_data can
598 * be %NULL, in which case the respective result will not be
599 * returned.
600 *
601 * Returns: %TRUE on success, %FALSE on errors.
602 **/
603gboolean
604ntd_uudecode(const gchar *encoded, gpointer *p_data, gsize *p_nbytes)
605{
606 GString *decoded;
607 const gchar *ptr;
608
609 g_return_val_if_fail(encoded != NULL, FALSE);
610
611 decoded = g_string_sized_new(strlen(encoded) / 4 * 3);
612 g_string_assign(decoded, "");
613 ptr = encoded;
614
615 while (*ptr) {
616 ptr = ntd_uudecode_row(ptr, decoded);
617 if (ptr == NULL)
618 break;
619
620 /* Skip paddings and newlines */
621 while (*ptr == '`' || *ptr == '\n' || *ptr == '\r')
622 ++ptr;
623 }
624
625 if (ptr == NULL) {
626 g_string_free(decoded, TRUE);
627 return FALSE;
628 }
629
630 if (p_nbytes)
631 *p_nbytes = decoded->len;
632
633 if (p_data) {
634 *p_data = g_string_free(decoded, FALSE);
635 } else {
636 g_string_free(decoded, TRUE);
637 }
638
639 return TRUE;
640}
641
642#if ! GLIB_CHECK_VERSION(2, 34, 0)
643/**
644 * g_type_ensure:
645 * @type: a #GType.
646 *
647 * Backward compatibility function that ensures that the indicated
648 * @type has been registered with the type system, and its _class_init()
649 * method has been run.
650 */
651void
652g_type_ensure (GType type)
653{
654 if (G_UNLIKELY(type == (GType) -1))
655 g_error("can't happen");
656}
657#endif

Archive Download this file

Branches

Tags