Home | History | Annotate | Line # | Download | only in libiberty
rust-demangle.c revision 1.1.1.1.2.2
      1  1.1.1.1.2.2  pgoyette /* Demangler for the Rust programming language
      2  1.1.1.1.2.2  pgoyette    Copyright (C) 2016-2017 Free Software Foundation, Inc.
      3  1.1.1.1.2.2  pgoyette    Written by David Tolnay (dtolnay (at) gmail.com).
      4  1.1.1.1.2.2  pgoyette 
      5  1.1.1.1.2.2  pgoyette This file is part of the libiberty library.
      6  1.1.1.1.2.2  pgoyette Libiberty is free software; you can redistribute it and/or
      7  1.1.1.1.2.2  pgoyette modify it under the terms of the GNU Library General Public
      8  1.1.1.1.2.2  pgoyette License as published by the Free Software Foundation; either
      9  1.1.1.1.2.2  pgoyette version 2 of the License, or (at your option) any later version.
     10  1.1.1.1.2.2  pgoyette 
     11  1.1.1.1.2.2  pgoyette In addition to the permissions in the GNU Library General Public
     12  1.1.1.1.2.2  pgoyette License, the Free Software Foundation gives you unlimited permission
     13  1.1.1.1.2.2  pgoyette to link the compiled version of this file into combinations with other
     14  1.1.1.1.2.2  pgoyette programs, and to distribute those combinations without any restriction
     15  1.1.1.1.2.2  pgoyette coming from the use of this file.  (The Library Public License
     16  1.1.1.1.2.2  pgoyette restrictions do apply in other respects; for example, they cover
     17  1.1.1.1.2.2  pgoyette modification of the file, and distribution when not linked into a
     18  1.1.1.1.2.2  pgoyette combined executable.)
     19  1.1.1.1.2.2  pgoyette 
     20  1.1.1.1.2.2  pgoyette Libiberty is distributed in the hope that it will be useful,
     21  1.1.1.1.2.2  pgoyette but WITHOUT ANY WARRANTY; without even the implied warranty of
     22  1.1.1.1.2.2  pgoyette MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     23  1.1.1.1.2.2  pgoyette Library General Public License for more details.
     24  1.1.1.1.2.2  pgoyette 
     25  1.1.1.1.2.2  pgoyette You should have received a copy of the GNU Library General Public
     26  1.1.1.1.2.2  pgoyette License along with libiberty; see the file COPYING.LIB.
     27  1.1.1.1.2.2  pgoyette If not, see <http://www.gnu.org/licenses/>.  */
     28  1.1.1.1.2.2  pgoyette 
     29  1.1.1.1.2.2  pgoyette 
     30  1.1.1.1.2.2  pgoyette #ifdef HAVE_CONFIG_H
     31  1.1.1.1.2.2  pgoyette #include "config.h"
     32  1.1.1.1.2.2  pgoyette #endif
     33  1.1.1.1.2.2  pgoyette 
     34  1.1.1.1.2.2  pgoyette #include "safe-ctype.h"
     35  1.1.1.1.2.2  pgoyette 
     36  1.1.1.1.2.2  pgoyette #include <sys/types.h>
     37  1.1.1.1.2.2  pgoyette #include <string.h>
     38  1.1.1.1.2.2  pgoyette #include <stdio.h>
     39  1.1.1.1.2.2  pgoyette 
     40  1.1.1.1.2.2  pgoyette #ifdef HAVE_STRING_H
     41  1.1.1.1.2.2  pgoyette #include <string.h>
     42  1.1.1.1.2.2  pgoyette #else
     43  1.1.1.1.2.2  pgoyette extern size_t strlen(const char *s);
     44  1.1.1.1.2.2  pgoyette extern int strncmp(const char *s1, const char *s2, size_t n);
     45  1.1.1.1.2.2  pgoyette extern void *memset(void *s, int c, size_t n);
     46  1.1.1.1.2.2  pgoyette #endif
     47  1.1.1.1.2.2  pgoyette 
     48  1.1.1.1.2.2  pgoyette #include <demangle.h>
     49  1.1.1.1.2.2  pgoyette #include "libiberty.h"
     50  1.1.1.1.2.2  pgoyette 
     51  1.1.1.1.2.2  pgoyette 
     52  1.1.1.1.2.2  pgoyette /* Mangled Rust symbols look like this:
     53  1.1.1.1.2.2  pgoyette      _$LT$std..sys..fd..FileDesc$u20$as$u20$core..ops..Drop$GT$::drop::hc68340e1baa4987a
     54  1.1.1.1.2.2  pgoyette 
     55  1.1.1.1.2.2  pgoyette    The original symbol is:
     56  1.1.1.1.2.2  pgoyette      <std::sys::fd::FileDesc as core::ops::Drop>::drop
     57  1.1.1.1.2.2  pgoyette 
     58  1.1.1.1.2.2  pgoyette    The last component of the path is a 64-bit hash in lowercase hex,
     59  1.1.1.1.2.2  pgoyette    prefixed with "h". Rust does not have a global namespace between
     60  1.1.1.1.2.2  pgoyette    crates, an illusion which Rust maintains by using the hash to
     61  1.1.1.1.2.2  pgoyette    distinguish things that would otherwise have the same symbol.
     62  1.1.1.1.2.2  pgoyette 
     63  1.1.1.1.2.2  pgoyette    Any path component not starting with a XID_Start character is
     64  1.1.1.1.2.2  pgoyette    prefixed with "_".
     65  1.1.1.1.2.2  pgoyette 
     66  1.1.1.1.2.2  pgoyette    The following escape sequences are used:
     67  1.1.1.1.2.2  pgoyette 
     68  1.1.1.1.2.2  pgoyette    ","  =>  $C$
     69  1.1.1.1.2.2  pgoyette    "@"  =>  $SP$
     70  1.1.1.1.2.2  pgoyette    "*"  =>  $BP$
     71  1.1.1.1.2.2  pgoyette    "&"  =>  $RF$
     72  1.1.1.1.2.2  pgoyette    "<"  =>  $LT$
     73  1.1.1.1.2.2  pgoyette    ">"  =>  $GT$
     74  1.1.1.1.2.2  pgoyette    "("  =>  $LP$
     75  1.1.1.1.2.2  pgoyette    ")"  =>  $RP$
     76  1.1.1.1.2.2  pgoyette    " "  =>  $u20$
     77  1.1.1.1.2.2  pgoyette    "\"" =>  $u22$
     78  1.1.1.1.2.2  pgoyette    "'"  =>  $u27$
     79  1.1.1.1.2.2  pgoyette    "+"  =>  $u2b$
     80  1.1.1.1.2.2  pgoyette    ";"  =>  $u3b$
     81  1.1.1.1.2.2  pgoyette    "["  =>  $u5b$
     82  1.1.1.1.2.2  pgoyette    "]"  =>  $u5d$
     83  1.1.1.1.2.2  pgoyette    "{"  =>  $u7b$
     84  1.1.1.1.2.2  pgoyette    "}"  =>  $u7d$
     85  1.1.1.1.2.2  pgoyette    "~"  =>  $u7e$
     86  1.1.1.1.2.2  pgoyette 
     87  1.1.1.1.2.2  pgoyette    A double ".." means "::" and a single "." means "-".
     88  1.1.1.1.2.2  pgoyette 
     89  1.1.1.1.2.2  pgoyette    The only characters allowed in the mangled symbol are a-zA-Z0-9 and _.:$  */
     90  1.1.1.1.2.2  pgoyette 
     91  1.1.1.1.2.2  pgoyette static const char *hash_prefix = "::h";
     92  1.1.1.1.2.2  pgoyette static const size_t hash_prefix_len = 3;
     93  1.1.1.1.2.2  pgoyette static const size_t hash_len = 16;
     94  1.1.1.1.2.2  pgoyette 
     95  1.1.1.1.2.2  pgoyette static int is_prefixed_hash (const char *start);
     96  1.1.1.1.2.2  pgoyette static int looks_like_rust (const char *sym, size_t len);
     97  1.1.1.1.2.2  pgoyette static int unescape (const char **in, char **out, const char *seq, char value);
     98  1.1.1.1.2.2  pgoyette 
     99  1.1.1.1.2.2  pgoyette /* INPUT: sym: symbol that has been through C++ (gnu v3) demangling
    100  1.1.1.1.2.2  pgoyette 
    101  1.1.1.1.2.2  pgoyette    This function looks for the following indicators:
    102  1.1.1.1.2.2  pgoyette 
    103  1.1.1.1.2.2  pgoyette    1. The hash must consist of "h" followed by 16 lowercase hex digits.
    104  1.1.1.1.2.2  pgoyette 
    105  1.1.1.1.2.2  pgoyette    2. As a sanity check, the hash must use between 5 and 15 of the 16
    106  1.1.1.1.2.2  pgoyette       possible hex digits. This is true of 99.9998% of hashes so once
    107  1.1.1.1.2.2  pgoyette       in your life you may see a false negative. The point is to
    108  1.1.1.1.2.2  pgoyette       notice path components that could be Rust hashes but are
    109  1.1.1.1.2.2  pgoyette       probably not, like "haaaaaaaaaaaaaaaa". In this case a false
    110  1.1.1.1.2.2  pgoyette       positive (non-Rust symbol has an important path component
    111  1.1.1.1.2.2  pgoyette       removed because it looks like a Rust hash) is worse than a false
    112  1.1.1.1.2.2  pgoyette       negative (the rare Rust symbol is not demangled) so this sets
    113  1.1.1.1.2.2  pgoyette       the balance in favor of false negatives.
    114  1.1.1.1.2.2  pgoyette 
    115  1.1.1.1.2.2  pgoyette    3. There must be no characters other than a-zA-Z0-9 and _.:$
    116  1.1.1.1.2.2  pgoyette 
    117  1.1.1.1.2.2  pgoyette    4. There must be no unrecognized $-sign sequences.
    118  1.1.1.1.2.2  pgoyette 
    119  1.1.1.1.2.2  pgoyette    5. There must be no sequence of three or more dots in a row ("...").  */
    120  1.1.1.1.2.2  pgoyette 
    121  1.1.1.1.2.2  pgoyette int
    122  1.1.1.1.2.2  pgoyette rust_is_mangled (const char *sym)
    123  1.1.1.1.2.2  pgoyette {
    124  1.1.1.1.2.2  pgoyette   size_t len, len_without_hash;
    125  1.1.1.1.2.2  pgoyette 
    126  1.1.1.1.2.2  pgoyette   if (!sym)
    127  1.1.1.1.2.2  pgoyette     return 0;
    128  1.1.1.1.2.2  pgoyette 
    129  1.1.1.1.2.2  pgoyette   len = strlen (sym);
    130  1.1.1.1.2.2  pgoyette   if (len <= hash_prefix_len + hash_len)
    131  1.1.1.1.2.2  pgoyette     /* Not long enough to contain "::h" + hash + something else */
    132  1.1.1.1.2.2  pgoyette     return 0;
    133  1.1.1.1.2.2  pgoyette 
    134  1.1.1.1.2.2  pgoyette   len_without_hash = len - (hash_prefix_len + hash_len);
    135  1.1.1.1.2.2  pgoyette   if (!is_prefixed_hash (sym + len_without_hash))
    136  1.1.1.1.2.2  pgoyette     return 0;
    137  1.1.1.1.2.2  pgoyette 
    138  1.1.1.1.2.2  pgoyette   return looks_like_rust (sym, len_without_hash);
    139  1.1.1.1.2.2  pgoyette }
    140  1.1.1.1.2.2  pgoyette 
    141  1.1.1.1.2.2  pgoyette /* A hash is the prefix "::h" followed by 16 lowercase hex digits. The
    142  1.1.1.1.2.2  pgoyette    hex digits must comprise between 5 and 15 (inclusive) distinct
    143  1.1.1.1.2.2  pgoyette    digits.  */
    144  1.1.1.1.2.2  pgoyette 
    145  1.1.1.1.2.2  pgoyette static int
    146  1.1.1.1.2.2  pgoyette is_prefixed_hash (const char *str)
    147  1.1.1.1.2.2  pgoyette {
    148  1.1.1.1.2.2  pgoyette   const char *end;
    149  1.1.1.1.2.2  pgoyette   char seen[16];
    150  1.1.1.1.2.2  pgoyette   size_t i;
    151  1.1.1.1.2.2  pgoyette   int count;
    152  1.1.1.1.2.2  pgoyette 
    153  1.1.1.1.2.2  pgoyette   if (strncmp (str, hash_prefix, hash_prefix_len))
    154  1.1.1.1.2.2  pgoyette     return 0;
    155  1.1.1.1.2.2  pgoyette   str += hash_prefix_len;
    156  1.1.1.1.2.2  pgoyette 
    157  1.1.1.1.2.2  pgoyette   memset (seen, 0, sizeof(seen));
    158  1.1.1.1.2.2  pgoyette   for (end = str + hash_len; str < end; str++)
    159  1.1.1.1.2.2  pgoyette     if (*str >= '0' && *str <= '9')
    160  1.1.1.1.2.2  pgoyette       seen[*str - '0'] = 1;
    161  1.1.1.1.2.2  pgoyette     else if (*str >= 'a' && *str <= 'f')
    162  1.1.1.1.2.2  pgoyette       seen[*str - 'a' + 10] = 1;
    163  1.1.1.1.2.2  pgoyette     else
    164  1.1.1.1.2.2  pgoyette       return 0;
    165  1.1.1.1.2.2  pgoyette 
    166  1.1.1.1.2.2  pgoyette   /* Count how many distinct digits seen */
    167  1.1.1.1.2.2  pgoyette   count = 0;
    168  1.1.1.1.2.2  pgoyette   for (i = 0; i < 16; i++)
    169  1.1.1.1.2.2  pgoyette     if (seen[i])
    170  1.1.1.1.2.2  pgoyette       count++;
    171  1.1.1.1.2.2  pgoyette 
    172  1.1.1.1.2.2  pgoyette   return count >= 5 && count <= 15;
    173  1.1.1.1.2.2  pgoyette }
    174  1.1.1.1.2.2  pgoyette 
    175  1.1.1.1.2.2  pgoyette static int
    176  1.1.1.1.2.2  pgoyette looks_like_rust (const char *str, size_t len)
    177  1.1.1.1.2.2  pgoyette {
    178  1.1.1.1.2.2  pgoyette   const char *end = str + len;
    179  1.1.1.1.2.2  pgoyette 
    180  1.1.1.1.2.2  pgoyette   while (str < end)
    181  1.1.1.1.2.2  pgoyette     switch (*str)
    182  1.1.1.1.2.2  pgoyette       {
    183  1.1.1.1.2.2  pgoyette       case '$':
    184  1.1.1.1.2.2  pgoyette 	if (!strncmp (str, "$C$", 3))
    185  1.1.1.1.2.2  pgoyette 	  str += 3;
    186  1.1.1.1.2.2  pgoyette 	else if (!strncmp (str, "$SP$", 4)
    187  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$BP$", 4)
    188  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$RF$", 4)
    189  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$LT$", 4)
    190  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$GT$", 4)
    191  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$LP$", 4)
    192  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$RP$", 4))
    193  1.1.1.1.2.2  pgoyette 	  str += 4;
    194  1.1.1.1.2.2  pgoyette 	else if (!strncmp (str, "$u20$", 5)
    195  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$u22$", 5)
    196  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$u27$", 5)
    197  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$u2b$", 5)
    198  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$u3b$", 5)
    199  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$u5b$", 5)
    200  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$u5d$", 5)
    201  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$u7b$", 5)
    202  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$u7d$", 5)
    203  1.1.1.1.2.2  pgoyette 		 || !strncmp (str, "$u7e$", 5))
    204  1.1.1.1.2.2  pgoyette 	  str += 5;
    205  1.1.1.1.2.2  pgoyette 	else
    206  1.1.1.1.2.2  pgoyette 	  return 0;
    207  1.1.1.1.2.2  pgoyette 	break;
    208  1.1.1.1.2.2  pgoyette       case '.':
    209  1.1.1.1.2.2  pgoyette 	/* Do not allow three or more consecutive dots */
    210  1.1.1.1.2.2  pgoyette 	if (!strncmp (str, "...", 3))
    211  1.1.1.1.2.2  pgoyette 	  return 0;
    212  1.1.1.1.2.2  pgoyette 	/* Fall through */
    213  1.1.1.1.2.2  pgoyette       case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
    214  1.1.1.1.2.2  pgoyette       case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
    215  1.1.1.1.2.2  pgoyette       case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
    216  1.1.1.1.2.2  pgoyette       case 's': case 't': case 'u': case 'v': case 'w': case 'x':
    217  1.1.1.1.2.2  pgoyette       case 'y': case 'z':
    218  1.1.1.1.2.2  pgoyette       case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
    219  1.1.1.1.2.2  pgoyette       case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
    220  1.1.1.1.2.2  pgoyette       case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
    221  1.1.1.1.2.2  pgoyette       case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
    222  1.1.1.1.2.2  pgoyette       case 'Y': case 'Z':
    223  1.1.1.1.2.2  pgoyette       case '0': case '1': case '2': case '3': case '4': case '5':
    224  1.1.1.1.2.2  pgoyette       case '6': case '7': case '8': case '9':
    225  1.1.1.1.2.2  pgoyette       case '_':
    226  1.1.1.1.2.2  pgoyette       case ':':
    227  1.1.1.1.2.2  pgoyette 	str++;
    228  1.1.1.1.2.2  pgoyette 	break;
    229  1.1.1.1.2.2  pgoyette       default:
    230  1.1.1.1.2.2  pgoyette 	return 0;
    231  1.1.1.1.2.2  pgoyette       }
    232  1.1.1.1.2.2  pgoyette 
    233  1.1.1.1.2.2  pgoyette   return 1;
    234  1.1.1.1.2.2  pgoyette }
    235  1.1.1.1.2.2  pgoyette 
    236  1.1.1.1.2.2  pgoyette /*
    237  1.1.1.1.2.2  pgoyette   INPUT: sym: symbol for which rust_is_mangled(sym) returned 1.
    238  1.1.1.1.2.2  pgoyette 
    239  1.1.1.1.2.2  pgoyette   The input is demangled in-place because the mangled name is always
    240  1.1.1.1.2.2  pgoyette   longer than the demangled one.  */
    241  1.1.1.1.2.2  pgoyette 
    242  1.1.1.1.2.2  pgoyette void
    243  1.1.1.1.2.2  pgoyette rust_demangle_sym (char *sym)
    244  1.1.1.1.2.2  pgoyette {
    245  1.1.1.1.2.2  pgoyette   const char *in;
    246  1.1.1.1.2.2  pgoyette   char *out;
    247  1.1.1.1.2.2  pgoyette   const char *end;
    248  1.1.1.1.2.2  pgoyette 
    249  1.1.1.1.2.2  pgoyette   if (!sym)
    250  1.1.1.1.2.2  pgoyette     return;
    251  1.1.1.1.2.2  pgoyette 
    252  1.1.1.1.2.2  pgoyette   in = sym;
    253  1.1.1.1.2.2  pgoyette   out = sym;
    254  1.1.1.1.2.2  pgoyette   end = sym + strlen (sym) - (hash_prefix_len + hash_len);
    255  1.1.1.1.2.2  pgoyette 
    256  1.1.1.1.2.2  pgoyette   while (in < end)
    257  1.1.1.1.2.2  pgoyette     switch (*in)
    258  1.1.1.1.2.2  pgoyette       {
    259  1.1.1.1.2.2  pgoyette       case '$':
    260  1.1.1.1.2.2  pgoyette 	if (!(unescape (&in, &out, "$C$", ',')
    261  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$SP$", '@')
    262  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$BP$", '*')
    263  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$RF$", '&')
    264  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$LT$", '<')
    265  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$GT$", '>')
    266  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$LP$", '(')
    267  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$RP$", ')')
    268  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u20$", ' ')
    269  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u22$", '\"')
    270  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u27$", '\'')
    271  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u2b$", '+')
    272  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u3b$", ';')
    273  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u5b$", '[')
    274  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u5d$", ']')
    275  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u7b$", '{')
    276  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u7d$", '}')
    277  1.1.1.1.2.2  pgoyette 	      || unescape (&in, &out, "$u7e$", '~'))) {
    278  1.1.1.1.2.2  pgoyette 	  /* unexpected escape sequence, not looks_like_rust. */
    279  1.1.1.1.2.2  pgoyette 	  goto fail;
    280  1.1.1.1.2.2  pgoyette 	}
    281  1.1.1.1.2.2  pgoyette 	break;
    282  1.1.1.1.2.2  pgoyette       case '_':
    283  1.1.1.1.2.2  pgoyette 	/* If this is the start of a path component and the next
    284  1.1.1.1.2.2  pgoyette 	   character is an escape sequence, ignore the underscore. The
    285  1.1.1.1.2.2  pgoyette 	   mangler inserts an underscore to make sure the path
    286  1.1.1.1.2.2  pgoyette 	   component begins with a XID_Start character. */
    287  1.1.1.1.2.2  pgoyette 	if ((in == sym || in[-1] == ':') && in[1] == '$')
    288  1.1.1.1.2.2  pgoyette 	  in++;
    289  1.1.1.1.2.2  pgoyette 	else
    290  1.1.1.1.2.2  pgoyette 	  *out++ = *in++;
    291  1.1.1.1.2.2  pgoyette 	break;
    292  1.1.1.1.2.2  pgoyette       case '.':
    293  1.1.1.1.2.2  pgoyette 	if (in[1] == '.')
    294  1.1.1.1.2.2  pgoyette 	  {
    295  1.1.1.1.2.2  pgoyette 	    /* ".." becomes "::" */
    296  1.1.1.1.2.2  pgoyette 	    *out++ = ':';
    297  1.1.1.1.2.2  pgoyette 	    *out++ = ':';
    298  1.1.1.1.2.2  pgoyette 	    in += 2;
    299  1.1.1.1.2.2  pgoyette 	  }
    300  1.1.1.1.2.2  pgoyette 	else
    301  1.1.1.1.2.2  pgoyette 	  {
    302  1.1.1.1.2.2  pgoyette 	    /* "." becomes "-" */
    303  1.1.1.1.2.2  pgoyette 	    *out++ = '-';
    304  1.1.1.1.2.2  pgoyette 	    in++;
    305  1.1.1.1.2.2  pgoyette 	  }
    306  1.1.1.1.2.2  pgoyette 	break;
    307  1.1.1.1.2.2  pgoyette       case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
    308  1.1.1.1.2.2  pgoyette       case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
    309  1.1.1.1.2.2  pgoyette       case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
    310  1.1.1.1.2.2  pgoyette       case 's': case 't': case 'u': case 'v': case 'w': case 'x':
    311  1.1.1.1.2.2  pgoyette       case 'y': case 'z':
    312  1.1.1.1.2.2  pgoyette       case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
    313  1.1.1.1.2.2  pgoyette       case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
    314  1.1.1.1.2.2  pgoyette       case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
    315  1.1.1.1.2.2  pgoyette       case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
    316  1.1.1.1.2.2  pgoyette       case 'Y': case 'Z':
    317  1.1.1.1.2.2  pgoyette       case '0': case '1': case '2': case '3': case '4': case '5':
    318  1.1.1.1.2.2  pgoyette       case '6': case '7': case '8': case '9':
    319  1.1.1.1.2.2  pgoyette       case ':':
    320  1.1.1.1.2.2  pgoyette 	*out++ = *in++;
    321  1.1.1.1.2.2  pgoyette 	break;
    322  1.1.1.1.2.2  pgoyette       default:
    323  1.1.1.1.2.2  pgoyette 	/* unexpected character in symbol, not looks_like_rust.  */
    324  1.1.1.1.2.2  pgoyette 	goto fail;
    325  1.1.1.1.2.2  pgoyette       }
    326  1.1.1.1.2.2  pgoyette   goto done;
    327  1.1.1.1.2.2  pgoyette 
    328  1.1.1.1.2.2  pgoyette fail:
    329  1.1.1.1.2.2  pgoyette   *out++ = '?'; /* This is pretty lame, but it's hard to do better. */
    330  1.1.1.1.2.2  pgoyette done:
    331  1.1.1.1.2.2  pgoyette   *out = '\0';
    332  1.1.1.1.2.2  pgoyette }
    333  1.1.1.1.2.2  pgoyette 
    334  1.1.1.1.2.2  pgoyette static int
    335  1.1.1.1.2.2  pgoyette unescape (const char **in, char **out, const char *seq, char value)
    336  1.1.1.1.2.2  pgoyette {
    337  1.1.1.1.2.2  pgoyette   size_t len = strlen (seq);
    338  1.1.1.1.2.2  pgoyette 
    339  1.1.1.1.2.2  pgoyette   if (strncmp (*in, seq, len))
    340  1.1.1.1.2.2  pgoyette     return 0;
    341  1.1.1.1.2.2  pgoyette 
    342  1.1.1.1.2.2  pgoyette   **out = value;
    343  1.1.1.1.2.2  pgoyette 
    344  1.1.1.1.2.2  pgoyette   *in += len;
    345  1.1.1.1.2.2  pgoyette   *out += 1;
    346  1.1.1.1.2.2  pgoyette 
    347  1.1.1.1.2.2  pgoyette   return 1;
    348  1.1.1.1.2.2  pgoyette }
    349