Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Useful functions for winegcc/winewrap |
| 3 | * |
| 4 | * Copyright 2000 Francois Gouget |
| 5 | * Copyright 2002 Dimitrie O. Paun |
| 6 | * Copyright 2003 Richard Cohen |
| 7 | * |
| 8 | * This library is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU Lesser General Public |
| 10 | * License as published by the Free Software Foundation; either |
| 11 | * version 2.1 of the License, or (at your option) any later version. |
| 12 | * |
| 13 | * This library is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | * Lesser General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU Lesser General Public |
| 19 | * License along with this library; if not, write to the Free Software |
Jonathan Ernst | 360a3f9 | 2006-05-18 14:49:52 +0200 | [diff] [blame] | 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 21 | */ |
| 22 | #include "config.h" |
| 23 | #include "wine/port.h" |
| 24 | |
| 25 | #include <stdio.h> |
| 26 | #include <string.h> |
| 27 | #include <stdarg.h> |
| 28 | #include <stdlib.h> |
| 29 | #include <errno.h> |
| 30 | |
| 31 | #include "utils.h" |
| 32 | |
| 33 | #if !defined(min) |
| 34 | # define min(x,y) (((x) < (y)) ? (x) : (y)) |
| 35 | #endif |
| 36 | |
| 37 | int verbose = 0; |
| 38 | |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 39 | void error(const char* s, ...) |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 40 | { |
| 41 | va_list ap; |
| 42 | |
| 43 | va_start(ap, s); |
Alexandre Julliard | 2d52cfa | 2004-03-09 04:49:42 +0000 | [diff] [blame] | 44 | fprintf(stderr, "winegcc: "); |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 45 | vfprintf(stderr, s, ap); |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 46 | va_end(ap); |
| 47 | exit(2); |
| 48 | } |
| 49 | |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 50 | void* xmalloc(size_t size) |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 51 | { |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 52 | void* p; |
| 53 | |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 54 | if ((p = malloc (size)) == NULL) |
Francois Gouget | dc3feef | 2007-10-18 17:11:47 +0200 | [diff] [blame] | 55 | error("Could not malloc %d bytes\n", size); |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 56 | |
| 57 | return p; |
| 58 | } |
| 59 | |
| 60 | void *xrealloc(void* p, size_t size) |
| 61 | { |
Alexandre Julliard | b30d92d | 2005-03-18 14:09:55 +0000 | [diff] [blame] | 62 | void* p2 = realloc (p, size); |
| 63 | if (size && !p2) |
Francois Gouget | dc3feef | 2007-10-18 17:11:47 +0200 | [diff] [blame] | 64 | error("Could not realloc %d bytes\n", size); |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 65 | |
| 66 | return p2; |
| 67 | } |
| 68 | |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 69 | int strendswith(const char* str, const char* end) |
| 70 | { |
| 71 | int l = strlen(str); |
| 72 | int m = strlen(end); |
| 73 | |
| 74 | return l >= m && strcmp(str + l - m, end) == 0; |
| 75 | } |
| 76 | |
| 77 | char* strmake(const char* fmt, ...) |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 78 | { |
| 79 | int n; |
| 80 | size_t size = 100; |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 81 | va_list ap; |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 82 | |
| 83 | while (1) |
| 84 | { |
Alexandre Julliard | 3a50349 | 2005-09-12 20:31:33 +0000 | [diff] [blame] | 85 | char *p = xmalloc (size); |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 86 | va_start(ap, fmt); |
| 87 | n = vsnprintf (p, size, fmt, ap); |
| 88 | va_end(ap); |
Alexandre Julliard | 3a50349 | 2005-09-12 20:31:33 +0000 | [diff] [blame] | 89 | if (n == -1) size *= 2; |
| 90 | else if ((size_t)n >= size) size = n + 1; |
| 91 | else return p; |
| 92 | free(p); |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 93 | } |
| 94 | } |
| 95 | |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 96 | strarray* strarray_alloc(void) |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 97 | { |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 98 | strarray* arr = xmalloc(sizeof(*arr)); |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 99 | arr->maximum = arr->size = 0; |
| 100 | arr->base = NULL; |
| 101 | return arr; |
| 102 | } |
| 103 | |
| 104 | void strarray_free(strarray* arr) |
| 105 | { |
| 106 | free(arr->base); |
| 107 | free(arr); |
| 108 | } |
| 109 | |
Daniel Marmier | f95be92 | 2003-10-15 03:35:54 +0000 | [diff] [blame] | 110 | void strarray_add(strarray* arr, const char* str) |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 111 | { |
| 112 | if (arr->size == arr->maximum) |
| 113 | { |
| 114 | arr->maximum += 10; |
| 115 | arr->base = xrealloc(arr->base, sizeof(*(arr->base)) * arr->maximum); |
| 116 | } |
| 117 | arr->base[arr->size++] = str; |
| 118 | } |
| 119 | |
Joris Huizer | 909425d | 2007-02-10 03:57:25 -0800 | [diff] [blame] | 120 | void strarray_del(strarray* arr, unsigned int i) |
Dimitrie O. Paun | 5f0796d | 2004-03-02 06:53:16 +0000 | [diff] [blame] | 121 | { |
Francois Gouget | dc3feef | 2007-10-18 17:11:47 +0200 | [diff] [blame] | 122 | if (i >= arr->size) error("Invalid index i=%d\n", i); |
Dimitrie O. Paun | 5f0796d | 2004-03-02 06:53:16 +0000 | [diff] [blame] | 123 | memmove(&arr->base[i], &arr->base[i + 1], (arr->size - i - 1) * sizeof(arr->base[0])); |
| 124 | arr->size--; |
| 125 | } |
| 126 | |
Dimitrie O. Paun | f41c2b2 | 2004-03-02 02:23:26 +0000 | [diff] [blame] | 127 | void strarray_addall(strarray* arr, const strarray* from) |
| 128 | { |
Joris Huizer | 909425d | 2007-02-10 03:57:25 -0800 | [diff] [blame] | 129 | unsigned int i; |
Dimitrie O. Paun | f41c2b2 | 2004-03-02 02:23:26 +0000 | [diff] [blame] | 130 | |
| 131 | for (i = 0; i < from->size; i++) |
| 132 | strarray_add(arr, from->base[i]); |
| 133 | } |
| 134 | |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 135 | strarray* strarray_dup(const strarray* arr) |
| 136 | { |
| 137 | strarray* dup = strarray_alloc(); |
Joris Huizer | 909425d | 2007-02-10 03:57:25 -0800 | [diff] [blame] | 138 | unsigned int i; |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 139 | |
| 140 | for (i = 0; i < arr->size; i++) |
| 141 | strarray_add(dup, arr->base[i]); |
| 142 | |
| 143 | return dup; |
| 144 | } |
| 145 | |
Dimitrie O. Paun | f41c2b2 | 2004-03-02 02:23:26 +0000 | [diff] [blame] | 146 | strarray* strarray_fromstring(const char* str, const char* delim) |
| 147 | { |
| 148 | strarray* arr = strarray_alloc(); |
| 149 | char* buf = strdup(str); |
| 150 | const char* tok; |
| 151 | |
| 152 | for(tok = strtok(buf, delim); tok; tok = strtok(0, delim)) |
| 153 | strarray_add(arr, strdup(tok)); |
| 154 | |
| 155 | free(buf); |
| 156 | return arr; |
| 157 | } |
| 158 | |
| 159 | char* strarray_tostring(const strarray* arr, const char* sep) |
| 160 | { |
| 161 | char *str, *newstr; |
Joris Huizer | 909425d | 2007-02-10 03:57:25 -0800 | [diff] [blame] | 162 | unsigned int i; |
Dimitrie O. Paun | f41c2b2 | 2004-03-02 02:23:26 +0000 | [diff] [blame] | 163 | |
| 164 | str = strmake("%s", arr->base[0]); |
| 165 | for (i = 1; i < arr->size; i++) |
| 166 | { |
| 167 | newstr = strmake("%s%s%s", str, sep, arr->base[i]); |
| 168 | free(str); |
| 169 | str = newstr; |
| 170 | } |
| 171 | |
| 172 | return str; |
| 173 | } |
| 174 | |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 175 | char* get_basename(const char* file) |
| 176 | { |
| 177 | const char* name; |
| 178 | char *base_name, *p; |
| 179 | |
| 180 | if ((name = strrchr(file, '/'))) name++; |
| 181 | else name = file; |
| 182 | |
| 183 | base_name = strdup(name); |
| 184 | if ((p = strrchr(base_name, '.'))) *p = 0; |
| 185 | |
| 186 | return base_name; |
| 187 | } |
| 188 | |
Dimitrie O. Paun | 2ab690b | 2004-03-03 20:11:20 +0000 | [diff] [blame] | 189 | void create_file(const char* name, int mode, const char* fmt, ...) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 190 | { |
| 191 | va_list ap; |
| 192 | FILE *file; |
| 193 | |
| 194 | if (verbose) printf("Creating file %s\n", name); |
| 195 | va_start(ap, fmt); |
| 196 | if ( !(file = fopen(name, "w")) ) |
Francois Gouget | dc3feef | 2007-10-18 17:11:47 +0200 | [diff] [blame] | 197 | error("Unable to open %s for writing\n", name); |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 198 | vfprintf(file, fmt, ap); |
| 199 | va_end(ap); |
| 200 | fclose(file); |
Alexandre Julliard | 9578430 | 2004-03-10 01:53:57 +0000 | [diff] [blame] | 201 | chmod(name, mode); |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 202 | } |
| 203 | |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 204 | file_type get_file_type(const char* filename) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 205 | { |
| 206 | /* see tools/winebuild/res32.c: check_header for details */ |
| 207 | static const char res_sig[] = { 0,0,0,0, 32,0,0,0, 0xff,0xff, 0,0, 0xff,0xff, 0,0, 0,0,0,0, 0,0, 0,0, 0,0,0,0, 0,0,0,0 }; |
| 208 | char buf[sizeof(res_sig)]; |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 209 | int fd, cnt; |
| 210 | |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 211 | fd = open( filename, O_RDONLY ); |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 212 | if (fd == -1) return file_na; |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 213 | cnt = read(fd, buf, sizeof(buf)); |
| 214 | close( fd ); |
| 215 | if (cnt == -1) return file_na; |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 216 | |
| 217 | if (cnt == sizeof(res_sig) && !memcmp(buf, res_sig, sizeof(res_sig))) return file_res; |
| 218 | if (strendswith(filename, ".o")) return file_obj; |
| 219 | if (strendswith(filename, ".a")) return file_arh; |
| 220 | if (strendswith(filename, ".res")) return file_res; |
| 221 | if (strendswith(filename, ".so")) return file_so; |
Pierre d'Herbemont | ec132fe | 2004-03-15 20:06:06 +0000 | [diff] [blame] | 222 | if (strendswith(filename, ".dylib")) return file_so; |
Dimitrie O. Paun | b613ee7 | 2004-03-23 00:14:54 +0000 | [diff] [blame] | 223 | if (strendswith(filename, ".def")) return file_def; |
| 224 | if (strendswith(filename, ".spec")) return file_spec; |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 225 | if (strendswith(filename, ".rc")) return file_rc; |
| 226 | |
| 227 | return file_other; |
| 228 | } |
| 229 | |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 230 | static char* try_lib_path(const char* dir, const char* pre, |
| 231 | const char* library, const char* ext, |
| 232 | file_type expected_type) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 233 | { |
| 234 | char *fullname; |
| 235 | file_type type; |
| 236 | |
Alexandre Julliard | 870d490 | 2006-03-16 16:28:04 +0100 | [diff] [blame] | 237 | /* first try a subdir named from the library we are looking for */ |
| 238 | fullname = strmake("%s/%s/%s%s%s", dir, library, pre, library, ext); |
| 239 | if (verbose > 1) fprintf(stderr, "Try %s...", fullname); |
| 240 | type = get_file_type(fullname); |
| 241 | if (verbose > 1) fprintf(stderr, type == expected_type ? "FOUND!\n" : "no\n"); |
| 242 | if (type == expected_type) return fullname; |
| 243 | free( fullname ); |
| 244 | |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 245 | fullname = strmake("%s/%s%s%s", dir, pre, library, ext); |
| 246 | if (verbose > 1) fprintf(stderr, "Try %s...", fullname); |
| 247 | type = get_file_type(fullname); |
| 248 | if (verbose > 1) fprintf(stderr, type == expected_type ? "FOUND!\n" : "no\n"); |
| 249 | if (type == expected_type) return fullname; |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 250 | free( fullname ); |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 251 | return 0; |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 252 | } |
| 253 | |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 254 | static file_type guess_lib_type(const char* dir, const char* library, char** file) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 255 | { |
| 256 | /* Unix shared object */ |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 257 | if ((*file = try_lib_path(dir, "lib", library, ".so", file_so))) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 258 | return file_so; |
Pierre d'Herbemont | ec132fe | 2004-03-15 20:06:06 +0000 | [diff] [blame] | 259 | |
| 260 | /* Mach-O (Darwin/Mac OS X) Dynamic Library behaves mostly like .so */ |
| 261 | if ((*file = try_lib_path(dir, "lib", library, ".dylib", file_so))) |
| 262 | return file_so; |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 263 | |
| 264 | /* Windows DLL */ |
Dimitrie O. Paun | b613ee7 | 2004-03-23 00:14:54 +0000 | [diff] [blame] | 265 | if ((*file = try_lib_path(dir, "lib", library, ".def", file_def))) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 266 | return file_dll; |
Dimitrie O. Paun | b613ee7 | 2004-03-23 00:14:54 +0000 | [diff] [blame] | 267 | if ((*file = try_lib_path(dir, "", library, ".def", file_def))) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 268 | return file_dll; |
| 269 | |
| 270 | /* Unix static archives */ |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 271 | if ((*file = try_lib_path(dir, "lib", library, ".a", file_arh))) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 272 | return file_arh; |
| 273 | |
| 274 | return file_na; |
| 275 | } |
| 276 | |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 277 | file_type get_lib_type(strarray* path, const char* library, char** file) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 278 | { |
Joris Huizer | 909425d | 2007-02-10 03:57:25 -0800 | [diff] [blame] | 279 | unsigned int i; |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 280 | |
| 281 | for (i = 0; i < path->size; i++) |
| 282 | { |
Dimitrie O. Paun | 006ec80 | 2004-02-26 05:28:35 +0000 | [diff] [blame] | 283 | file_type type = guess_lib_type(path->base[i], library, file); |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 284 | if (type != file_na) return type; |
| 285 | } |
| 286 | return file_na; |
| 287 | } |
| 288 | |
Alexandre Julliard | 866f52b | 2005-08-09 20:47:18 +0000 | [diff] [blame] | 289 | void spawn(const strarray* prefix, const strarray* args, int ignore_errors) |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 290 | { |
Joris Huizer | 909425d | 2007-02-10 03:57:25 -0800 | [diff] [blame] | 291 | unsigned int i; |
| 292 | int status; |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 293 | strarray* arr = strarray_dup(args); |
Alexandre Julliard | acead48 | 2004-03-09 03:44:52 +0000 | [diff] [blame] | 294 | const char** argv; |
Dimitrie O. Paun | fb1ae96 | 2004-03-09 01:34:00 +0000 | [diff] [blame] | 295 | char* prog = 0; |
| 296 | |
Alexandre Julliard | acead48 | 2004-03-09 03:44:52 +0000 | [diff] [blame] | 297 | strarray_add(arr, NULL); |
| 298 | argv = arr->base; |
| 299 | |
Dimitrie O. Paun | fb1ae96 | 2004-03-09 01:34:00 +0000 | [diff] [blame] | 300 | if (prefix) |
| 301 | { |
Alexandre Julliard | 2d52cfa | 2004-03-09 04:49:42 +0000 | [diff] [blame] | 302 | for (i = 0; i < prefix->size; i++) |
| 303 | { |
| 304 | const char* p; |
| 305 | struct stat st; |
Dimitrie O. Paun | fb1ae96 | 2004-03-09 01:34:00 +0000 | [diff] [blame] | 306 | |
Alexandre Julliard | 2d52cfa | 2004-03-09 04:49:42 +0000 | [diff] [blame] | 307 | if (!(p = strrchr(argv[0], '/'))) p = argv[0]; |
| 308 | free( prog ); |
| 309 | prog = strmake("%s/%s", prefix->base[i], p); |
| 310 | if (stat(prog, &st) == 0) |
| 311 | { |
| 312 | if ((st.st_mode & S_IFREG) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) |
| 313 | { |
| 314 | argv[0] = prog; |
| 315 | break; |
| 316 | } |
| 317 | } |
| 318 | } |
Dimitrie O. Paun | fb1ae96 | 2004-03-09 01:34:00 +0000 | [diff] [blame] | 319 | } |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 320 | |
| 321 | if (verbose) |
| 322 | { |
| 323 | for(i = 0; argv[i]; i++) printf("%s ", argv[i]); |
| 324 | printf("\n"); |
| 325 | } |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 326 | |
Alexandre Julliard | 866f52b | 2005-08-09 20:47:18 +0000 | [diff] [blame] | 327 | if ((status = spawnvp( _P_WAIT, argv[0], argv)) && !ignore_errors) |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 328 | { |
Francois Gouget | dc3feef | 2007-10-18 17:11:47 +0200 | [diff] [blame] | 329 | if (status > 0) error("%s failed\n", argv[0]); |
Alexandre Julliard | 2d52cfa | 2004-03-09 04:49:42 +0000 | [diff] [blame] | 330 | else perror("winegcc"); |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 331 | exit(3); |
| 332 | } |
| 333 | |
Dimitrie O. Paun | fb1ae96 | 2004-03-09 01:34:00 +0000 | [diff] [blame] | 334 | free(prog); |
Dimitrie O. Paun | e11108c | 2004-02-24 01:00:53 +0000 | [diff] [blame] | 335 | strarray_free(arr); |
Richard Cohen | 2e6eed6 | 2003-09-11 22:16:33 +0000 | [diff] [blame] | 336 | } |