source: trunk/src/common.c @ 136

Last change on this file since 136 was 136, checked in by tim, 15 years ago

fixed various integer issues and memory allocation issues

polished error message functions and added initial messages in a few places

  • Property svn:keywords set to Id
File size: 9.5 KB
Line 
1/*
2 * This file stores code common to the command line tools.
3 * XXX: This should be converted to a proper library.
4 *
5 * Copyright (C) 2005-2008 Timothy D. Morgan
6 * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 3 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
20 *
21 * $Id: common.c 136 2009-01-23 17:29:51Z tim $
22 */
23
24#include <iconv.h>
25iconv_t conv_desc;
26
27const char* key_special_chars = ",\"\\/";
28const char* subfield_special_chars = ",\"\\|";
29const char* common_special_chars = ",\"\\";
30
31#define REGLOOKUP_VERSION "0.10.0"
32
33
34void bailOut(int code, char* message)
35{
36  fprintf(stderr, message);
37  exit(code);
38}
39
40
41/* Returns a newly malloc()ed string which contains original buffer,
42 * except for non-printable or special characters are quoted in hex
43 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
44 * character.  A null terminator is added, since only ascii, not binary,
45 * is returned.
46 */
47static char* quote_buffer(const unsigned char* str, 
48                          unsigned int len, const char* special)
49{
50  unsigned int i, added_len;
51  unsigned int num_written = 0;
52
53  unsigned int buf_len = sizeof(char)*(len+1);
54  char* ret_val = NULL; 
55  char* tmp_buf;
56
57  if(buf_len > 0) 
58    ret_val = malloc(buf_len);
59  if(ret_val == NULL)
60    return NULL;
61
62  for(i=0; i<len; i++)
63  {
64    if(buf_len <= (num_written+5))
65    {
66      /* Expand the buffer by the memory consumption rate seen so far
67       * times the amount of input left to process.  The expansion is bounded
68       * below by a minimum safety increase, and above by the maximum possible
69       * output string length.  This should minimize both the number of
70       * reallocs() and the amount of wasted memory.
71       */
72      added_len = (len-i)*num_written/(i+1);
73      if((buf_len+added_len) > (len*4+1))
74        buf_len = len*4+1;
75      else
76      {
77        if (added_len < 5)
78          buf_len += 5;
79        else
80          buf_len += added_len;
81      }
82
83      tmp_buf = realloc(ret_val, buf_len);
84      if(tmp_buf == NULL)
85      {
86        free(ret_val);
87        return NULL;
88      }
89      ret_val = tmp_buf;
90    }
91   
92    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
93    {
94      num_written += snprintf(ret_val + num_written, buf_len - num_written,
95                              "\\x%.2X", str[i]);
96    }
97    else
98      ret_val[num_written++] = str[i];
99  }
100  ret_val[num_written] = '\0';
101
102  return ret_val;
103}
104
105
106/* Returns a newly malloc()ed string which contains original string,
107 * except for non-printable or special characters are quoted in hex
108 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
109 * character.
110 */
111static char* quote_string(const char* str, const char* special)
112{
113  unsigned int len;
114
115  if(str == NULL)
116    return NULL;
117
118  len = strlen(str);
119  return quote_buffer((const unsigned char*)str, len, special);
120}
121
122
123/*
124 * Convert from UTF-16LE to ASCII.  Accepts a Unicode buffer, uni, and
125 * it's length, uni_max.  Writes ASCII to the buffer ascii, whose size
126 * is ascii_max.  Writes at most (ascii_max-1) bytes to ascii, and null
127 * terminates the string.  Returns the length of the data written to
128 * ascii.  On error, returns a negative errno code.
129 */
130static int uni_to_ascii(unsigned char* uni, char* ascii, 
131                        uint32 uni_max, uint32 ascii_max)
132{
133  char* inbuf = (char*)uni;
134  char* outbuf = ascii;
135  size_t in_len = (size_t)uni_max;
136  size_t out_len = (size_t)(ascii_max-1);
137  int ret;
138
139  /* Set up conversion descriptor. */
140  conv_desc = iconv_open("US-ASCII//TRANSLIT", "UTF-16LE");
141
142  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
143  if(ret == -1)
144  {
145    iconv_close(conv_desc);
146    return -errno;
147  }
148  *outbuf = '\0';
149
150  iconv_close(conv_desc); 
151  return ascii_max-out_len-1;
152}
153
154
155static char* quote_unicode(unsigned char* uni, uint32 length, 
156                           const char* special, char** error_msg)
157{
158  char* ret_val;
159  char* ascii = NULL;
160  char* tmp_err;
161  int ret_err;
162  *error_msg = NULL;
163
164  if(length+1 > 0)
165    ascii = malloc(length+1);
166  if(ascii == NULL)
167  {
168    *error_msg = (char*)malloc(27);
169    if(*error_msg == NULL)
170      return NULL;
171    strcpy(*error_msg, "Memory allocation failure.");
172    return NULL;
173  }
174 
175  ret_err = uni_to_ascii(uni, ascii, length, length+1);
176  if(ret_err < 0)
177  {
178    free(ascii);
179    tmp_err = strerror(-ret_err);
180    *error_msg = (char*)malloc(61+strlen(tmp_err));
181    if(*error_msg == NULL)
182      return NULL;
183
184    sprintf(*error_msg, 
185            "Unicode conversion failed with '%s'. Quoting as binary.", tmp_err);
186    ret_val = quote_buffer(uni, length, special);
187  }
188  else
189  {
190    ret_val = quote_string(ascii, special);
191    free(ascii);
192  }
193 
194  return ret_val;
195}
196
197
198/*
199 * Convert a data value to a string for display.  Returns NULL on error,
200 * and the string to display if there is no error, or a non-fatal
201 * error.  On any error (fatal or non-fatal) occurs, (*error_msg) will
202 * be set to a newly allocated string, containing an error message.  If
203 * a memory allocation failure occurs while generating the error
204 * message, both the return value and (*error_msg) will be NULL.  It
205 * is the responsibility of the caller to free both a non-NULL return
206 * value, and a non-NULL (*error_msg).
207 */
208static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type, 
209                           char** error_msg)
210{
211  char* asciip;
212  char* ascii;
213  char* ascii_tmp;
214  char* cur_quoted;
215  char* tmp_err = NULL;
216  const char* delim;
217  uint32 i;
218  uint32 cur_str_len;
219  uint32 ascii_max;
220  uint32 str_rem, alen;
221  int ret_err;
222
223  if(datap == NULL)
224  {
225    *error_msg = (char*)malloc(24);
226    if(*error_msg == NULL)
227      return NULL;
228    strcpy(*error_msg, "Data pointer was NULL.");
229    return NULL;
230  }
231  *error_msg = NULL;
232
233  switch (type) 
234  {
235  case REG_SZ:
236  case REG_EXPAND_SZ:
237    /* REG_LINK is a symbolic link, stored as a unicode string. */
238  case REG_LINK:
239    /* Sometimes values have binary stored in them.  If the unicode
240     * conversion fails, just quote it raw.
241     */
242    cur_quoted = quote_unicode(datap, len, common_special_chars, &tmp_err);
243    if(cur_quoted == NULL)
244    {
245      if(tmp_err == NULL && (*error_msg = (char*)malloc(49)) != NULL)
246        strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
247      else if((*error_msg = (char*)malloc(42+strlen(tmp_err))) != NULL)
248      {
249        sprintf(*error_msg, "Buffer could not be quoted due to error: %s", 
250                tmp_err);
251        free(tmp_err);
252      }
253    }
254    else if (tmp_err != NULL)
255      *error_msg = tmp_err;
256    return cur_quoted;
257    break;
258
259  case REG_DWORD:
260    ascii_max = sizeof(char)*(8+2+1);
261    ascii = malloc(ascii_max);
262    if(ascii == NULL)
263      return NULL;
264
265    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
266             datap[3], datap[2], datap[1], datap[0]);
267    return ascii;
268    break;
269
270  case REG_DWORD_BE:
271    ascii_max = sizeof(char)*(8+2+1);
272    ascii = malloc(ascii_max);
273    if(ascii == NULL)
274      return NULL;
275
276    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
277             datap[0], datap[1], datap[2], datap[3]);
278    return ascii;
279    break;
280
281  case REG_QWORD:
282    ascii_max = sizeof(char)*(16+2+1);
283    ascii = malloc(ascii_max);
284    if(ascii == NULL)
285      return NULL;
286
287    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
288             datap[7], datap[6], datap[5], datap[4],
289             datap[3], datap[2], datap[1], datap[0]);
290    return ascii;
291    break;
292   
293  case REG_MULTI_SZ:
294    ascii_max = sizeof(char)*(len*4+1);
295    ascii_tmp = malloc(ascii_max);
296    if(ascii_tmp == NULL)
297      return NULL;
298
299    /* Attempt to convert entire string from UTF-16LE to ASCII,
300     * then parse and quote fields individually.
301     * If this fails, simply quote entire buffer as binary.
302     */
303    ret_err = uni_to_ascii(datap, ascii_tmp, len, ascii_max);
304    if(ret_err < 0)
305    {
306      tmp_err = strerror(-ret_err);
307      *error_msg = (char*)malloc(61+strlen(tmp_err));
308      if(*error_msg == NULL)
309      {
310        free(ascii_tmp);
311        return NULL;
312      }
313
314      sprintf(*error_msg, "MULTI_SZ unicode conversion"
315              " failed with '%s'. Quoting as binary.", tmp_err);
316      ascii = quote_buffer(datap, len, subfield_special_chars);
317    }
318    else
319    {
320      ascii = malloc(ascii_max);
321      if(ascii == NULL)
322      {
323        free(ascii_tmp);
324        return NULL;
325      }
326      asciip = ascii;
327      asciip[0] = '\0';
328      str_rem = ascii_max;
329      delim = "";
330      for(i=0; i<ret_err; i+=cur_str_len+1)
331      {
332        cur_str_len = strlen(ascii_tmp+i);
333        if(ascii_tmp[i] != '\0')
334        {
335          cur_quoted = quote_string(ascii_tmp+i, subfield_special_chars);
336          if(cur_quoted != NULL)
337          {
338            alen = snprintf(asciip, str_rem, "%s%s", delim, cur_quoted);
339            asciip += alen;
340            str_rem -= alen;
341            free(cur_quoted);
342          }
343        }
344        delim = "|";
345      }
346    }
347
348    free(ascii_tmp);
349    return ascii;
350    break;
351
352  /* XXX: Dont know what to do with these yet, just print as binary... */
353  default:
354    *error_msg = (char*)malloc(65);
355    if(*error_msg == NULL)
356      return NULL;
357    sprintf(*error_msg,
358            "Unrecognized registry data type (0x%.8X); quoting as binary.",
359            type);
360   
361  case REG_NONE:
362  case REG_RESOURCE_LIST:
363  case REG_FULL_RESOURCE_DESCRIPTOR:
364  case REG_RESOURCE_REQUIREMENTS_LIST:
365
366  case REG_BINARY:
367    return quote_buffer(datap, len, common_special_chars);
368    break;
369  }
370
371  return NULL;
372}
Note: See TracBrowser for help on using the repository browser.