1 /* |
|
2 * This little program is used to parse the FreeType headers and |
|
3 * find the declaration of all public APIs. This is easy, because |
|
4 * they all look like the following: |
|
5 * |
|
6 * FT_EXPORT( return_type ) |
|
7 * function_name( function arguments ); |
|
8 * |
|
9 * You must pass the list of header files as arguments. Wildcards are |
|
10 * accepted if you are using GCC for compilation (and probably by |
|
11 * other compilers too). |
|
12 * |
|
13 * Author: David Turner, 2005, 2006, 2008, 2009, 2010 |
|
14 * |
|
15 * This code is explicitly placed into the public domain. |
|
16 * |
|
17 */ |
|
18 |
|
19 #include <stdio.h> |
|
20 #include <stdlib.h> |
|
21 #include <string.h> |
|
22 #include <ctype.h> |
|
23 |
|
24 #define PROGRAM_NAME "apinames" |
|
25 #define PROGRAM_VERSION "0.1" |
|
26 |
|
27 #define LINEBUFF_SIZE 1024 |
|
28 |
|
29 typedef enum OutputFormat_ |
|
30 { |
|
31 OUTPUT_LIST = 0, /* output the list of names, one per line */ |
|
32 OUTPUT_WINDOWS_DEF, /* output a Windows .DEF file for Visual C++ or Mingw */ |
|
33 OUTPUT_BORLAND_DEF, /* output a Windows .DEF file for Borland C++ */ |
|
34 OUTPUT_WATCOM_LBC /* output a Watcom Linker Command File */ |
|
35 |
|
36 } OutputFormat; |
|
37 |
|
38 |
|
39 static void |
|
40 panic( const char* message ) |
|
41 { |
|
42 fprintf( stderr, "PANIC: %s\n", message ); |
|
43 exit(2); |
|
44 } |
|
45 |
|
46 |
|
47 typedef struct NameRec_ |
|
48 { |
|
49 char* name; |
|
50 unsigned int hash; |
|
51 |
|
52 } NameRec, *Name; |
|
53 |
|
54 static Name the_names; |
|
55 static int num_names; |
|
56 static int max_names; |
|
57 |
|
58 static void |
|
59 names_add( const char* name, |
|
60 const char* end ) |
|
61 { |
|
62 int nn, len, h; |
|
63 Name nm; |
|
64 |
|
65 if ( end <= name ) |
|
66 return; |
|
67 |
|
68 /* compute hash value */ |
|
69 len = (int)(end - name); |
|
70 h = 0; |
|
71 for ( nn = 0; nn < len; nn++ ) |
|
72 h = h*33 + name[nn]; |
|
73 |
|
74 /* check for an pre-existing name */ |
|
75 for ( nn = 0; nn < num_names; nn++ ) |
|
76 { |
|
77 nm = the_names + nn; |
|
78 |
|
79 if ( (int)nm->hash == h && |
|
80 memcmp( name, nm->name, len ) == 0 && |
|
81 nm->name[len] == 0 ) |
|
82 return; |
|
83 } |
|
84 |
|
85 /* add new name */ |
|
86 if ( num_names >= max_names ) |
|
87 { |
|
88 max_names += (max_names >> 1) + 4; |
|
89 the_names = (NameRec*)realloc( the_names, sizeof(the_names[0])*max_names ); |
|
90 if ( the_names == NULL ) |
|
91 panic( "not enough memory" ); |
|
92 } |
|
93 nm = &the_names[num_names++]; |
|
94 |
|
95 nm->hash = h; |
|
96 nm->name = (char*)malloc( len+1 ); |
|
97 if ( nm->name == NULL ) |
|
98 panic( "not enough memory" ); |
|
99 |
|
100 memcpy( nm->name, name, len ); |
|
101 nm->name[len] = 0; |
|
102 } |
|
103 |
|
104 |
|
105 static int |
|
106 name_compare( const void* name1, |
|
107 const void* name2 ) |
|
108 { |
|
109 Name n1 = (Name)name1; |
|
110 Name n2 = (Name)name2; |
|
111 |
|
112 return strcmp( n1->name, n2->name ); |
|
113 } |
|
114 |
|
115 static void |
|
116 names_sort( void ) |
|
117 { |
|
118 qsort( the_names, (size_t)num_names, sizeof(the_names[0]), name_compare ); |
|
119 } |
|
120 |
|
121 |
|
122 static void |
|
123 names_dump( FILE* out, |
|
124 OutputFormat format, |
|
125 const char* dll_name ) |
|
126 { |
|
127 int nn; |
|
128 |
|
129 |
|
130 switch ( format ) |
|
131 { |
|
132 case OUTPUT_WINDOWS_DEF: |
|
133 if ( dll_name ) |
|
134 fprintf( out, "LIBRARY %s\n", dll_name ); |
|
135 |
|
136 fprintf( out, "DESCRIPTION FreeType 2 DLL\n" ); |
|
137 fprintf( out, "EXPORTS\n" ); |
|
138 for ( nn = 0; nn < num_names; nn++ ) |
|
139 fprintf( out, " %s\n", the_names[nn].name ); |
|
140 break; |
|
141 |
|
142 case OUTPUT_BORLAND_DEF: |
|
143 if ( dll_name ) |
|
144 fprintf( out, "LIBRARY %s\n", dll_name ); |
|
145 |
|
146 fprintf( out, "DESCRIPTION FreeType 2 DLL\n" ); |
|
147 fprintf( out, "EXPORTS\n" ); |
|
148 for ( nn = 0; nn < num_names; nn++ ) |
|
149 fprintf( out, " _%s\n", the_names[nn].name ); |
|
150 break; |
|
151 |
|
152 case OUTPUT_WATCOM_LBC: |
|
153 { |
|
154 /* we must omit the .dll suffix from the library name */ |
|
155 char temp[512]; |
|
156 const char* dot; |
|
157 |
|
158 |
|
159 if ( dll_name == NULL ) |
|
160 { |
|
161 fprintf( stderr, |
|
162 "you must provide a DLL name with the -d option!\n" ); |
|
163 exit( 4 ); |
|
164 } |
|
165 |
|
166 dot = strchr( dll_name, '.' ); |
|
167 if ( dot != NULL ) |
|
168 { |
|
169 int len = dot - dll_name; |
|
170 |
|
171 |
|
172 if ( len > (int)( sizeof( temp ) - 1 ) ) |
|
173 len = sizeof ( temp ) - 1; |
|
174 |
|
175 memcpy( temp, dll_name, len ); |
|
176 temp[len] = 0; |
|
177 |
|
178 dll_name = (const char*)temp; |
|
179 } |
|
180 |
|
181 for ( nn = 0; nn < num_names; nn++ ) |
|
182 fprintf( out, "++_%s.%s.%s\n", the_names[nn].name, dll_name, |
|
183 the_names[nn].name ); |
|
184 } |
|
185 break; |
|
186 |
|
187 default: /* LIST */ |
|
188 for ( nn = 0; nn < num_names; nn++ ) |
|
189 fprintf( out, "%s\n", the_names[nn].name ); |
|
190 } |
|
191 } |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 /* states of the line parser */ |
|
197 |
|
198 typedef enum State_ |
|
199 { |
|
200 STATE_START = 0, /* waiting for FT_EXPORT keyword and return type */ |
|
201 STATE_TYPE /* type was read, waiting for function name */ |
|
202 |
|
203 } State; |
|
204 |
|
205 static int |
|
206 read_header_file( FILE* file, int verbose ) |
|
207 { |
|
208 static char buff[ LINEBUFF_SIZE+1 ]; |
|
209 State state = STATE_START; |
|
210 |
|
211 while ( !feof( file ) ) |
|
212 { |
|
213 char* p; |
|
214 |
|
215 if ( !fgets( buff, LINEBUFF_SIZE, file ) ) |
|
216 break; |
|
217 |
|
218 p = buff; |
|
219 |
|
220 while ( *p && (*p == ' ' || *p == '\\') ) /* skip leading whitespace */ |
|
221 p++; |
|
222 |
|
223 if ( *p == '\n' || *p == '\r' ) /* skip empty lines */ |
|
224 continue; |
|
225 |
|
226 switch ( state ) |
|
227 { |
|
228 case STATE_START: |
|
229 { |
|
230 if ( memcmp( p, "FT_EXPORT(", 10 ) != 0 ) |
|
231 break; |
|
232 |
|
233 p += 10; |
|
234 for (;;) |
|
235 { |
|
236 if ( *p == 0 || *p == '\n' || *p == '\r' ) |
|
237 goto NextLine; |
|
238 |
|
239 if ( *p == ')' ) |
|
240 { |
|
241 p++; |
|
242 break; |
|
243 } |
|
244 |
|
245 p++; |
|
246 } |
|
247 |
|
248 state = STATE_TYPE; |
|
249 |
|
250 /* sometimes, the name is just after the FT_EXPORT(...), so |
|
251 * skip whitespace, and fall-through if we find an alphanumeric |
|
252 * character |
|
253 */ |
|
254 while ( *p == ' ' || *p == '\t' ) |
|
255 p++; |
|
256 |
|
257 if ( !isalpha(*p) ) |
|
258 break; |
|
259 } |
|
260 /* fall-through */ |
|
261 |
|
262 case STATE_TYPE: |
|
263 { |
|
264 char* name = p; |
|
265 |
|
266 while ( isalnum(*p) || *p == '_' ) |
|
267 p++; |
|
268 |
|
269 if ( p > name ) |
|
270 { |
|
271 if ( verbose ) |
|
272 fprintf( stderr, ">>> %.*s\n", (int)(p - name), name ); |
|
273 |
|
274 names_add( name, p ); |
|
275 } |
|
276 |
|
277 state = STATE_START; |
|
278 } |
|
279 break; |
|
280 |
|
281 default: |
|
282 ; |
|
283 } |
|
284 |
|
285 NextLine: |
|
286 ; |
|
287 } |
|
288 |
|
289 return 0; |
|
290 } |
|
291 |
|
292 |
|
293 static void |
|
294 usage( void ) |
|
295 { |
|
296 static const char* const format = |
|
297 "%s %s: extract FreeType API names from header files\n\n" |
|
298 "this program is used to extract the list of public FreeType API\n" |
|
299 "functions. It receives the list of header files as argument and\n" |
|
300 "generates a sorted list of unique identifiers\n\n" |
|
301 |
|
302 "usage: %s header1 [options] [header2 ...]\n\n" |
|
303 |
|
304 "options: - : parse the content of stdin, ignore arguments\n" |
|
305 " -v : verbose mode, output sent to standard error\n" |
|
306 " -oFILE : write output to FILE instead of standard output\n" |
|
307 " -dNAME : indicate DLL file name, 'freetype.dll' by default\n" |
|
308 " -w : output .DEF file for Visual C++ and Mingw\n" |
|
309 " -wB : output .DEF file for Borland C++\n" |
|
310 " -wW : output Watcom Linker Response File\n" |
|
311 "\n"; |
|
312 |
|
313 fprintf( stderr, |
|
314 format, |
|
315 PROGRAM_NAME, |
|
316 PROGRAM_VERSION, |
|
317 PROGRAM_NAME |
|
318 ); |
|
319 exit(1); |
|
320 } |
|
321 |
|
322 |
|
323 int main( int argc, const char* const* argv ) |
|
324 { |
|
325 int from_stdin = 0; |
|
326 int verbose = 0; |
|
327 OutputFormat format = OUTPUT_LIST; /* the default */ |
|
328 FILE* out = stdout; |
|
329 const char* library_name = NULL; |
|
330 |
|
331 if ( argc < 2 ) |
|
332 usage(); |
|
333 |
|
334 /* '-' used as a single argument means read source file from stdin */ |
|
335 while ( argc > 1 && argv[1][0] == '-' ) |
|
336 { |
|
337 const char* arg = argv[1]; |
|
338 |
|
339 switch ( arg[1] ) |
|
340 { |
|
341 case 'v': |
|
342 verbose = 1; |
|
343 break; |
|
344 |
|
345 case 'o': |
|
346 if ( arg[2] == 0 ) |
|
347 { |
|
348 if ( argc < 2 ) |
|
349 usage(); |
|
350 |
|
351 arg = argv[2]; |
|
352 argv++; |
|
353 argc--; |
|
354 } |
|
355 else |
|
356 arg += 2; |
|
357 |
|
358 out = fopen( arg, "wt" ); |
|
359 if ( out == NULL ) |
|
360 { |
|
361 fprintf( stderr, "could not open '%s' for writing\n", argv[2] ); |
|
362 exit(3); |
|
363 } |
|
364 break; |
|
365 |
|
366 case 'd': |
|
367 if ( arg[2] == 0 ) |
|
368 { |
|
369 if ( argc < 2 ) |
|
370 usage(); |
|
371 |
|
372 arg = argv[2]; |
|
373 argv++; |
|
374 argc--; |
|
375 } |
|
376 else |
|
377 arg += 2; |
|
378 |
|
379 library_name = arg; |
|
380 break; |
|
381 |
|
382 case 'w': |
|
383 format = OUTPUT_WINDOWS_DEF; |
|
384 switch ( arg[2] ) |
|
385 { |
|
386 case 'B': |
|
387 format = OUTPUT_BORLAND_DEF; |
|
388 break; |
|
389 |
|
390 case 'W': |
|
391 format = OUTPUT_WATCOM_LBC; |
|
392 break; |
|
393 |
|
394 case 0: |
|
395 break; |
|
396 |
|
397 default: |
|
398 usage(); |
|
399 } |
|
400 break; |
|
401 |
|
402 case 0: |
|
403 from_stdin = 1; |
|
404 break; |
|
405 |
|
406 default: |
|
407 usage(); |
|
408 } |
|
409 |
|
410 argc--; |
|
411 argv++; |
|
412 } |
|
413 |
|
414 if ( from_stdin ) |
|
415 { |
|
416 read_header_file( stdin, verbose ); |
|
417 } |
|
418 else |
|
419 { |
|
420 for ( --argc, argv++; argc > 0; argc--, argv++ ) |
|
421 { |
|
422 FILE* file = fopen( argv[0], "rb" ); |
|
423 |
|
424 if ( file == NULL ) |
|
425 fprintf( stderr, "unable to open '%s'\n", argv[0] ); |
|
426 else |
|
427 { |
|
428 if ( verbose ) |
|
429 fprintf( stderr, "opening '%s'\n", argv[0] ); |
|
430 |
|
431 read_header_file( file, verbose ); |
|
432 fclose( file ); |
|
433 } |
|
434 } |
|
435 } |
|
436 |
|
437 if ( num_names == 0 ) |
|
438 panic( "could not find exported functions !!\n" ); |
|
439 |
|
440 names_sort(); |
|
441 names_dump( out, format, library_name ); |
|
442 |
|
443 if ( out != stdout ) |
|
444 fclose( out ); |
|
445 |
|
446 return 0; |
|
447 } |
|