From 90c9dd5a5ada2623502777988814fcc250aa8977 Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Sat, 27 Aug 2016 19:42:15 -0400 Subject: [PATCH] pwmc: Add .listkeys command. To show human readable output of the LISTKEYS protocol command. --- doc/pwmc.1.in | 10 +- src/Makefile.am | 2 +- src/pwmc.c | 347 ++++++++++++++++++++++++++++++++ src/util-slist.c | 114 +++++++++++ src/util-slist.h | 35 ++++ src/util-string.c | 592 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util-string.h | 64 ++++++ 7 files changed, 1162 insertions(+), 2 deletions(-) create mode 100644 src/util-slist.c create mode 100644 src/util-slist.h create mode 100644 src/util-string.c create mode 100644 src/util-string.h diff --git a/doc/pwmc.1.in b/doc/pwmc.1.in index c9e112dc..8ec6d28c 100644 --- a/doc/pwmc.1.in +++ b/doc/pwmc.1.in @@ -20,7 +20,7 @@ \\$2 \(laURL: \\$1 \(ra\\$3 .. .if \n[.g] .mso www.tmac -.TH PWMC 1 "20 Aug 2016" "@VERSION@" "Password Manager Client" +.TH PWMC 1 "27 Aug 2016" "@VERSION@" "Password Manager Client" .SH NAME pwmc \- send a command to a pwmd server @@ -456,6 +456,7 @@ is specified, the option .B name is unset. .RS + .TP .I passphrase-file [] Obtain the passphrase from @@ -464,6 +465,7 @@ for the next command requiring a passphrase. This is equivalent to the .I "\-\-passphrase-file" command line option. This value will be unset after the next protocol command to prevent misuse. + .TP .I new-passphrase-file [] Obtain the passphrase from @@ -472,6 +474,7 @@ for the next command requiring a new passphrase. This is equivalent to the .I "\-\-new-passphrase-file" command line option. This value will be unset after the next protocol command to prevent misuse. + .TP .I sign-passphrase-file [] Obtain the passphrase from @@ -482,6 +485,7 @@ equivalent to the command line option and is optionally used for signing a symmetrically encrypted data file. This value will be unset after the next protocol command to prevent misuse. + .TP .I pinentry-timeout Set the amount of seconds before the pinentry program will close and return an @@ -507,6 +511,10 @@ pinentry or not. For symmetrically encrypted data files, use the command instead. .TP +.I .listkeys [--options] [pattern[,...]] +Display human readable output of the LISTKEYS protocol command. + +.TP .I .help Show available interactive commands. diff --git a/src/Makefile.am b/src/Makefile.am index 6a022414..9990501b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -33,7 +33,7 @@ libpwmd_la_LDFLAGS += -lm endif bin_PROGRAMS = pwmc -pwmc_SOURCES = pwmc.c +pwmc_SOURCES = pwmc.c util-string.c util-string.h util-slist.c util-slist.h if NEED_GETOPT_LONG pwmc_SOURCES += getopt_long.c getopt_long.h endif diff --git a/src/pwmc.c b/src/pwmc.c index 8e629a2a..65877b83 100644 --- a/src/pwmc.c +++ b/src/pwmc.c @@ -40,6 +40,9 @@ #include #include +#include "util-string.h" +#include "util-slist.h" + #ifdef WITH_GNUTLS #include #endif @@ -118,6 +121,31 @@ struct inquire_s char *last_keyword; }; +struct userid_s { + char *userid; + char *name; + char *email; + char *comment; +}; + +struct keyid_s { + char *keyid; + char *fpr; + char *grip; + char *card; + int can_sign; + int can_encrypt; + int can_certify; + int can_auth; + int expired; + time_t expires; + time_t created; + int secret; + int revoked; + struct slist_s *userids; + struct slist_s *subkeys; +}; + static gpg_error_t finalize (); static gpg_error_t set_inquire (int fd, const char *line, struct inquire_s **result); @@ -985,6 +1013,9 @@ help_command (const char *line) "\n" " .passwd [args]\n" " change the passphrase of a data file\n" + "\n" + " .listkeys [--options] [pattern[,..]]\n" + " show human readable output of the LISTKEYS command\n" "\n" " .help\n" " this help text\n")); @@ -1217,6 +1248,320 @@ save_command (const char *line) return do_save_passwd_command (line, 1); } +static void +search_and_replace (char *str, const char *s, const char r) +{ + char *p; + + p = strstr (str, s); + if (p) + { + *p = r; + while (*++p) + *p = *(p+1); + search_and_replace (str, s, r); + return; + } +} + +static char * +openpgp_unescape (char *str) +{ + search_and_replace (str, "\\x3a", ':'); + search_and_replace (str, "\\x5c", '\\'); + return str; +} + +static void +free_listkeys (struct slist_s *keys) +{ + unsigned i, t; + + t = slist_length (keys); + for (i = 0; i < t; i++) + { + struct keyid_s *key = slist_nth_data (keys, i); + unsigned n, nt; + + nt = slist_length (key->userids); + for (n = 0; n < nt; n++) + { + struct userid_s *userid = slist_nth_data (key->userids, n); + + pwmd_free (userid->userid); + pwmd_free (userid->name); + pwmd_free (userid->email); + pwmd_free (userid->comment); + pwmd_free (userid); + } + + slist_free (key->userids); + nt = slist_length (key->subkeys); + for (n = 0; n < nt; n++) + { + struct keyid_s *sub = slist_nth_data (key->subkeys, n); + + pwmd_free (sub->keyid); + pwmd_free (sub->fpr); + pwmd_free (sub->grip); + pwmd_free (sub->card); + pwmd_free (sub); + } + + slist_free (key->subkeys); + pwmd_free (key); + } + + slist_free (keys); +} + +static void +display_listkeys (struct slist_s *keys) +{ + unsigned i, t; + + t = slist_length (keys); + for (i = 0; i < t; i++) + { + struct keyid_s *key = slist_nth_data (keys, i); + unsigned st = slist_length (key->subkeys); + unsigned s; + unsigned ut = slist_length (key->userids); + unsigned u; + + for (u = 0; u < ut; u++) + { + struct userid_s *userid = slist_nth_data (key->userids, u); + + fprintf(stdout, N_("User ID: %s\n"), userid->userid); + } + + for (s = 0; s < st; s++) + { + struct keyid_s *sub = slist_nth_data (key->subkeys, s); + struct tm *tmp; + char *caps = NULL; + char expires[11] = { 0 }; + char created[11] = { 0 }; + + if (sub->expires) + { + tmp = localtime (&sub->expires); + strftime (expires, sizeof (expires), "%F", tmp); + } + + tmp = localtime (&sub->created); + strftime (created, sizeof (created), "%F", tmp); + + if (sub->can_encrypt) + caps = "E"; + else if (sub->can_sign) + caps = "S"; + else if (sub->can_auth) + caps = "A"; + else if (sub->can_certify) + caps = "C"; + + fprintf(stdout, N_( + " Subkey %u: %s [%s%s%s] [Created: %s] %s%s%s\n" + " Fingerprint: %s\n" + " Keygrip: %s\n" + "%s%s%s"), + s+1, + sub->keyid, + caps, + sub->secret || sub->card ? "P" : "", + sub->revoked ? "R" : "", + created, + sub->expired ? N_("[Expired: ") : expires[0] ? N_("[Expires: ") : "", + sub->expired || expires[0] ? expires : "", + sub->expired || expires[0] ? "]" : "", + sub->fpr, + sub->grip, + sub->card ? N_(" Card: ") : "", + sub->card ? sub->card : "", + sub->card ? "\n" : ""); + if (s+1 < st) + fprintf (stdout, "\n"); + } + + if (i+1 < t) + fprintf (stdout, "\n"); + } +} + +static gpg_error_t +listkeys_command (const char *pattern) +{ + gpg_error_t rc; + char *result; + char **lines = NULL, **p; + struct slist_s *keys = NULL; + + rc = pwmd_command (pwm, &result, NULL, NULL, NULL, "LISTKEYS %s", pattern); + if (rc) + return rc; + + lines = str_split (result, "\n", 0); + pwmd_free (result); + if (!lines) + return GPG_ERR_ENOMEM; + + for (p = lines; *p; p++) + { + struct keyid_s *key; + char **fields = str_split (*p, ":", 0); + int i, f; + unsigned s; + unsigned n_subkeys = 0; + struct slist_s *tlist; + + if (!fields) + { + rc = GPG_ERR_ENOMEM; + break; + } + + key = pwmd_calloc (1, sizeof (struct keyid_s)); + if (!key) + { + strv_free (fields); + rc = GPG_ERR_ENOMEM; + break; + } + + for (f = i = 0; i < 17; i++, f++) + { + int b = 0; + + if (i < 10) + b = atoi (fields[f]) != 0; + + switch (i) + { + case 0: key->revoked = b; break; + case 1: key->expired = b; break; + case 4: key->can_encrypt = b; break; + case 5: key->can_sign = b; break; + case 7: key->secret = b; break; + case 16: n_subkeys = strtoul (fields[f], NULL, 10); break; + default: + break; + } + } + + for (s = 0; s < n_subkeys; s++) + { + struct keyid_s *sub; + int b = 0; + + sub = pwmd_calloc (1, sizeof (struct keyid_s)); + if (!sub) + { + strv_free (fields); + rc = GPG_ERR_ENOMEM; + break; + } + + for (i = 0; i < 20; i++, f++) + { + if (i < 11) + b = atoi (fields[f]) != 0; + + switch (i) + { + case 0: sub->revoked = b; break; + case 1: sub->expired = b; break; + case 4: sub->can_encrypt = b; break; + case 5: sub->can_sign = b; break; + case 6: sub->can_certify = b; break; + case 7: sub->secret = b; break; + case 8: sub->can_auth = b; break; + case 13: sub->keyid = pwmd_strdup (fields[f]); break; + case 14: sub->fpr = pwmd_strdup (fields[f]); break; + case 15: sub->grip = pwmd_strdup (fields[f]); break; + case 16: sub->created = strtoul (fields[f], NULL, 10); break; + case 17: sub->expires = strtoul (fields[f], NULL, 10); break; + case 18: sub->card = fields[f] && strlen(fields[f]) > 1 + ? pwmd_strdup (fields[f]) : NULL; break; + default: + break; + } + } + + tlist = slist_append (key->subkeys, sub); + if (!tlist) + { + rc = GPG_ERR_ENOMEM; + break; + } + + key->subkeys = tlist; + } + + if (rc) + { + strv_free (fields); + break; + } + + // Re-create a line containing the userIds. + for (; f < strv_length (fields); f++) + { + struct userid_s *userid; + + userid = pwmd_calloc (1, sizeof (struct userid_s)); + if (!userid) + { + rc = GPG_ERR_ENOMEM; + break; + } + + // Revoked. + f++; + // Invalid. + f++; + // Validity. + f++; + + userid->userid = pwmd_strdup (openpgp_unescape (fields[f++])); + userid->name = pwmd_strdup (openpgp_unescape (fields[f++])); + userid->email = pwmd_strdup (openpgp_unescape (fields[f++])); + userid->comment = pwmd_strdup (openpgp_unescape (fields[f])); + + tlist = slist_append (key->userids, userid); + if (!tlist) + { + rc = GPG_ERR_ENOMEM; + break; + } + + key->userids = tlist; + } + + strv_free (fields); + if (rc) + break; + + tlist = slist_append (keys, key); + if (!tlist) + { + rc = GPG_ERR_ENOMEM; + break; + } + + keys = tlist; + } + + strv_free (lines); + + if (!rc) + display_listkeys (keys); + + free_listkeys (keys); + return rc; +} + static gpg_error_t parse_dotcommand (const char *line, char **result, size_t * len, struct inquire_s *inq) @@ -1238,6 +1583,8 @@ parse_dotcommand (const char *line, char **result, rc = do_save_passwd_command (p + 5, 1); else if (!strncmp (p, ".passwd", 7)) rc = do_save_passwd_command (p + 7, 0); + else if (!strncmp (p, ".listkeys", 9)) + rc = listkeys_command (p+9); else { rc = pwmd_command (pwm, result, len, inquire_cb, inq, "%s", line); diff --git a/src/util-slist.c b/src/util-slist.c new file mode 100644 index 00000000..5e187410 --- /dev/null +++ b/src/util-slist.c @@ -0,0 +1,114 @@ +/* + Copyright (C) 2012, 2013, 2014, 2015, 2016 + Ben Kibbey + + This file is part of pwmd. + + Pwmd is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Pwmd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Pwmd. If not, see . +*/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "util-slist.h" + +unsigned +slist_length (struct slist_s *list) +{ + struct slist_s *cur; + unsigned n = 0; + + for (cur = list; cur; cur = cur->next) + n++; + + return n; +} + +void * +slist_nth_data (struct slist_s *list, unsigned n) +{ + struct slist_s *cur; + unsigned i; + + for (i = 0, cur = list; cur; cur = cur->next, i++) + { + if (i == n) + return cur->data; + } + + return NULL; +} + +struct slist_s * +slist_append (struct slist_s *list, void *data) +{ + struct slist_s *cur; + + if (!list) + { + cur = pwmd_calloc (1, sizeof (struct slist_s)); + cur->data = data; + return cur; + } + + for (cur = list; cur; cur = cur->next) + { + if (!cur->next) + break; + } + + cur->next = pwmd_calloc (1, sizeof (struct slist_s)); + cur->next->data = data; + return list; +} + +static struct slist_s * +free_once (struct slist_s *cur) +{ + struct slist_s *next = cur ? cur->next : NULL; + + pwmd_free (cur); + return next; +} + +void +slist_free (struct slist_s *list) +{ + while (list) + list = free_once (list); +} + +struct slist_s * +slist_remove (struct slist_s *list, void *data) +{ + struct slist_s *prev, *cur; + + for (cur = prev = list; cur; prev = cur, cur = cur->next) + { + if (cur->data == data) + { + struct slist_s *next = free_once (cur); + + if (cur != list) + prev->next = next; + else + list = next; + break; + } + } + + return list; +} diff --git a/src/util-slist.h b/src/util-slist.h new file mode 100644 index 00000000..82a0da50 --- /dev/null +++ b/src/util-slist.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2012, 2013, 2014, 2015, 2016 + Ben Kibbey + + This file is part of pwmd. + + Pwmd is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Pwmd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Pwmd. If not, see . +*/ +#ifndef UTIL_SLIST_H +#define UTIL_SLIST_H + +struct slist_s +{ + void *data; + struct slist_s *next; +}; + +unsigned slist_length (struct slist_s *list); +void *slist_nth_data (struct slist_s *list, unsigned n); +struct slist_s *slist_append (struct slist_s *list, void *data); +void slist_free (struct slist_s *list); +struct slist_s *slist_remove (struct slist_s *list, void *data); + +#endif diff --git a/src/util-string.c b/src/util-string.c new file mode 100644 index 00000000..29c19114 --- /dev/null +++ b/src/util-string.c @@ -0,0 +1,592 @@ +/* + Copyright (C) 2012, 2013, 2014, 2015, 2016 + Ben Kibbey + + This file is part of pwmd. + + Pwmd is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Pwmd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Pwmd. If not, see . +*/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "util-string.h" + +void +string_free (struct string_s *s, int with_data) +{ + if (!s) + return; + + if (with_data) + pwmd_free (s->str); + + pwmd_free (s); +} + +struct string_s * +string_erase (struct string_s *s, ssize_t pos, ssize_t len) +{ + if (pos > s->len) + return s; + + if (len == -1) + { + s->str[pos] = 0; + s->len = s->len - pos; + return s; + } + + int n = s->len - pos; + n -= len; + memmove (&s->str[pos], &s->str[pos + len], n); + s->len -= len; + s->str[s->len] = 0; + return s; +} + +/* Be careful about allocations since other string_ functions may + * realloc the 'str' pointer. */ +struct string_s * +string_new_content (char *str) +{ + struct string_s *s = pwmd_calloc (1, sizeof (struct string_s)); + + s->str = str; + s->len = strlen (s->str); + s->allocated = s->len + 1; + return s; +} + +struct string_s * +string_new (const char *str) +{ + struct string_s *s = pwmd_calloc (1, sizeof (struct string_s)); + + if (str) + { + s->str = str_dup (str); + if (!s->str) + { + pwmd_free (s); + return NULL; + } + + s->len = strlen (s->str); + s->allocated = s->len + 1; + } + + return s; +} + +struct string_s * +string_append (struct string_s *s, const char *str) +{ + size_t len = strlen (str); + + if (s->allocated < len + s->len + 1) + { + char *p = pwmd_realloc (s->str, (len + s->len + 1) * sizeof (char)); + + if (!p) + return NULL; + + s->str = p; + s->allocated = s->len + len + 1; + } + + memcpy (&s->str[s->len], str, len); + s->len += len; + s->str[s->len] = 0; + return s; +} + +struct string_s * +string_truncate (struct string_s *s, size_t n) +{ + if (!s->str) + return s; + + if (s->len < n) + return s; + + s->str[n] = 0; + s->len = n; + return s; +} + +struct string_s * +string_prepend (struct string_s *s, const char *str) +{ + size_t len = strlen (str); + + if (s->allocated < s->len + len + 1) + { + char *p = pwmd_realloc (s->str, (len + s->len + 1) * sizeof (char)); + + if (!p) + return NULL; + + s->str = p; + s->allocated = s->len + len + 1; + } + + memmove (&s->str[len], s->str, s->len); + memcpy (s->str, str, len); + s->len += len; + s->str[s->len] = 0; + return s; +} + +struct string_s * +string_append_printf (struct string_s *s, const char *fmt, ...) +{ + va_list ap; + char *buf = NULL; + size_t len; + + va_start (ap, fmt); + len = str_vasprintf (&buf, fmt, ap); + va_end (ap); + + if (len == -1) + return NULL; + + s = string_append (s, buf); + pwmd_free (buf); + return s; +} + +struct string_s * +string_insert_c (struct string_s *s, ssize_t pos, char c) +{ + size_t len = s->len + 2; + + if (pos >= s->allocated) + len = pos + 2; + + if (s->allocated < len) + { + char *p = pwmd_realloc (s->str, len * sizeof (char)); + + if (!p) + return NULL; + + s->str = p; + s->allocated = len; + } + + memmove (&s->str[pos + 1], &s->str[pos], s->len - pos); + s->str[pos] = c; + s->len = pos + 1 == s->allocated ? pos + 1 : s->len + 1; + s->str[s->len] = 0; + return s; +} + +int +strv_length (char **a) +{ + int n = 0; + + while (a && *a++) + n++; + + return n; +} + +/* 'str' must already be allocated. */ +char ** +strv_cat (char **a, char *str) +{ + int len = a ? strv_length (a) : 0; + char **dst; + + dst = pwmd_realloc (a, (len + 2) * sizeof (char *)); + if (!dst) + return NULL; + + dst[len++] = str; + dst[len] = NULL; + return dst; +} + +int +strv_printf (char ***array, const char *fmt, ...) +{ + char **a; + va_list ap; + char *buf; + int ret; + + va_start (ap, fmt); + ret = str_vasprintf (&buf, fmt, ap); + va_end (ap); + + if (ret == -1) + return 0; + + a = strv_cat (*array, buf); + if (!a) + { + pwmd_free (buf); + return 0; + } + + *array = a; + return 1; +} + +void +strv_free (char **a) +{ + char **p; + + for (p = a; p && *p; p++) + pwmd_free (*p); + + pwmd_free (a); +} + +char ** +strv_dup (char **src) +{ + char **dst = NULL; + char **p; + + for (p = src; p && *p; p++) + { + char **tmp; + char *xp = str_dup (*p); + + if (!xp) + { + strv_free (dst); + return NULL; + } + + tmp = strv_cat (dst, xp); + if (!tmp) + { + pwmd_free (xp); + strv_free (dst); + return NULL; + } + + dst = tmp; + } + + return dst; +} + +char ** +strv_catv (char **dst, char **src) +{ + char **p; + char **d = NULL; + + if (dst && *dst) + { + d = strv_dup (dst); + if (!d) + return NULL; + } + + for (p = src; p && *p; p++) + { + char **tmp; + char *xp = str_dup (*p); + + if (!xp) + { + strv_free (d); + return NULL; + } + + tmp = strv_cat (d, xp); + if (!tmp) + { + pwmd_free (xp); + strv_free (d); + return NULL; + } + + d = tmp; + } + + return d; +} + +static char * +trim (char *str) +{ + size_t len = strlen (str); + char *p = str; + + while (isspace (str[--len])) + str[len] = 0; + + while (isspace (*p)) + p++; + + return p; +} + +static char ** +str_split_common (const char *src, const char *delim, int count, int ws) +{ + char **dst = NULL; + int index = 0; + const char *dp = delim; + char *str = str_dup (src); + char *p = str; + size_t dlen = strlen (delim); + size_t pos = 0, lastpos = 0; + + if (!str || !*str) + { + pwmd_free (str); + return NULL; + } + + for (; *p; p++) + { + if (*p == *dp++) + { + if (!*dp) + { + char **tmp; + char *xp, *s; + size_t len = pos - lastpos - dlen + 1; + + index++; + xp = pwmd_malloc (len + 1); + if (!xp) + { + strv_free (dst); + pwmd_free (str); + return NULL; + } + + memcpy (xp, &str[lastpos], len); + xp[len] = 0; + s = xp; + + if (ws) + s = trim (xp); + + tmp = strv_cat (dst, s); + if (!tmp) + { + pwmd_free (xp); + strv_free (dst); + pwmd_free (str); + return NULL; + } + + dst = tmp; + lastpos = pos + 1; + + if (count > 0 && index+1 == count && *(p + 1)) + { + if (!strv_printf (&dst, "%s", p + 1)) + { + strv_free (dst); + pwmd_free (str); + return NULL; + } + + pwmd_free (str); + return dst; + } + + dp = delim; + } + + pos++; + continue; + } + + pos++; + dp = delim; + } + + p = str + lastpos; + if (ws) + p = trim (p); + + if (!strv_printf (&dst, "%s", p)) + { + strv_free (dst); + pwmd_free (str); + return NULL; + } + + pwmd_free (str); + return dst; +} + +/* Like str_split() but trims whitespace between 'delim'. */ +char ** +str_split_ws (const char *str, const char *delim, int count) +{ + return str_split_common (str, delim, count, 1); +} + +char ** +str_split (const char *str, const char *delim, int count) +{ + return str_split_common (str, delim, count, 0); +} + +char * +strv_join (char *delim, char **a) +{ + char **p; + char *dst = NULL; + struct string_s *s = string_new (""); + + if (!s) + return NULL; + + for (p = a; *p; p++) + { + struct string_s *sp; + + sp = string_append_printf (s, "%s%s", *p, delim + && *(p + 1) ? delim : ""); + if (!sp) + { + string_free (s, 1); + return NULL; + } + + s = sp; + } + + dst = s->str; + string_free (s, 0); + return dst; +} + +char * +str_down (char *str) +{ + char *p; + + for (p = str; *p; p++) + { + if (isascii (*p)) + *p = tolower (*p); + } + + return str; +} + +char * +str_chomp (char *str) +{ + int len = strlen (str); + char *p = str + len - 1; + + while (len && isspace (*p)) + { + *p-- = 0; + len--; + } + + p = str; + while (isspace (*p)) + { + p++; + len--; + } + + memmove (str, p, len); + str[len] = 0; + return str; +} + +char * +str_dup (const char *str) +{ + char *t; + size_t len; + register size_t c; + + len = strlen (str); + t = pwmd_malloc ((len + 1) * sizeof (char)); + if (!t) + return NULL; + + for (c = 0; c < len; c++) + t[c] = str[c]; + + t[c] = 0; + return t; +} + +char * +str_asprintf (const char *fmt, ...) +{ + va_list ap; + char *result; + int len; + + va_start (ap, fmt); + len = str_vasprintf (&result, fmt, ap); + va_end (ap); + return len == -1 ? NULL : result; +} + +#define BUFSIZE 128 +int +str_vasprintf (char **result, const char *fmt, va_list ap) +{ + size_t alloc = BUFSIZE; + char *buf = NULL; + int len; + + for (;;) + { + char *p = pwmd_realloc (buf, alloc * sizeof (char)); + va_list cp; + + if (!p) + { + pwmd_free (buf); + return -1; + } + + buf = p; + va_copy (cp, ap); + len = vsnprintf (buf, alloc, fmt, cp); + va_end (cp); + + if (len >= alloc) + { + alloc = len + 1; + continue; + } + + break; + } + + *result = buf; + return len; +} diff --git a/src/util-string.h b/src/util-string.h new file mode 100644 index 00000000..4ef8e4c5 --- /dev/null +++ b/src/util-string.h @@ -0,0 +1,64 @@ +/* + Copyright (C) 2012, 2013, 2014, 2015, 2016 + Ben Kibbey + + This file is part of pwmd. + + Pwmd is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Pwmd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Pwmd. If not, see . +*/ +#ifndef UTIL_STRING_H +#define UTIL_STRING_H + +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +struct string_s +{ + size_t allocated; + size_t len; + char *str; +}; + +// These mimic GLib's string utility functions. +void string_free (struct string_s *s, int with_data); +struct string_s *string_erase (struct string_s *s, ssize_t pos, ssize_t len); +struct string_s *string_new (const char *str); +struct string_s *string_new_content (char *str); +struct string_s *string_append (struct string_s *s, const char *str); +struct string_s *string_truncate (struct string_s *s, size_t n); +struct string_s *string_prepend (struct string_s *s, const char *str); +struct string_s *string_append_printf (struct string_s *s, const char *fmt, + ...); +struct string_s *string_insert_c (struct string_s *s, ssize_t pos, char c); + +int strv_printf (char ***array, const char *fmt, ...); +void strv_free (char **str); +char **strv_cat (char **a, char *str); +char **strv_catv (char **dst, char **src); +int strv_length (char **a); +char **strv_dup (char **src); +char *strv_join (char *delim, char **a); + +char **str_split (const char *str, const char *delim, int count); +char **str_split_ws (const char *str, const char *delim, int count); +char *str_down (char *str); +char *str_chomp (char *str); +char *str_dup (const char *); +char *str_asprintf (const char *fmt, ...); +int str_vasprintf (char **result, const char *fmt, va_list ap); + +#endif -- 2.11.4.GIT