custr.c
#include "ustr.h"
#include <getopt.h>
#include <errno.h>
#include <ctype.h>
static int combine = USTR_FALSE;
static int interp = USTR_FALSE;
static int fnames = USTR_FALSE;
static void die(const char *prog_name, const char *msg)
{
fprintf(stderr, "%s: %s\n", prog_name, msg);
exit (EXIT_FAILURE);
}
static void usage(const char *prog_name, int xcode)
{
fprintf((xcode == EXIT_SUCCESS) ? stdout : stderr, "\
Format: %s [-cfhiV] <string> [string]...\n\
-c = Combine arguments into single line.\n\
-f = Interpret arguments as filenames.\n\
-i = Interpret arguments from C-escaping.\n\
-h = Print help.\n\
-V = Print version.\n\
", prog_name ? prog_name : "ustr");
exit (xcode);
}
static int cescape_decode(Ustr **ps1)
{
size_t fpos = 0;
size_t len = ustr_len(*ps1);
char *src = NULL;
char *dst = NULL;
if (!(fpos = ustr_srch_chr_fwd(*ps1, 0, '\\')) || (fpos == len))
return (USTR_TRUE);
if (!(dst = src = ustr_sc_wstr(ps1)))
return (USTR_FALSE);
--fpos;
src += fpos;
dst += fpos;
len -= fpos;
while (len--)
{
if ((*src != '\\') || !len)
{
normal:
*dst++ = *src++;
}
else
{
--len; ++src;
switch (*src)
{
default:
goto normal;
case '\n': ++src; break;
case '"': ++src; break;
case '\'': ++src; break;
case 'b': ++src; *dst++ = '\b'; break;
case 'f': ++src; *dst++ = '\f'; break;
case 'n': ++src; *dst++ = '\n'; break;
case 'r': ++src; *dst++ = '\r'; break;
case 't': ++src; *dst++ = '\t'; break;
case 'v': ++src; *dst++ = '\v'; break;
case '\\': ++src; *dst++ = '\\'; break;
case 'o':
case 'O':
if ((len < 1) || !isdigit((unsigned char)src[1]))
{
*dst++ = '\\';
goto normal;
}
--len; ++src;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
{
unsigned char val = 0;
if (USTR_FALSE)
{ }
else if ((len >= 2) && isdigit((unsigned char)src[1]) &&
isdigit((unsigned char)src[2]))
{
val = (*src++ - '0');
val *= 8;
val += (*src++ - '0');
val *= 8;
val += (*src++ - '0');
len -= 2;
}
else if ((len >= 1) && isdigit((unsigned char)src[1]))
{
val = (*src++ - '0');
val *= 8;
val += (*src++ - '0');
len -= 1;
}
else
val = (*src++ - '0');
*dst++ = val;
break;
}
case 'x':
case 'X':
{
unsigned char val = 0;
Ustr *bconv = USTR1(\x16, "0123456789ABCDEFabcdef");
Ustr *uconv = USTR1(\x10, "0123456789ABCDEF");
Ustr *lconv = USTR1(\x6, "abcdef");
size_t pos = 0;
if ((len < 2) ||
!ustr_srch_chr_fwd(bconv, 0, src[1]) ||
!ustr_srch_chr_fwd(bconv, 0, src[2]))
{
*dst++ = '\\';
goto normal;
}
if (!(pos = ustr_srch_chr_fwd(uconv, 0, src[1])))
pos = ustr_srch_chr_fwd(lconv, 0, src[1]) + 10;
val = pos - 1;
val *= 16;
if (!(pos = ustr_srch_chr_fwd(uconv, 0, src[2])))
pos = ustr_srch_chr_fwd(lconv, 0, src[2]) + 10;
val += pos - 1;
*dst++ = val;
len -= 2;
src += 3;
}
break;
}
}
}
ustr_del(ps1, src - dst);
return (USTR_TRUE);
}
static int cescape_encode(Ustr **ret, const char *src, size_t len)
{
const char *last = NULL;
if (!ustr_sc_ensure_owner(ret))
return (USTR_FALSE);
ustr_add_rep_chr(ret, '"', 1);
last = src;
while (len--)
{
if ((*src == 0x20) || (*src == 0x21) ||
((*src >= 0x23) && (*src <= 0x7E)))
++src;
else
{
if (last != src)
ustr_add_buf(ret, last, src - last);
switch (*src)
{
case '\b': ustr_add_cstr(ret, "\\b"); break;
case '\f': ustr_add_cstr(ret, "\\f"); break;
case '\n': ustr_add_cstr(ret, "\\n"); break;
case '\r': ustr_add_cstr(ret, "\\r"); break;
case '\t': ustr_add_cstr(ret, "\\t"); break;
case '\v': ustr_add_cstr(ret, "\\v"); break;
default:
if (!len ||
!(((src[1] >= '0') && (src[1] <= '9')) ||
((src[1] >= 'A') && (src[1] <= 'F')) ||
((src[1] >= 'a') && (src[1] <= 'f'))))
{
if (!*src)
ustr_add_cstr(ret, "\\0");
else
ustr_add_fmt(ret, "\\x%02hhX", *src);
}
else if (!*src)
ustr_add_cstr(ret, "\" \"\\0\" \"");
else
ustr_add_fmt(ret, "\\x%02hhX\" \"", *src);
}
++src;
last = src;
}
}
if (last != src)
ustr_add_buf(ret, last, src - last);
ustr_add_rep_chr(ret, '"', 1);
return (!ustr_enomem(*ret));
}
static Ustr *gen_custr_beg(size_t len)
{
if (len > 65535)
return (ustr_dup_fmt("USTR1(\\x%zX, \\x%02zX, \\x%02zX, \\x%02zX, ",
((len >> 24) & 0xFF), ((len >> 16) & 0xFF),
((len >> 8) & 0xFF), ((len >> 0) & 0xFF)));
if (len > 255)
return (ustr_dup_fmt("USTR2(\\x%zX, \\x%02zX, ",
((len >> 8) & 0xFF), ((len >> 0) & 0xFF)));
if (len > 7)
return (ustr_dup_fmt("USTR1(\\x%zX, ", len));
return (ustr_dup_fmt("USTR1(\\%zo, ", len));
}
static Ustr *gen_custr_buf(const char *buf, size_t len)
{
Ustr *tmp = USTR_NULL;
Ustr *ret = USTR_NULL;
if (interp)
{
if (!(tmp = ustr_dup_buf(buf, len)))
goto fail_mem;
if (!cescape_decode(&tmp))
goto fail_mem;
if (!(ret = gen_custr_beg(ustr_len(tmp))))
goto fail_mem;
if (!cescape_encode(&ret, ustr_cstr(tmp), ustr_len(tmp)))
goto fail_mem;
}
else
{
if (!(ret = gen_custr_beg(len)))
goto fail_mem;
if (!cescape_encode(&ret, buf, len))
goto fail_mem;
}
if (!ustr_add_cstr(&ret, ");"))
goto fail_mem;
ustr_free(tmp);
return (ret);
fail_mem:
ustr_free(tmp);
ustr_free(ret);
return (USTR_NULL);
}
static void custr(Ustr *data, const char *prog_name)
{
Ustr *arg = USTR_NULL;
if (!(arg = gen_custr_buf(ustr_cstr(data), ustr_len(data))))
die(prog_name, strerror(errno));
if (!ustr_io_putfileline(&arg, stdout))
die(prog_name, strerror(errno));
ustr_free(arg);
}
int main(int argc, char *argv[])
{
struct option long_options[] =
{
{"combine", no_argument, NULL, 'c'},
{"interpret", no_argument, NULL, 'i'},
{"filenames", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
int scan = 0;
int optchar = -1;
const char *prog_name = NULL;
if (!argc)
usage(NULL, EXIT_FAILURE);
if ((prog_name = strrchr(argv[0], '/')))
++prog_name;
else
prog_name = argv[0];
while ((optchar = getopt_long(argc, argv, "cfhiV", long_options,NULL)) != -1)
switch (optchar)
{
case '?': usage(prog_name, EXIT_FAILURE);
case 'h': usage(prog_name, EXIT_SUCCESS);
case 'V':
printf("%s version %s\n", prog_name, "1.0.0");
exit (EXIT_SUCCESS);
case 'c': combine = !combine; break;
case 'f': fnames = !fnames; break;
case 'i': interp = !interp; break;
}
argc -= optind;
argv += optind;
if (!argc)
usage(prog_name, EXIT_FAILURE);
if (fnames)
{
char buf_line[USTR_SIZE_FIXED(160)];
Ustr *line = USTR_SC_INIT_AUTO(buf_line, USTR_FALSE, 0);
while (scan < argc)
{
FILE *fp = NULL;
if (ustr_cmp_cstr_eq(USTR1(\1, "-"), argv[scan]))
fp = stdin;
else if (!(fp = fopen(argv[scan], "rb")))
die(prog_name, strerror(errno));
++scan;
if (combine)
{
if (!ustr_io_getfile(&line, fp))
die(prog_name, strerror(errno));
custr(line, prog_name);
}
else
{
while (ustr_io_getline(&line, fp))
{
ustr_del(&line, ustr_spn_chr_rev(line, 0, '\n'));
custr(line, prog_name);
ustr_sc_del(&line);
if (line != USTR(buf_line))
ustr_sc_free2(&line, USTR_SC_INIT_AUTO(buf_line, USTR_FALSE, 0));
}
if (errno)
die(prog_name, strerror(errno));
}
if (line != USTR(buf_line))
ustr_sc_free2(&line, USTR_SC_INIT_AUTO(buf_line, USTR_FALSE, 0));
fclose(fp);
}
}
else if (!combine)
while (scan < argc)
{
Ustr *arg = gen_custr_buf(argv[scan], strlen(argv[scan]));
if (!arg || !ustr_io_putfileline(&arg, stdout))
die(prog_name, strerror(errno));
ustr_free(arg);
++scan;
}
else
{
char buf_args[USTR_SIZE_FIXED(160)];
Ustr *args = USTR_SC_INIT_AUTO(buf_args, USTR_FALSE, 0);
int done = USTR_FALSE;
while (scan < argc)
{
if (done)
ustr_add_cstr(&args, " ");
done = USTR_TRUE;
ustr_add_cstr(&args, argv[scan++]);
}
if (ustr_enomem(args))
die(prog_name, strerror(ENOMEM));
custr(args, prog_name);
ustr_free(args);
}
USTR_CNTL_MALLOC_CHECK_END();
return (EXIT_SUCCESS);
}