Home | History | Annotate | Line # | Download | only in global
      1 /*	$NetBSD: xtext.c,v 1.4 2025/02/25 19:15:46 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	xtext 3
      6 /* SUMMARY
      7 /*	quote/unquote text, xtext style.
      8 /* SYNOPSIS
      9 /*	#include <xtext.h>
     10 /*
     11 /*	VSTRING	*xtext_quote(quoted, unquoted, special)
     12 /*	VSTRING	*quoted;
     13 /*	const char *unquoted;
     14 /*	const char *special;
     15 /*
     16 /*	VSTRING	*xtext_quote_append(unquoted, quoted, special)
     17 /*	VSTRING	*unquoted;
     18 /*	const char *quoted;
     19 /*	const char *special;
     20 /*
     21 /*	VSTRING	*xtext_unquote(unquoted, quoted)
     22 /*	VSTRING	*unquoted;
     23 /*	const char *quoted;
     24 /*
     25 /*	VSTRING	*xtext_unquote_append(unquoted, quoted)
     26 /*	VSTRING	*unquoted;
     27 /*	const char *quoted;
     28 /* DESCRIPTION
     29 /*	xtext_quote() takes a null-terminated string and replaces characters
     30 /*	+, <33(10) and >126(10), as well as characters specified with "special"
     31 /*	by +XX, XX being the two-digit uppercase hexadecimal equivalent.
     32 /*
     33 /*	xtext_quote_append() is like xtext_quote(), but appends the conversion
     34 /*	result to the result buffer.
     35 /*
     36 /*	xtext_unquote() performs the opposite transformation. This function
     37 /*	understands lowercase, uppercase, and mixed case +XX sequences. The
     38 /*	result value is the unquoted argument in case of success, a null pointer
     39 /*	otherwise.
     40 /*
     41 /*	xtext_unquote_append() is like xtext_unquote(), but appends
     42 /*	the conversion result to the result buffer.
     43 /* BUGS
     44 /*	This module cannot process null characters in data.
     45 /* LICENSE
     46 /* .ad
     47 /* .fi
     48 /*	The Secure Mailer license must be distributed with this software.
     49 /* AUTHOR(S)
     50 /*	Wietse Venema
     51 /*	IBM T.J. Watson Research
     52 /*	P.O. Box 704
     53 /*	Yorktown Heights, NY 10598, USA
     54 /*
     55 /*	Wietse Venema
     56 /*	Google, Inc.
     57 /*	111 8th Avenue
     58 /*	New York, NY 10011, USA
     59 /*--*/
     60 
     61 /* System library. */
     62 
     63 #include <sys_defs.h>
     64 #include <string.h>
     65 #include <ctype.h>
     66 
     67 /* Utility library. */
     68 
     69 #include "msg.h"
     70 #include "vstring.h"
     71 #include "xtext.h"
     72 
     73 /* Application-specific. */
     74 
     75 #define STR(x)	vstring_str(x)
     76 #define LEN(x)	VSTRING_LEN(x)
     77 
     78 /* xtext_quote_append - append unquoted data to quoted data */
     79 
     80 VSTRING *xtext_quote_append(VSTRING *quoted, const char *unquoted,
     81 			            const char *special)
     82 {
     83     const char *cp;
     84     int     ch;
     85 
     86     for (cp = unquoted; (ch = *(unsigned const char *) cp) != 0; cp++) {
     87 	if (ch != '+' && ch > 32 && ch < 127
     88 	    && (*special == 0 || strchr(special, ch) == 0)) {
     89 	    VSTRING_ADDCH(quoted, ch);
     90 	} else {
     91 	    vstring_sprintf_append(quoted, "+%02X", ch);
     92 	}
     93     }
     94     VSTRING_TERMINATE(quoted);
     95     return (quoted);
     96 }
     97 
     98 /* xtext_quote - unquoted data to quoted */
     99 
    100 VSTRING *xtext_quote(VSTRING *quoted, const char *unquoted, const char *special)
    101 {
    102     VSTRING_RESET(quoted);
    103     xtext_quote_append(quoted, unquoted, special);
    104     return (quoted);
    105 }
    106 
    107 /* xtext_unquote_append - quoted data to unquoted */
    108 
    109 VSTRING *xtext_unquote_append(VSTRING *unquoted, const char *quoted)
    110 {
    111     const unsigned char *cp;
    112     int     ch;
    113 
    114     for (cp = (const unsigned char *) quoted; (ch = *cp) != 0; cp++) {
    115 	if (ch == '+') {
    116 	    if (ISDIGIT(cp[1]))
    117 		ch = (cp[1] - '0') << 4;
    118 	    else if (cp[1] >= 'a' && cp[1] <= 'f')
    119 		ch = (cp[1] - 'a' + 10) << 4;
    120 	    else if (cp[1] >= 'A' && cp[1] <= 'F')
    121 		ch = (cp[1] - 'A' + 10) << 4;
    122 	    else
    123 		return (0);
    124 	    if (ISDIGIT(cp[2]))
    125 		ch |= (cp[2] - '0');
    126 	    else if (cp[2] >= 'a' && cp[2] <= 'f')
    127 		ch |= (cp[2] - 'a' + 10);
    128 	    else if (cp[2] >= 'A' && cp[2] <= 'F')
    129 		ch |= (cp[2] - 'A' + 10);
    130 	    else
    131 		return (0);
    132 	    cp += 2;
    133 	}
    134 	VSTRING_ADDCH(unquoted, ch);
    135     }
    136     VSTRING_TERMINATE(unquoted);
    137     return (unquoted);
    138 }
    139 
    140 /* xtext_unquote - quoted data to unquoted */
    141 
    142 VSTRING *xtext_unquote(VSTRING *unquoted, const char *quoted)
    143 {
    144     VSTRING_RESET(unquoted);
    145     return (xtext_unquote_append(unquoted, quoted) ? unquoted : 0);
    146 }
    147 
    148 #ifdef TEST
    149 
    150  /*
    151   * Proof-of-concept test program: convert to quoted and back.
    152   */
    153 #include <vstream.h>
    154 
    155 #define BUFLEN 1024
    156 
    157 static ssize_t read_buf(VSTREAM *fp, VSTRING *buf)
    158 {
    159     ssize_t len;
    160 
    161     len = vstream_fread_buf(fp, buf, BUFLEN);
    162     VSTRING_TERMINATE(buf);
    163     return (len);
    164 }
    165 
    166 int     main(int unused_argc, char **unused_argv)
    167 {
    168     VSTRING *unquoted = vstring_alloc(BUFLEN);
    169     VSTRING *quoted = vstring_alloc(100);
    170     ssize_t len;
    171 
    172     /*
    173      * Negative tests.
    174      */
    175     if (xtext_unquote(unquoted, "++1") != 0)
    176 	msg_warn("undetected error pattern 1");
    177     if (xtext_unquote(unquoted, "+2+") != 0)
    178 	msg_warn("undetected error pattern 2");
    179 
    180     /*
    181      * Positive tests.
    182      */
    183     while ((len = read_buf(VSTREAM_IN, unquoted)) > 0) {
    184 	xtext_quote(quoted, STR(unquoted), "+=");
    185 	if (xtext_unquote(unquoted, STR(quoted)) == 0)
    186 	    msg_fatal("bad input: %.100s", STR(quoted));
    187 	if (LEN(unquoted) != len)
    188 	    msg_fatal("len %ld != unquoted len %ld",
    189 		      (long) len, (long) LEN(unquoted));
    190 	if (vstream_fwrite(VSTREAM_OUT, STR(unquoted), LEN(unquoted)) != LEN(unquoted))
    191 	    msg_fatal("write error: %m");
    192     }
    193     vstream_fflush(VSTREAM_OUT);
    194     vstring_free(unquoted);
    195     vstring_free(quoted);
    196     return (0);
    197 }
    198 
    199 #endif
    200