File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / util / string_quote.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (12 years, 4 months ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel


/*
 * Copyright (c) 2001-2002 Packet Design, LLC.
 * All rights reserved.
 * 
 * Subject to the following obligations and disclaimer of warranty,
 * use and redistribution of this software, in source or object code
 * forms, with or without modifications are expressly permitted by
 * Packet Design; provided, however, that:
 * 
 *    (i)  Any and all reproductions of the source or object code
 *         must include the copyright notice above and the following
 *         disclaimer of warranties; and
 *    (ii) No rights are granted, in any manner or form, to use
 *         Packet Design trademarks, including the mark "PACKET DESIGN"
 *         on advertising, endorsements, or otherwise except as such
 *         appears in the above copyright notice or in the software.
 * 
 * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
 * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
 * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
 * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
 * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
 * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
 * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
 * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
 * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Archie Cobbs <archie@freebsd.org>
 */

#include <sys/types.h>

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <pthread.h>

#include "structs/structs.h"
#include "structs/type/array.h"

#include "util/string_quote.h"
#include "util/typed_mem.h"

struct string_dequote_info {
	const char	*mtype;
	char		**bufp;
};

/*
 * Internal variables
 */
static const	char *escapes[2] = { "tnrvf\"\\", "\t\n\r\v\f\"\\" };
static const	char hexdigit[16] = "0123456789abcdef";

/*
 * Internal functions
 */
static void	string_dequote_cleanup(void *arg);

/*
 * Parse a doubly-quoted string token.
 */
char *
string_dequote(FILE *fp, const char *mtype)
{
	struct string_dequote_info info;
	char *buf = NULL;
	int alloc = 0;
	int len = 0;
	void *mem;
	int ch;

	/* Cleanup properly if thread is canceled */
	info.bufp = &buf;
	info.mtype = mtype;
	pthread_cleanup_push(string_dequote_cleanup, &info);

	/* Parse string */
	while ((ch = getc(fp)) != EOF) {

		/* Increase buffer length if necessary */
		if (len + 8 >= alloc) {
			alloc += 64;
			if ((mem = REALLOC(mtype, buf, alloc)) == NULL)
				goto fail;
			buf = mem;
		}

		/* Check special characters */
		switch (ch) {
		case '"':
			buf[len] = '\0';
			goto done;
		case '\\':
			switch ((ch = getc(fp))) {
			case '0': case '1': case '2': case '3':
			case '4': case '5': case '6': case '7':
			    {
				char chsave[3];
				int x, k;

				for (x = k = 0; k < 3; k++) {
					if (k > 0 && (ch = getc(fp)) == EOF) {
						k--;	/* char not saved */
						break;
					}
					chsave[k] = ch;
					if (ch < '0' || ch > '7')
						break;
					x = (x << 3) + (ch - '0');
				}
				if (k == 3)		/* got a whole byte */
					buf[len++] = (char)x;
				else {			/* copy chars as-is */
					buf[len++] = '\\';
					for (x = 0; x <= k; x++)
						buf[len++] = chsave[x];
				}
				break;
			    }
			case 'x':
			    {
				char chsave[2];
				int x, k;

				for (x = k = 0; k < 2; k++) {
					if ((ch = getc(fp)) == EOF) {
						k--;	/* char not saved */
						break;
					}
					chsave[k] = ch;
					if (!isxdigit(ch))
						break;
					x = (x << 4) + (isdigit(ch) ?
					      (ch - '0') :
					      (tolower(ch) - 'a' + 10));
				}
				if (k == 2)		/* got a whole byte */
					buf[len++] = (char)x;
				else {			/* copy chars as-is */
					buf[len++] = '\\';
					buf[len++] = 'x';
					for (x = 0; x <= k; x++)
						buf[len++] = chsave[x];
				}
				break;
			    }

			case EOF:
				goto got_eof;

			default:
			    {
				char *x;

				if ((x = strchr(escapes[0], ch)) != NULL)
					buf[len++] = escapes[1][x - escapes[0]];
				else
					buf[len++] = ch;
			    }
			}
			break;
		default:
			buf[len++] = (char)ch;
			break;
		}
	}

got_eof:
	/* EOF was read: check for error or actual end of file */
	if (!ferror(fp))
		errno = EINVAL;

fail:
	/* Error */
	FREE(mtype, buf);
	buf = NULL;

done:;
	/* Done */
	pthread_cleanup_pop(0);
	return (buf);
}

/*
 * Cleanup for string_dequote()
 */
static void
string_dequote_cleanup(void *arg)
{
	struct string_dequote_info *const info = arg;

	FREE(info->mtype, *info->bufp);
}

/*
 * Enquote a string.
 */
char *
string_enquote(const char *s, const char *mtype)
{
	char *buf = NULL;
	int pass2 = 0;
	int len;
	char *t;
	int i;

pass2:
	/* Encode characters */
	len = 0;
	if (pass2)
		buf[len] = '"';
	len++;
	for (i = 0; s[i] != '\0'; i++) {
		if ((t = strchr(escapes[1], s[i])) != NULL) {
			if (pass2) {
				buf[len] = '\\';
				buf[len + 1] = escapes[0][t - escapes[1]];
			}
			len += 2;
		} else if (isprint(s[i])) {
			if (pass2)
				buf[len] = s[i];
			len++;
		} else {
			if (pass2) {
				buf[len] = '\\';
				buf[len + 1] = 'x';
				buf[len + 2] = hexdigit[((s[i]) >> 4) & 0x0f];
				buf[len + 3] = hexdigit[(s[i]) & 0x0f];
			}
			len += 4;
		}
	}
	if (pass2)
		buf[len] = '"';
	len++;

	/* Finish up */
	if (pass2) {
		buf[len] = '\0';
		return (buf);
	}

	/* Initialize buffer */
	if ((buf = MALLOC(mtype, len + 1)) == NULL)
		return (NULL);
	pass2 = 1;
	goto pass2;
}

#ifdef STRING_QUOTE_TEST

#include <unistd.h>
#include <err.h>

int
main(int ac, char **av)
{
	int decode = -1;
	FILE *fp;
	char *s;
	int ch;

	while ((ch = getopt(ac, av, "de")) != -1) {
		switch (ch) {
		case 'd':
			decode = 1;
			break;
		case 'e':
			decode = 0;
			break;
		default:
		usage:
			errx(1, "usage: string_quote -d dquotefile\n"
				"\tstring_quote -e [rawtext]");
		}
	}
	ac -= optind;
	av += optind;
	if (decode == -1)
		goto usage;
	if (decode && ac != 1)
		goto usage;
	if (!decode && ac != 0 && ac != 1)
		goto usage;

	/* Encode or decode */
	if (ac == 0)
		fp = stdin;
	else if ((fp = fopen(av[0], "r")) == NULL)
		err(1, "%s", av[0]);
	if (decode) {
		if ((ch = getc(fp)) != '"')
			errx(1, "input does not start with a double quote");
		if ((s = string_dequote(fp, TYPED_MEM_TEMP)) == NULL)
			err(1, "error dequoting %s", av[0]);
		fputs(s, stdout);
		FREE(TYPED_MEM_TEMP, s);
	} else {
		char buf[1024];
		int len;

		len = fread(buf, 1, sizeof(buf) - 1, fp);
		if (ferror(fp))
			err(1, "reading rawtext input");
		if (len == sizeof(buf) - 1)
			warnx("warning: only %u characters dealt with", len);
		buf[len] = '\0';
		if ((s = string_enquote(buf, TYPED_MEM_TEMP)) == NULL)
			err(1, "error dequoting %s", av[0]);
		fputs(s, stdout);
		putchar('\n');
		FREE(TYPED_MEM_TEMP, s);
	}
	return (0);
}

#endif /* STRING_QUOTE_TEST */


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>