Buffer overflow hunt: removing strcpy, strcat, sprintf
[fw/sdcc] / src / SDCCutil.c
1 /*-------------------------------------------------------------------------
2   SDCCutil.c - Small utility functions.
3
4              Written By -  Sandeep Dutta . sandeep.dutta@usa.net (1999)
5
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20    In other words, you are welcome to use, share and improve this program.
21    You are forbidden to forbid anyone else to use, share and improve
22    what you give them.   Help stamp out software-hoarding!
23 -------------------------------------------------------------------------*/
24
25 #include "common.h"
26 #include "SDCCmacro.h"
27 #include "SDCCutil.h"
28 #include "newalloc.h"
29 #include <sys/stat.h>
30
31 /** Given an array of name, value string pairs creates a new hash
32     containing all of the pairs.
33 */
34 hTab *
35 populateStringHash(const char **pin)
36 {
37   hTab *pret = NULL;
38
39   while (*pin)
40     {
41       shash_add (&pret, pin[0], pin[1]);
42       pin += 2;
43     }
44
45   return pret;
46 }
47
48 /** Given an array of string pointers and another string, adds the
49     string to the end of the list.  The end of the list is assumed to
50     be the first NULL pointer.
51 */
52 void
53 addToList (const char **list, const char *str)
54 {
55   /* This is the bad way to do things :) */
56   while (*list)
57     list++;
58   *list = Safe_strdup (str);
59   if (!*list)
60     {
61       werror (E_OUT_OF_MEM, __FILE__, 0);
62       exit (1);
63     }
64   *(++list) = NULL;
65 }
66
67 /** Given an array of string pointers returns a string containing all
68     of the strings seperated by spaces.  The returned string is on the
69     heap.  The join stops when a NULL pointer is hit.
70 */
71 char *
72 join(const char **pplist)
73 {
74     buffer[0] = 0;  
75     
76     while (*pplist)
77     {
78         strncatz(buffer, *pplist, PATH_MAX);
79         strncatz(buffer, " ", PATH_MAX);
80         pplist++;
81     }
82
83     return buffer;
84 }
85
86 /** Given an array of string pointers, returns a string containing all
87     of the strings seperated by spaces.  The returned string is on the
88     heap.  n is the number of strings in the list.
89 */
90 char *
91 joinn(char **pplist, int n)
92 {
93     buffer[0] = 0;  
94     
95     while (n--)
96     {
97         strncatz(buffer, *pplist, PATH_MAX);
98         strncatz(buffer, " ", PATH_MAX);
99         pplist++;
100     }
101
102     return buffer;
103 }
104
105 /** Returns TRUE if for the host the two path characters are
106     equivalent.
107 */
108 static bool
109 pathCharsEquivalent(char c1, char c2)
110 {
111 #if NATIVE_WIN32
112   /* win32 is case insensitive */
113   if (tolower(c1) == tolower(c2))
114     {
115       return TRUE;
116     }
117   /* And / is equivalent to \ */
118   else if (c1 == '/' && c2 == '\\')
119     {
120       return TRUE;
121     }
122   else if (c1 == '\\' && c2 == '/')
123     {
124       return TRUE;
125     }
126   else
127     {
128       return FALSE;
129     }
130 #else
131   /* Assume a Unix host where they must match exactly. */
132   return c1 == c2;
133 #endif
134 }
135
136 static bool
137 pathEquivalent(const char *p1, const char *p2)
138 {
139   while (*p1 != '\0' && *p2 != '\0')
140     {
141       if (pathCharsEquivalent (*p1, *p2) == FALSE)
142         {
143           break;
144         }
145       p1++;
146       p2++;
147     }
148
149   return *p1 == *p2;
150 }
151
152 static char
153 pathCharTransform(char c)
154 {
155 #if NATIVE_WIN32
156   if (c == '/')
157     {
158       return DIR_SEPARATOR_CHAR;
159     }
160   else
161     {
162       return c;
163     }
164 #else
165   return c;
166 #endif
167 }
168
169 /** Fixes up a potentially mixed path to the proper representation for
170     the host.  Fixes up in place.
171 */
172 static char *
173 fixupPath(char *pin)
174 {
175   char *p = pin;
176
177   while (*p)
178     {
179       *p = pathCharTransform(*p);
180       p++;
181     }
182   *p = '\0';
183
184   return pin;
185 }
186
187 /** Returns the characters in p2 past the last matching characters in
188     p1.  
189 */
190 char *
191 getPathDifference (char *pinto, const char *p1, const char *p2)
192 {
193   char *p = pinto;
194
195 #if NATIVE_WIN32
196   /* win32 can have a path at the start. */
197   if (strchr(p2, ':'))
198     {
199       p2 = strchr(p2, ':')+1;
200     }
201 #endif  
202
203   while (*p1 != '\0' && *p2 != '\0')
204     {
205       if (pathCharsEquivalent(*p1, *p2) == FALSE)
206         {
207           break;
208         }
209       p1++;
210       p2++;
211     }
212   while (*p2)
213     {
214       *p++ = *p2++;
215     }
216   *p = '\0';
217
218   return fixupPath(pinto);
219 }
220
221 /** Given a file with path information in the binary files directory,
222     returns what PREFIX must be to get this path.  Used for discovery
223     of where SDCC is installed.  Returns NULL if the path is
224     impossible.
225 */
226 char *
227 getPrefixFromBinPath (const char *prel)
228 {
229   strncpyz(scratchFileName, prel, PATH_MAX);
230   /* Strip off the /sdcc at the end */
231   *strrchr(scratchFileName, DIR_SEPARATOR_CHAR) = '\0';
232   /* Compute what the difference between the prefix and the bin dir
233      should be. */
234   getPathDifference (buffer, PREFIX, BINDIR);
235
236   /* Verify that the path in has the expected suffix */
237   if (strlen(buffer) > strlen(scratchFileName))
238     {
239       /* Not long enough */
240       return NULL;
241     }
242
243   if (pathEquivalent (buffer, scratchFileName + strlen(scratchFileName) - strlen(buffer)) == FALSE)
244     {
245       /* Doesn't match */
246       return NULL;
247     }
248
249   scratchFileName[strlen(scratchFileName) - strlen(buffer)] = '\0';
250
251   return Safe_strdup (scratchFileName);
252 }
253
254 /** Returns true if the given path exists.
255  */
256 bool
257 pathExists (const char *ppath)
258 {
259   struct stat s;
260
261   return stat (ppath, &s) == 0;
262 }
263
264 static hTab *_mainValues;
265
266 void
267 setMainValue (const char *pname, const char *pvalue)
268 {
269   assert(pname);
270   assert(pvalue);
271
272   shash_add (&_mainValues, pname, pvalue);
273 }
274
275 void
276 buildCmdLine2 (char *pbuffer, const char *pcmd, size_t len)
277 {
278   char *poutcmd;
279   assert(pbuffer && pcmd);
280   assert(_mainValues);
281
282   poutcmd = msprintf(_mainValues, pcmd);
283   strncpyz(pbuffer, poutcmd, len);
284 }
285
286 void
287 populateMainValues (const char **ppin)
288 {
289   _mainValues = populateStringHash(ppin);
290 }
291
292 /** Returns true if sz starts with the string given in key.
293  */
294 bool
295 startsWith (const char *sz, const char *key)
296 {
297   return !strncmp (sz, key, strlen (key));
298 }
299
300 /** Removes any newline characters from the string.  Not strictly the
301     same as perl's chomp.
302 */
303 void
304 chomp (char *sz)
305 {
306   char *nl;
307   while ((nl = strrchr (sz, '\n')))
308     *nl = '\0';
309 }
310
311 hTab *
312 getRuntimeVariables(void)
313 {
314   return _mainValues;
315 }
316
317
318 /* strncpy() with guaranteed NULL termination. */
319 char *strncpyz(char *dest, const char *src, size_t n)
320 {
321     assert(n > 0);
322     
323     // paranoia...
324     if (strlen(src) >= n)
325     {
326         fprintf(stderr, "strncpyz prevented buffer overrun!\n");
327     }
328     
329     strncpy(dest, src, n);
330     dest[n - 1] = 0;
331     return dest;
332 }
333
334 /* like strncat() with guaranteed NULL termination
335  * The passed size should be the size of the dest buffer, not the number of 
336  * bytes to copy.
337  */
338 char *strncatz(char *dest, const char *src, size_t n)
339 {
340     size_t maxToCopy;
341     size_t destLen = strlen(dest);
342     
343     assert(n > 0);
344     assert(n > destLen);
345     
346     maxToCopy = n - destLen - 1;
347     
348     // paranoia...
349     if (strlen(src) + destLen >= n)
350     {
351         fprintf(stderr, "strncatz prevented buffer overrun!\n");
352     }
353     
354     strncat(dest, src, maxToCopy);
355     dest[n - 1] = 0;
356     return dest;
357 }
358
359
360 #if defined(HAVE_VSNPRINTF) || defined(have_VSPRINTF)
361 size_t SDCCsnprintf(char *dst, size_t n, const char *fmt, ...)
362 {
363     va_list args;
364     int  len;
365     
366     va_start(args, fmt);
367     
368 # if defined(HAVE_VSNPRINTF)    
369     len = vsnprintf(dst, n, fmt, args);
370 # else
371     vsprintf(dst, fmt, args);
372     len = strlen(dst) + 1;
373 # endif    
374     
375     va_end(args);
376     
377     /* on some gnu systems, vsnprintf returns -1 if output is truncated.
378      * In the C99 spec, vsnprintf returns the number of characters that 
379      * would have been written, were space available.
380      */
381     if ((len < 0) || len >= n)
382     {
383         fprintf(stderr, "internal error: sprintf truncated.\n");
384     }
385     return len;
386 }
387
388 #endif