added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / tools / archtools / archtool.c
blob00e06f31f6f14646dc815078c5ad71bf02ccb432
1 /*
2 Copyright © 1995-2001, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <stdio.h>
7 #include <stdarg.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <unistd.h>
12 #include <time.h>
13 #include <sys/stat.h>
15 /* Prototypes of reusable functions */
17 /* Read in one line from file
18 - mallocs needed space */
19 char *get_line(FILE *file);
21 /* Search for '#Keyword' in string
22 - mallocs needed space
23 - returns 'Keyword' */
24 char *keyword(char *line);
26 /* Searches line for whitespace separated words
27 - outarray = pointer to array of found words (strings)
28 - mallocs needed space
29 - frees previously allocated space before !
30 - for first use pass
31 char **words=NULL; get_words(line,&words) */
32 int get_words(char *line, char ***outarray);
34 /* Converts string to lower case */
35 void strlower(char *string);
37 /* Compares two files
38 returns 0 for equal files,
39 1 for different files,
40 -1 if file1 is not present,
41 -2 if file2 is not present */
42 int filesdiffer( char *file1, char *file2 );
44 /* Compares old and new file,
45 if old file is not found or different from new
46 new file will be renamed to name of old
47 old will be named old.bak */
48 void moveifchanged(char *old, char *new);
50 /* Parses file or lib.conf if file==NULL
51 puts values in struct libconf
52 returns 0 on success */
53 struct libconf * parse_libconf(char *file);
56 char *get_line(FILE *fd)
58 int count,len;
59 char *line;
60 char buffer;
62 len = 0;
65 count = fread(&buffer,1,1,fd);
66 len += count;
67 } while(count!=0 && buffer!='\n');
68 if(len==0 && count==0)
69 return NULL;
70 fseek( fd, -len, SEEK_CUR );
71 line = malloc( (len+1) * sizeof(char) );
72 fread (line,1,len,fd);
73 line[len]=0;
74 len--;
75 while(isspace(line[len])&& len>=0)
77 line[len] = 0;
78 len--;
81 return line;
84 char *keyword(char *line)
86 char *key;
87 int len;
89 if(line[0]=='#')
91 len = 1;
92 while(line[len] && !isspace(line[len]))
93 len++;
94 key = malloc( len * sizeof(char) );
95 strncpy( key, &line[1], len-1 );
96 key[len-1] = 0;
98 return key;
100 else
101 return NULL;
104 int get_words(char *line, char ***outarray)
106 char **array;
107 char *word;
108 int num,len;
110 if(!outarray||!line)
112 fprintf( stderr, "Passed invalid NULL pointer to get_words()!\n" );
113 exit(-1);
115 array = *outarray;
116 if( array )
118 while( *array )
120 free(*array);
121 array++;
123 free(*outarray);
125 array = NULL;
126 num = 0;
127 word = line;
128 while(*word!=0)
130 while( *word && isspace(*word) )
131 word++;
132 len = 0;
133 while( word[len] && !isspace(word[len]) )
134 len++;
135 if(len)
137 num++;
138 array = realloc( array, (num+1) * sizeof(char *) );
139 array[num-1] = malloc( (len+1) * sizeof(char) );
140 strncpy( array[num-1], word, len );
141 array[num-1][len] = 0;
142 word = &word[len];
145 if (array)
146 array[num] = NULL;
148 *outarray = array;
150 return num;
153 void strlower(char *string)
155 while(*string)
157 *string = tolower(*string);
158 string++;
162 int filesdiffer( char *file1, char *file2 )
164 FILE *fd1, *fd2;
165 int cnt1,cnt2;
166 char buffer1[1], buffer2[1];
167 int retval = 0;
169 fd1 = fopen(file1,"rb");
170 if(!fd1)
171 return -1;
172 fd2 = fopen(file2,"rb");
173 if(!fd2)
174 return -2;
177 cnt1 = fread(buffer1,1,1,fd1);
178 cnt2 = fread(buffer2,1,1,fd2);
179 } while( cnt1 && cnt2 && buffer1[0]==buffer2[0] );
180 if( buffer1[0]!=buffer2[0] || cnt1 != cnt2 )
181 retval = 1;
183 fclose(fd1);
184 fclose(fd2);
186 return retval;
189 void moveifchanged(char *old, char *new)
191 struct stat *statold, *statnew;
192 char *bakname;
194 statold = calloc(1, sizeof(struct stat) );
195 statnew = calloc(1, sizeof(struct stat) );
196 if(stat(old,statold))
198 /* Couldn't stat old file -- assume non-existent */
199 rename(new,old);
200 return;
202 if(stat(new,statnew))
204 /* Couldn't stat new file -- this shouldn't happen */
205 fprintf( stderr, "Couldn't stat() file %s!\n", new );
206 exit(-1);
208 bakname = malloc( (strlen(old)+5) * sizeof(char) );
209 sprintf( bakname, "%s.bak", old );
210 if(statold->st_size != statnew->st_size)
212 rename(old,bakname);
213 rename(new,old);
215 else if( filesdiffer(old,new) )
217 rename(old,bakname);
218 rename(new,old);
220 else
221 remove(new);
222 free(bakname);
225 enum libtype
227 t_device = 1,
228 t_library = 2,
229 t_resource = 3,
230 t_hidd = 4,
231 t_gadget = 5,
232 t_image = 6,
233 t_class = 7,
234 t_datatype = 8
237 enum liboption
239 o_noexpunge = 1,
240 o_rom = 2,
241 o_unique = 4,
242 o_nolibheader = 8,
243 o_hasrt = 16
246 struct libconf
248 char *libname;
249 char *basename;
250 char *libbase;
251 char *libbasetype;
252 char *libbasetypeptr;
253 int version, revision;
254 char *copyright;
255 char *define;
256 int type;
257 int option;
261 struct libconf * parse_libconf(char *file)
263 FILE *fd;
264 int num, len, i;
265 char *line, *word;
266 char **words = NULL;
267 struct libconf * lc = calloc (1, sizeof (struct libconf));
269 fd = fopen((file?file:"lib.conf"),"rb");
270 if(!fd)
272 fprintf( stderr, "Couldn't open %s!\n", (file?file:"lib.conf") );
273 return NULL;
275 while( (line = get_line(fd)) )
277 num = get_words(line,&words);
278 if( num > 1 )
280 if( strcmp(words[0],"name")==0 )
282 strlower(words[1]);
283 len = strlen(words[1]);
284 lc->libname = strdup(words[1]);
286 else if( strcmp(words[0],"libname")==0 )
288 free(lc->libname);
289 lc->libname = strdup(words[1]);
291 else if( strcmp(words[0],"basename")==0 )
293 len = strlen(words[1]);
294 free(lc->basename);
295 lc->basename = strdup(words[1]);
297 else if( strcmp(words[0],"libbase")==0 )
299 len = strlen(words[1]);
300 free(lc->libbase);
301 lc->libbase = strdup(words[1]);
303 else if( strcmp(words[0],"libbasetype")==0 )
305 len = 0;
306 for( i=1 ; i<num ; i++ )
307 len += strlen(words[i]);
308 len += num-1;
309 free(lc->libbasetype);
310 lc->libbasetype = malloc( len * sizeof(char) );
311 strcpy(lc->libbasetype, words[1] );
312 for( i=2 ; i<num ; i++ )
314 strcat( lc->libbasetype, " " );
315 strcat( lc->libbasetype, words[i] );
318 else if( strcmp(words[0],"libbasetypeptr")==0 )
320 len = 0;
321 for( i=1 ; i<num ; i++ )
322 len += strlen(words[i]);
323 len += num-1;
324 free(lc->libbasetypeptr);
325 lc->libbasetypeptr = malloc( len * sizeof(char) );
326 strcpy(lc->libbasetypeptr, words[1]);
327 for( i=2 ; i<num ; i++ )
329 strcat( lc->libbasetypeptr, " " );
330 strcat( lc->libbasetypeptr, words[i] );
333 else if( strcmp(words[0],"version")==0 )
335 i = 0;
336 while( words[1][i] && words[1][i]!='.' )
337 i++;
338 lc->revision = (words[1][i]==0?0:atoi(&words[1][i+1]));
339 if( i==0 )
341 lc->version = 0;
343 else
345 words[1][i] = 0;
346 lc->version = atoi(words[1]);
349 else if( strcmp(words[0],"copyright")==0 )
351 word = &line[9];
352 while( *word && isspace(*word) )
353 word++;
354 free(lc->copyright);
355 lc->copyright = strdup(word);
357 else if( strcmp(words[0],"define")==0 )
359 free(lc->define);
360 lc->define = strdup(words[1]);
362 else if( strcmp(words[0],"type")==0 )
364 if( strcmp(words[1],"device")==0 )
365 lc->type = t_device;
366 else if( strcmp(words[1],"library")==0 )
367 lc->type = t_library;
368 else if( strcmp(words[1],"resource")==0 )
369 lc->type = t_resource;
370 else if( strcmp(words[1],"hidd")==0 )
371 lc->type = t_hidd;
372 else if( strcmp(words[1],"gadget")==0 )
373 lc->type = t_gadget;
374 else if( strcmp(words[1],"image")==0 )
375 lc->type = t_image;
376 else if( strcmp(words[1],"class")==0 )
377 lc->type = t_class;
378 else if( strcmp(words[1],"datatype")==0 )
379 lc->type = t_datatype;
381 else if( strcmp(words[0],"options")==0 || strcmp(words[0],"option")==0 )
383 for( i=1 ; i<num ; i++ )
385 if( strcmp(words[i],"noexpunge")==0 )
386 lc->option |= o_noexpunge;
387 else if( strcmp(words[i],"rom")==0 )
388 lc->option |= o_rom;
389 else if( strcmp(words[i],"unique")==0 )
390 lc->option |= o_unique;
391 else if( strcmp(words[i],"nolibheader")==0 )
392 lc->option |= o_nolibheader;
393 else if( strcmp(words[i],"hasrt")==0 )
394 lc->option |= o_hasrt;
398 free(line);
401 if( lc->libname == NULL )
403 fprintf( stderr, "Missing field \"name\" in lib.conf!\n" );
404 return NULL;
406 if( lc->basename == NULL )
408 lc->basename = strdup(lc->libname);
409 lc->basename[0] = toupper(lc->basename[0]);
411 if( lc->libbase == NULL )
413 lc->libbase = malloc( (strlen(lc->basename)+5) * sizeof(char) );
414 sprintf( lc->libbase, "%sBase", lc->basename );
416 if( lc->libbasetype == NULL )
418 lc->libbasetype = malloc( (strlen(lc->libbase)+8) * sizeof(char) );
419 sprintf( lc->libbasetype, "struct %s", lc->libbase );
421 if( lc->libbasetypeptr == NULL )
423 lc->libbasetypeptr = malloc( (strlen(lc->libbasetype)+3) * sizeof(char) );
424 sprintf( lc->libbasetypeptr, "%s *", lc->libbasetype );
427 if( lc->define == NULL )
428 lc->define = strdup( "_LIBDEFS_H" );
429 if( lc->type == 0 )
430 lc->type = t_library;
432 return lc;
437 # Create libdefs.h from a file lib.conf. lib.conf may contain these options:
439 # name <string> - Init the various fields with reasonable defaults. If
440 # <string> is XXX, then this is the result:
442 # libname xxx
443 # basename Xxx
444 # libbase XxxBase
445 # libbasetype XxxBase
446 # libbasetypeptr XxxBase *
448 # Variables will only be changed if they have not yet been
449 # specified.
451 # libname <string> - Set libname to <string>. This is the name of the
452 # library (ie. you can open it with <string>.library).
453 # It will show up in the version string, too.
454 # basename <string> - Set basename to <string>. The basename is used in
455 # the AROS-LHx macros in the location part (last parameter)
456 # and to specify defaults for libbase and libbasetype
457 # in case they have no value yet. If <string> is xXx, then
458 # libbase will become xXxBase and libbasetype will become
459 # xXxBase.
460 # libbase <string> - Defines the name of the library base (ie. SysBase,
461 # DOSBase, IconBase, etc). If libbasetype is not set, then
462 # it is set to <string>, too.
463 # libbasetype <string> - The type of libbase (with struct), ie.
464 # struct ExecBase, struct DosLibrary, struct IconBase, etc).
465 # libbasetypeptr <string> - Type of a pointer to the libbase. (eg.
466 # struct ExecBase *).
467 # version <version>.<revision> - Specifies the version and revision of the
468 # library. 41.0103 means version 41 and revision 103.
469 # copyright <string> - Copyright string.
470 # define <string> - The define to use to protect the resulting file
471 # against double inclusion (ie. #ifndef <string>...)
472 # The default is _LIBDEFS_H.
473 # type <string> - What kind of library is this ? Valid values
474 # for <string> are: device, library, resource and hidd.
475 # option <string>... - Specify an option. Valid values for <string> are:
477 # noexpunge - Once the lib/dev is loaded, it can't be
478 # removed from memory. Be careful with this
479 # option.
480 # rom - For ROM based libraries. Implies noexpunge and
481 # unique.
482 # unique - Generate unique names for all external
483 # symbols.
484 # nolibheader - We don't want to use the LibHeader prefixed
485 # functions in the function table.
486 # hasrt - This library has resource tracking.
488 # You can specify more than one option in a config file and
489 # more than one option per option line. Separate options by
490 # space.
493 int genlibdefs(int argc, char **argv)
495 struct libconf *lc;
496 FILE *fd;
497 char *date, *filename, *conffile;
498 struct tm *tm;
499 time_t t;
500 int i;
502 filename = "libdefs.h";
503 conffile = "lib.conf";
504 for (i=2; i<=argc; i++)
506 if (strcmp(argv[i-1],"-o")==0)
508 filename = argv[i];
509 i++;
511 else
512 conffile = argv[i-1];
514 time(&t);
515 tm = localtime(&t);
516 date = malloc( 11 * sizeof(char) );
517 sprintf( date, "%02d.%02d.%4d", tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900 );
518 fd = fopen(filename,"w");
519 if(!fd)
521 fprintf( stderr, "Couldn't open file %s!\n", "libdefs.h.new" );
522 return -1;
524 if(!(lc = parse_libconf(conffile)) )
525 return(-1);
526 if( lc->copyright == NULL )
528 lc->copyright = strdup("");
530 fprintf( fd, "/* *** Automatic generated file. Do not edit *** */\n\n");
531 fprintf( fd, "#ifndef %s\n#define %s\n", lc->define, lc->define );
533 if (lc->type == t_library)
535 fprintf( fd, "#define NAME_STRING \"%s.library\"\n", lc->libname );
536 fprintf( fd, "#define NT_TYPE NT_LIBRARY\n" );
538 else if (lc->type == t_device)
540 fprintf( fd, "#define NAME_STRING \"%s.device\"\n", lc->libname );
541 fprintf( fd, "#define NT_TYPE NT_DEVICE\n" );
543 else if (lc->type == t_resource)
545 fprintf( fd, "#define NAME_STRING \"%s.resource\"\n", lc->libname );
546 fprintf( fd, "#define NT_TYPE NT_RESOURCE\n" );
548 else if (lc->type == t_hidd)
550 fprintf( fd, "#define NAME_STRING \"%s.hidd\"\n", lc->libname );
551 fprintf( fd, "#define NT_TYPE NT_HIDD\n" );
553 else if (lc->type == t_gadget)
555 fprintf( fd, "#define NAME_STRING \"%s.gadget\"\n", lc->libname );
556 fprintf( fd, "#define NT_TYPE NT_LIBRARY\n" );
558 else if (lc->type == t_image)
560 fprintf( fd, "#define NAME_STRING \"%s.image\"\n", lc->libname );
561 fprintf( fd, "#define NT_TYPE NT_LIBRARY\n" );
563 else if (lc->type == t_class)
565 fprintf( fd, "#define NAME_STRING \"%s.class\"\n", lc->libname );
566 fprintf( fd, "#define NT_TYPE NT_LIBRARY\n" );
568 else if (lc->type == t_datatype)
570 fprintf( fd, "#define NAME_STRING \"%s.datatype\"\n", lc->libname );
571 fprintf( fd, "#define NT_TYPE NT_LIBRARY\n" );
574 if (lc->option & o_rom)
575 lc->option |= o_noexpunge;
577 if (lc->option & o_noexpunge)
578 fprintf( fd, "#define NOEXPUNGE\n" );
579 if (lc->option & o_rom)
580 fprintf( fd, "#define ROMBASED\n" );
581 if (lc->option & o_nolibheader)
582 fprintf( fd, "#define NOLIBHEADER\n" );
584 if (lc->option & o_rom || lc->option & o_unique)
586 fprintf( fd, "#define LC_UNIQUE_PREFIX %s\n", lc->basename );
587 fprintf( fd, "#define LC_BUILDNAME(n) %s ## n\n", lc->basename );
589 else
591 fprintf( fd, "#define LC_BUILDNAME(n) n\n" );
594 fprintf( fd, "#define LIBBASE %s\n", lc->libbase );
595 fprintf( fd, "#define LIBBASETYPE %s\n", lc->libbasetype );
596 fprintf( fd, "#define LIBBASETYPEPTR %s\n", lc->libbasetypeptr );
597 fprintf( fd, "#define VERSION_NUMBER %d\n", lc->version );
598 fprintf( fd, "#define REVISION_NUMBER %d\n", lc->revision );
599 fprintf( fd, "#define BASENAME %s\n", lc->basename );
600 fprintf( fd, "#define BASENAME_STRING \"%s\"\n", lc->basename );
601 fprintf( fd, "#define VERSION_STRING \"$VER: %s %d.%d (%s)\\r\\n\"\n", lc->libname, lc->version, lc->revision , date );
602 fprintf( fd, "#define LIBEND %s_end\n", lc->basename );
603 fprintf( fd, "#define LIBFUNCTABLE %s_functable\n", lc->basename );
604 fprintf( fd, "#define COPYRIGHT_STRING \"%s\"\n", lc->copyright );
605 fprintf( fd, "#endif /* %s */\n", lc->define );
607 fclose(fd);
609 return 0;
612 void emit(FILE *out, struct libconf *lc, char **names, int number)
614 int i;
616 fprintf( out, "/*\n" );
617 fprintf( out, " Copyright © 1995-1998, The AROS Development Team. All rights reserved.\n" );
618 fprintf( out, " *** Automatic generated file. Do not edit ***\n" );
619 fprintf( out, " Desc: Function table for %s\n", lc->basename );
620 fprintf( out, " Lang: english\n" );
621 fprintf( out, "*/\n" );
622 fprintf( out, "#ifndef LIBCORE_COMPILER_H\n" );
623 fprintf( out, "# include <libcore/compiler.h>\n" );
624 fprintf( out, "#endif\n" );
625 fprintf( out, "#ifndef NULL\n" );
626 fprintf( out, "#define NULL ((void *)0)\n" );
627 fprintf( out, "#endif\n\n" );
628 fprintf( out, "#ifndef LC_LIBDEFS_FILE\n" );
629 fprintf( out, "#define LC_LIBDEFS_FILE \"libdefs.h\"\n" );
630 fprintf( out, "#endif\n" );
631 fprintf( out, "#include LC_LIBDEFS_FILE\n" );
632 if(lc->option & o_nolibheader)
634 fprintf( out, "extern void AROS_SLIB_ENTRY(open,BASENAME) (void);\n" );
635 fprintf( out, "extern void AROS_SLIB_ENTRY(close,BASENAME) (void);\n" );
636 fprintf( out, "extern void AROS_SLIB_ENTRY(expunge,BASENAME) (void);\n" );
637 fprintf( out, "extern void AROS_SLIB_ENTRY(null,BASENAME) (void);\n" );
639 else
641 fprintf( out, "extern void AROS_SLIB_ENTRY(LC_BUILDNAME(OpenLib),LibHeader) (void);\n" );
642 fprintf( out, "extern void AROS_SLIB_ENTRY(LC_BUILDNAME(CloseLib),LibHeader) (void);\n" );
643 fprintf( out, "extern void AROS_SLIB_ENTRY(LC_BUILDNAME(ExpungeLib),LibHeader) (void);\n" );
644 fprintf( out, "extern void AROS_SLIB_ENTRY(LC_BUILDNAME(ExtFuncLib),LibHeader) (void);\n" );
646 for( i = 0 ; i < number-4 ; i++ )
648 if(names[i])
649 fprintf( out, "extern void AROS_SLIB_ENTRY(%s,BASENAME) (void);\n", names[i] );
651 fprintf( out, "\nvoid *const LIBFUNCTABLE[]=\n{\n" );
652 if(lc->option & o_nolibheader)
654 fprintf( out, " AROS_SLIB_ENTRY(open, BASENAME),\n" );
655 fprintf( out, " AROS_SLIB_ENTRY(close, BASENAME),\n" );
656 fprintf( out, " AROS_SLIB_ENTRY(expunge, BASENAME),\n" );
657 fprintf( out, " AROS_SLIB_ENTRY(null, BASENAME),\n" );
659 else
661 fprintf( out, " AROS_SLIB_ENTRY(LC_BUILDNAME(OpenLib),LibHeader),\n" );
662 fprintf( out, " AROS_SLIB_ENTRY(LC_BUILDNAME(CloseLib),LibHeader),\n" );
663 fprintf( out, " AROS_SLIB_ENTRY(LC_BUILDNAME(ExpungeLib),LibHeader),\n" );
664 fprintf( out, " AROS_SLIB_ENTRY(LC_BUILDNAME(ExtFuncLib),LibHeader),\n" );
666 for( i = 0 ; i < number-4 ; i++ )
668 if(names[i])
669 fprintf( out, " AROS_SLIB_ENTRY(%s,BASENAME), /* %d */\n", names[i], i+5 );
670 else
671 fprintf( out, " NULL, /* %d */\n", i+5 );
673 fprintf( out, " (void *)-1L\n};\n" );
676 int genfunctable(int argc, char **argv)
678 FILE *fd = NULL, *fdo;
679 struct libconf *lc;
680 char *line = 0;
681 char *word, **words = NULL;
682 int in_archive, in_header, in_function, in_autodoc, in_code;
683 int num;
684 char *funcname = NULL, **funcnames = NULL;
686 /* Well, there are already 4 functions (open,close,expunge,null) */
687 int numfuncs = 4;
689 int has_arch = 1;
692 /* First check if we have a HIDD which does not have an archive */
693 if(!(lc=parse_libconf(NULL)))
694 return(-1);
696 if (lc->type == t_hidd || lc->type == t_gadget || lc->type == t_class)
697 has_arch = 0;
699 if(has_arch)
701 if(argc == 2)
703 fd = fopen(argv[1],"rb");
704 if(!fd)
706 fprintf( stderr, "Couldn't open file %s!\n", argv[1] );
707 exit(-1);
710 else if (argc == 1)
712 has_arch = 0;
714 else
716 fprintf( stderr, "Usage: %s <archfile>\n", argv[0] );
717 exit(-1);
720 fdo = fopen("functable.c.new","w");
721 if(!fdo)
723 fprintf( stderr, "Couldn't open file %s!\n", "functable.c.new" );
724 exit(-1);
727 if(has_arch)
729 in_archive = 0;
730 in_function = 0;
731 in_autodoc = 0;
732 in_code = 0;
733 in_header = 0;
734 while( (line = get_line(fd)) )
736 word = keyword(line);
737 if( word )
739 if( strcmp(word,"Archive")==0 && !in_archive )
740 in_archive = 1;
741 else if( strcmp(word,"/Archive")==0 && in_archive && ! in_function )
742 break;
743 else if( strcmp(word,"AutoDoc")==0 && in_function && !in_autodoc && !in_code )
744 in_autodoc = 1;
745 else if( strcmp(word,"/AutoDoc")==0 && in_autodoc )
746 in_autodoc = 0;
747 else if( strcmp(word,"Code")==0 && in_function && !in_code && !in_autodoc )
748 in_code = 1;
749 else if( strcmp(word,"/Code")==0 && in_code )
750 in_code = 0;
751 else if( strcmp(word,"Header")==0 && in_archive && !in_function )
752 in_header = 1;
753 else if( strcmp(word,"/Header")==0 && in_header )
754 in_header = 0;
755 else if( strcmp(word,"Function")==0 && in_archive && !in_function && !in_header )
757 num = get_words(line,&words);
758 funcname = strdup(words[num-1]);
759 in_function = 1;
761 else if( strcmp(word,"/Function")==0 && in_function && !in_autodoc && !in_code )
762 in_function = 0;
763 else if( strcmp(word,"LibOffset")==0 && in_function && !in_autodoc && !in_code )
765 get_words(line,&words);
766 num = atoi(words[1]);
767 if( num>numfuncs )
769 funcnames = realloc( funcnames, (num-4) * sizeof(char *));
770 /* initialize new memory */
771 for( ;numfuncs<num; numfuncs++)
772 funcnames[numfuncs-4] = NULL;
774 funcnames[num-5] = funcname;
777 free(word);
779 free(line);
782 emit(fdo,lc,funcnames,numfuncs);
783 fclose(fdo);
784 if(has_arch)
786 fclose(fd);
788 moveifchanged("functable.c","functable.c.new");
789 free(lc);
791 return 0;
795 const char * m68k_registers[] = {
796 "D0",
797 "D1",
798 "D2",
799 "D3",
800 "D4",
801 "D5",
802 "D6",
803 "D7",
804 "A0",
805 "A1",
806 "A2",
807 "A3",
808 "A4",
809 "A5",
810 "A6",
811 "A7",
814 struct pragma_description
816 struct pragma_description *next;
817 char basename[200];
818 char funcname[200];
819 unsigned short offset;
820 int numargs;
821 int args[16];
824 char * skipstr(char * line, char skipper,int direction)
826 while (line[0] == skipper) {
827 line += direction;
829 return line;
832 void free_pragma_description(struct pragma_description * pd)
834 struct pragma_description * _pd;
835 while (pd != NULL) {
836 _pd = pd -> next;
837 free(pd);
838 pd = _pd;
843 struct pragma_description * find_pragma_description(struct pragma_description * pd,
844 char * funcname)
846 while (NULL != pd) {
847 if (0 == strcmp(pd->funcname,
848 funcname))
849 return pd;
850 pd = pd->next;
852 return NULL;
855 struct pragma_description * parse_pragmas(char * filename)
857 FILE *fdi =NULL;
858 char *line;
859 struct pragma_description * base_pd = NULL;
860 fdi = fopen(filename,"r");
862 if (!fdi){
863 fprintf(stderr, "Couldn't open file %s!\n",filename);
864 return NULL;
867 while( (line = get_line(fdi))) {
868 char * substr;
869 //printf("%s\n",line);
871 * look for '#', then 'pragma'.
873 substr = skipstr(line,' ',1);
875 if (substr[0] == '#'){
876 substr++;
877 substr = skipstr(substr, ' ',1);
878 //printf("1. %s\n",substr);
879 if (0 == strncmp(substr, "pragma",6)) {
880 substr += 6;
881 substr = skipstr(substr,' ',1);
882 //printf("2. %s\n",substr);
883 if (!strncmp(substr, "amicall",7)) {
884 substr += 7;
885 substr = skipstr(substr,' ',1);
887 if (substr[0] == '(') {
888 struct pragma_description * pd = calloc(1,sizeof(struct pragma_description));
889 substr += 1;
890 substr = skipstr(substr,' ',1);
891 if (NULL != pd) {
892 char * comma;
893 char * lastchar;
894 pd->next = base_pd;
895 base_pd = pd;
897 * Now read the name of the base
898 * of the library!
900 comma = strchr(substr, ',');
901 lastchar = skipstr(comma-1,' ',-1);
903 strncpy(&pd->basename[0],
904 substr,
905 lastchar-substr+1);
906 //printf("basename: %s\n",pd->basename);
909 * after the comma comes the offset in HEX!
911 pd->offset = strtol(comma+1,&lastchar,16)/6;
912 //printf("Offset : %x\n",pd->offset);
915 * Now for the next ','
917 comma = strchr(lastchar, ',');
918 comma = skipstr(comma+1,' ',1);
919 //printf("%s\n",comma);
921 if ( NULL == comma) {
922 fprintf(stderr, "%d: Error parsing pragma file!\n",__LINE__);
923 free_pragma_description(base_pd);
924 fclose(fdi);
925 exit(-1);
929 * Now the name of the function
931 lastchar = strchr(comma+1, '(');
932 if ( NULL == lastchar) {
933 fprintf(stderr, "%d: Error parsing pragma file!\n",__LINE__);
934 free_pragma_description(base_pd);
935 fclose(fdi);
936 exit(-1);
940 strncpy(&pd->funcname[0],
941 comma,
942 skipstr(lastchar,' ',-1)-comma);
943 //printf("funcname: %s\n",pd->funcname);
945 substr = lastchar + 1;
946 //printf("%s\n",substr);
947 lastchar = strchr(substr,')');
949 * Now read the CPU registers.
952 while (substr < lastchar) {
953 int r = 0;
954 substr = skipstr(substr, ' ',1);
956 if (substr[0] == 'a' ||
957 substr[0] == 'A') {
958 r = 8;
959 } else if (substr[0] == 'd' ||
960 substr[0] == 'D') {
961 } else {
962 fprintf(stderr, "Wrong register (letter) in pragma file!\n");
963 free_pragma_description(base_pd);
964 fclose(fdi);
965 exit(-1);
968 if (substr[1] >= '0' && substr[1] <= '8') {
969 r += substr[1] - '0';
970 } else {
971 fprintf(stderr, "Wrong register (number) in pragma file!\n");
972 free_pragma_description(base_pd);
973 fclose(fdi);
974 exit(-1);
977 printf("r:%d\n",r);
978 printf("parameter %d goes into %s\n",
979 pd->numargs+1,
980 m68k_registers[r]);
982 pd->args[pd->numargs] = r;
983 pd->numargs++;
985 substr+=2;
986 skipstr(substr, ' ', 1);
987 substr+=1;
993 } else if (!strncmp(substr, "libcall",7)) {
994 struct pragma_description * pd = calloc(1,sizeof(struct pragma_description));
995 substr += 7;
996 //substr = skipstr(substr,' ',1);
997 if (NULL != pd) {
998 char offset_s[10];
999 char parameters_s[20];
1000 char hex[3]={0,0,0};
1001 int i,c;
1003 * Read in the description of the
1004 * function
1006 sscanf(substr, "%s %s %s %s",
1007 &pd->basename[0],
1008 &pd->funcname[0],
1009 offset_s,
1010 parameters_s);
1011 pd->offset = strtol(offset_s,NULL,16)/6;
1012 #if 1
1013 printf("|%s| |%s| %d %s[%d]\t",
1014 pd->basename,
1015 pd->funcname,
1016 pd->offset,
1017 parameters_s,
1018 strlen(parameters_s));
1019 #endif
1021 * Process the parameters.
1023 i = strlen(parameters_s)-1;
1024 hex[0] = parameters_s[i-1];
1025 hex[1] = parameters_s[i];
1026 i -= 2;
1027 pd->numargs = strtol(hex,NULL,16);
1028 c = 0;
1029 hex[1] = 0;
1030 while (i >= 0) {
1031 hex[0] = parameters_s[i];
1032 pd->args[c] = strtol(hex,NULL,16);
1033 printf("%s ",m68k_registers[pd->args[c]]);
1034 i--;
1035 c++;
1037 printf("\n");
1039 pd->next = base_pd;
1040 base_pd = pd;
1042 } else if(!strncmp(substr, "usrcall",7)) {
1043 substr += 7;
1044 substr = skipstr(substr,' ',1);
1046 if (substr[0] == '(') {
1047 struct pragma_description * pd = calloc(1,sizeof(struct pragma_description));
1048 pd->offset = 0xffff; // sign for user function
1049 substr += 1;
1050 substr = skipstr(substr,' ',1);
1051 if (NULL != pd) {
1052 char * lastchar;
1053 pd->next = base_pd;
1054 base_pd = pd;
1056 * Now the name of the function
1058 printf("%s\n",substr);
1059 lastchar = strchr(substr+1, '(');
1060 if ( NULL == lastchar) {
1061 fprintf(stderr, "%d: Error parsing pragma file!\n",__LINE__);
1062 free_pragma_description(base_pd);
1063 fclose(fdi);
1064 exit(-1);
1068 strncpy(&pd->funcname[0],
1069 substr,
1070 skipstr(lastchar,' ',-1)-substr);
1071 //printf("funcname: %s\n",pd->funcname);
1073 substr = lastchar + 1;
1074 //printf("%s\n",substr);
1075 lastchar = strchr(substr,')');
1077 * Now read the CPU registers.
1080 while (substr < lastchar) {
1081 int r = 0;
1082 substr = skipstr(substr, ' ',1);
1084 if (substr[0] == 'a' ||
1085 substr[0] == 'A') {
1086 r = 8;
1087 } else if (substr[0] == 'd' ||
1088 substr[0] == 'D') {
1089 } else {
1090 fprintf(stderr, "Wrong register (letter) in pragma file!\n");
1091 free_pragma_description(base_pd);
1092 fclose(fdi);
1093 exit(-1);
1096 if (substr[1] >= '0' && substr[1] <= '8') {
1097 r += substr[1] - '0';
1098 } else {
1099 fprintf(stderr, "Wrong register (number) in pragma file!\n");
1100 free_pragma_description(base_pd);
1101 fclose(fdi);
1102 exit(-1);
1105 printf("r:%d\n",r);
1106 printf("parameter %d goes into %s\n",
1107 pd->numargs+1,
1108 m68k_registers[r]);
1110 pd->args[pd->numargs] = r;
1111 pd->numargs++;
1113 substr+=2;
1114 skipstr(substr, ' ', 1);
1115 substr+=1;
1123 free(line);
1127 fclose(fdi);
1128 return base_pd;
1132 int countchars (char * s, char c)
1134 int ctr = 0;
1135 while (s[0] != 0) {
1136 if (s[0] == c)
1137 ctr++;
1138 s++;
1140 return ctr;
1144 * Add a line to the cache. If NULL is added the old cache
1145 * is flushed to the disk.
1146 * If a line is added everything that ended with a ';' up
1147 * to that point will be written into the file and the
1148 * cache will be cut down to everything that was not written
1149 * so far.
1150 * If a 'end-of-comment' is added also everything is flushed to output.
1152 char * addtocache(char * cache, char * newline, int len, FILE * fdo)
1154 int cachelen;
1155 char * semic;
1156 char * newcache = NULL;
1157 char * endcomment;
1159 if (NULL == newline) {
1160 if (NULL != cache) {
1161 fprintf(fdo, "%s\n", cache);
1162 free(cache);
1164 return NULL;
1168 semic = strchr(newline, ';');
1169 endcomment = strstr(newline, "*/");
1171 if (NULL != semic || NULL != endcomment) {
1172 int newlinelen = strlen(newline);
1173 int i = newlinelen -1;
1174 char * tmp;
1176 * write the cache and everything up to the
1177 * last ';' in the new line to the file.
1179 if (NULL != cache) {
1180 fprintf(fdo,"%s\n",cache);
1181 //printf("1. Flush: |%s|\n",cache);
1184 if (NULL != endcomment) {
1185 i = endcomment - newline + 1;
1186 } else {
1187 while (newline[i] != ';')
1188 i--;
1192 tmp = malloc(i+2);
1193 memcpy(tmp,newline,i+1);
1194 tmp[i+1] = 0;
1195 fprintf(fdo, "%s",tmp);
1196 //printf("2. Flush: |%s|\n",tmp);
1197 free(tmp);
1199 if (i < newlinelen) {
1200 newcache = malloc(newlinelen-i+1);
1201 memcpy(newcache, &newline[i+1], newlinelen-i);
1202 newcache[newlinelen-i] = 0;
1204 free(cache);
1206 } else {
1208 * ';' could not be found. Enhance the cache.
1210 cachelen = 0;
1211 if (NULL != cache)
1212 cachelen = strlen(cache);
1214 cachelen += strlen(newline)+1+1;
1215 newcache = malloc(cachelen);
1216 if (NULL != cache)
1217 sprintf(newcache,"%s\n%s",cache,newline);
1218 else
1219 sprintf(newcache,"%s",newline);
1220 //printf("cache: %s\tnewcache: %s\n",cache,newcache);
1221 free (cache);
1224 return newcache;
1227 char * clear_cache(char * cache)
1229 free(cache);
1230 return NULL;
1234 * cut out all '\n' '\t' and ' ' at the end and
1235 * beginning of a string
1237 void strtrim(char ** s)
1239 int end = strlen(*s)-1;
1240 int start = 0;
1241 char * newstr;
1242 int end_orig = end;
1244 while ((start < end) && (
1245 '\t' == (*s)[start] ||
1246 '\n' == (*s)[start] ||
1247 ' ' == (*s)[start])) {
1248 start++;
1251 while (end > 0 && (
1252 '\t' == (*s)[end] ||
1253 '\n' == (*s)[end] ||
1254 ' ' == (*s)[end])) {
1255 end--;
1258 if ((end > start) && ((start > 0) || (end_orig != end))) {
1259 newstr = malloc(end-start+2);
1260 strncpy(newstr, (*s)+start, end-start+1);
1261 newstr[end-start+1] = 0;
1262 free(*s);
1263 *s = newstr;
1267 char * getfuncname(char * s)
1269 int i = strlen(s)-1;
1270 int c = 0;
1271 char * name;
1272 int last;
1273 while (i > 0) {
1274 if (')' == s[i])
1275 c++;
1276 else if ('(' == s[i]) {
1277 c--;
1278 if (0 == c) {
1279 i--;
1280 while (i >= 0 && ' ' == s[i])
1281 i--;
1283 if (i == 0)
1284 return NULL;
1286 last = i;
1287 while (i > 0 && ' ' != s[i] && '\n' != s[i])
1288 i--;
1290 if (last == i)
1291 return NULL;
1293 if (s[i+1] == '*')
1294 i++;
1296 name = malloc(last-i+2);
1297 strncpy(name, &s[i+1], last-i);
1298 name[last-i]=0;
1299 strtrim(&name);
1300 return name;
1304 i--;
1306 return NULL;
1309 char * get_returntype(char * pattern, char * funcname)
1311 int len;
1312 int c = 0;
1313 char * ret_type;
1314 len = (int)strstr(pattern, funcname) - (int)pattern;
1316 ret_type = malloc(len-c+1);
1317 strncpy(ret_type, pattern+c, len-c);
1318 ret_type[len-c] = 0;
1320 strtrim(&ret_type);
1322 return ret_type;
1325 void get_argument(int num, int max, char * pattern, char ** type, char ** val)
1327 int i = 0;
1328 int _num = num;
1329 while (1) {
1330 if ('(' == pattern[i++]) {
1331 char * start;
1332 int c;
1333 int depth = 0;
1334 int other = 0;
1336 while (_num > 0) {
1337 if (',' == pattern[i])
1338 _num--;
1339 i++;
1342 /* Start of nth argument. */
1343 start = &pattern[i];
1345 //printf("start: %s\n",start);
1347 i = 0;
1348 while (1) {
1349 if (',' == start[i])
1350 break;
1351 else if ('(' == start[i])
1352 depth++;
1353 else if (')' == start[i])
1354 depth--;
1356 if (-1 == depth)
1357 break;
1359 i++;
1362 i--;
1363 //printf("end at %d\n",i);
1365 * Search for the parameter value backwards
1367 c = i;
1368 while (1) {
1369 if (1 == other && (' ' == start[c] || '\t' == start[c] || '\n' == start[c] || '*' == start[c]))
1370 break;
1372 if (' ' != start[c])
1373 other = 1;
1374 c--;
1376 //c++;
1377 //printf("variable at %d\n",c);
1378 *type = malloc(c+2);
1379 strncpy(*type, start, c+1);
1380 (*type)[c+1] = 0;
1381 *val = malloc(i-c+2);
1382 strncpy(*val , start+c+1, i-c);
1383 (*val)[i-c] = 0;
1384 //printf("|%s| |%s|\n",*type,*val);
1386 strtrim(type);
1387 strtrim(val);
1388 return;
1395 * Rewrite a c function to AROS style c function
1397 int rewrite_function(FILE * fdo,
1398 char * pattern,
1399 struct pragma_description * pd,
1400 struct libconf * lc,
1401 FILE * fdefines)
1403 char output[1024];
1404 int i = 0;
1405 char * ret_type;
1406 char * argtype, * argval;
1407 memset(&output[0],0,1024);
1409 printf("must pick ???? from info in: \n%s\n",pattern);
1410 ret_type = get_returntype(pattern,pd->funcname);
1413 if (0xffff != pd->offset) {
1414 fprintf(fdo,
1415 "\nAROS_LH%d(%s, %s,\n",
1416 pd->numargs,
1417 ret_type,
1418 pd->funcname);
1421 while (i < pd->numargs) {
1422 get_argument(i, pd->numargs,pattern, &argtype, &argval);
1423 fprintf(fdo,
1424 " AROS_LHA(%s, %s, %s),\n",
1425 argtype,
1426 argval,
1427 m68k_registers[pd->args[i]]);
1429 free(argtype);
1430 free(argval);
1431 i++;
1434 if (0 != pd->offset) {
1435 fprintf(fdo,
1436 " %s, %s, %d, %s)\n{\n",
1437 lc->libbasetypeptr,
1438 lc->libbase,
1439 pd->offset,
1440 lc->basename);
1441 } else {
1442 fprintf(fdo,
1443 " struct ExecBase *, SysBase, 0, %s)\n{\n",
1444 lc->basename);
1448 * Make the entry in the defines file. Only write
1449 * those functions with offset > 4 (not init,open,close,
1450 * expunge, null)
1452 if (pd->offset > 4) {
1453 fprintf(fdefines, "#ifndef %s\n#define %s(",
1454 pd->funcname,
1455 pd->funcname);
1457 i = 0;
1459 while (i < pd->numargs) {
1460 get_argument(i, pd->numargs,pattern, &argtype, &argval);
1461 fprintf(fdefines,
1462 "%s",
1463 argval);
1464 if (i < pd->numargs-1) {
1465 fprintf(fdefines,",");
1467 free(argtype);
1468 free(argval);
1470 i++;
1473 fprintf(fdefines,") \\\n\tAROS_LC%d(%s,%s, \\\n",
1474 pd->numargs,
1475 ret_type,
1476 pd->funcname);
1478 i = 0;
1479 while (i < pd->numargs) {
1480 get_argument(i, pd->numargs,pattern, &argtype, &argval);
1481 fprintf(fdefines,
1482 "\tAROS_LCA(%s,%s,%s),\\\n",
1483 argtype,
1484 argval,
1485 m68k_registers[pd->args[i]]);
1486 free(argtype);
1487 free(argval);
1489 i++;
1492 fprintf(fdefines,
1493 "\t%s, %s, %d, %s)\n#endif\n\n",
1494 lc->libbasetypeptr,
1495 lc->libbase,
1496 pd->offset,
1497 lc->basename);
1499 free(ret_type);
1501 } else {
1502 fprintf(fdo,
1503 "\nAROS_UFH%d(%s, %s",
1504 pd->numargs,
1505 ret_type,
1506 pd->funcname);
1509 while (i < pd->numargs) {
1510 get_argument(i, pd->numargs,pattern, &argtype, &argval);
1511 strtrim(&argtype);
1512 strtrim(&argval);
1514 fprintf(fdo,
1515 ",\n AROS_UFHA(%s, %s, %s)",
1516 argtype,
1517 argval,
1518 m68k_registers[pd->args[i]]);
1520 free(argtype);
1521 free(argval);
1522 i++;
1524 fprintf(fdo,")\n{\n");
1526 return 0;
1530 * Rewrite a whole c source code file according to the info provided
1531 * from a pragma file.
1533 int rewritecfile(FILE * fdi,
1534 FILE * fdo,
1535 struct pragma_description * pd,
1536 struct libconf * lc,
1537 FILE * fdefines)
1539 char * line;
1540 int depth = 0; // counting '{' and '}'
1541 char * cache = NULL;
1543 while (1) {
1544 line = get_line(fdi);
1545 if (NULL == line) {
1546 cache = addtocache(cache, NULL, 0, fdo);
1547 return 0;
1550 if (0 == depth) {
1551 depth = countchars(line,'{');
1552 if (0 != depth) {
1553 char * funcname;
1555 * A function begins in this line.
1558 cache = addtocache(cache,line,-1,fdo);
1560 //printf("\ncache where to find function: %s\n",cache);
1561 funcname = getfuncname(cache);
1562 if (funcname != NULL) {
1563 struct pragma_description * _pd;
1564 printf("funcname: %s",funcname);
1565 _pd = find_pragma_description(pd, funcname);
1566 if (_pd) {
1567 printf("-> Rewriting!\n");
1568 rewrite_function(fdo,cache,_pd,lc,fdefines);
1570 * Do not throw the cache into the
1571 * file but clear it
1573 cache = clear_cache(cache);
1574 } else
1575 printf("-> Not rewriting!\n");
1576 free(funcname);
1577 } else {
1578 //printf("ADDING 1\n");
1579 // cache = addtocache(cache,line,-1,fdo);
1582 //printf("ADDING 2\n");
1583 cache = addtocache(cache,NULL,0,fdo);
1584 } else {
1585 char * substr;
1587 * No function begins in this line.
1588 * So let's collect it unless it is
1589 * a comment ot starts with a '#'.
1591 substr = skipstr(line, ' ',1);
1593 if ('#' == substr[0] ||
1594 NULL != strstr(line,"//") ) {
1595 cache = addtocache(cache,line,-1,fdo);
1596 cache = addtocache(cache,NULL,0,fdo);
1597 //printf("cache: %p\n",cache);
1598 //fprintf(fdo,"%s\n",line);
1599 } else {
1600 cache = addtocache(cache,line,-1,fdo);
1603 depth -= countchars(line,'}');
1604 } else {
1605 depth += countchars(line,'{');
1606 depth -= countchars(line,'}');
1607 fprintf(fdo,"%s\n",line);
1609 free(line);
1612 return 0;
1615 FILE * create_definesfile(char * filename)
1617 FILE * fdo = fopen(filename, "r");
1620 * If file existed position at end.
1622 if (fdo) {
1623 fclose(fdo);
1624 fdo = fopen(filename, "a+");
1625 return fdo;
1629 * File does not exist, so start it, if possible.
1631 fdo = fopen(filename, "w");
1632 if (NULL != fdo) {
1633 fprintf(fdo, "#include <aros/libcall.h>\n"
1634 "#include <exec/types.h>\n\n");
1638 return fdo;
1642 * Generate AROS source code for a library from the description
1643 * in a pragmas file.
1645 int genarossource(int argc, char **argv)
1647 FILE *fdo =NULL;
1648 FILE *fdi =NULL;
1649 FILE *fdefines = NULL;
1650 char * filename;
1651 char * sourcefile, * destfile;
1652 char * definesfile;
1653 struct libconf *lc;
1654 struct pragma_description * pd;
1657 if (argc !=5) {
1658 fprintf(stderr,"Usage: %s <source pragma file> <source c file> <dest c file> <output defines file>\n", argv[0]);
1659 exit(-1);
1662 if(!(lc = parse_libconf(NULL)) )
1664 return(-1);
1666 filename = malloc( (strlen(argv[1])+strlen(lc->libname)+20) * sizeof(char) );
1667 sprintf( filename, "%s", argv[1]);
1669 sourcefile = argv[2];
1670 destfile = argv[3];
1671 definesfile = argv[4];
1673 pd = parse_pragmas(filename);
1675 if (NULL == pd) {
1676 fprintf(stderr, "Could not read in the pragmas!\n");
1677 exit(-1);
1681 * Now open and parse the C input file and generate an output.
1683 fdi = fopen(sourcefile, "r");
1684 fdo = fopen(destfile, "w");
1685 fdefines = create_definesfile(definesfile);
1687 if (NULL != fdi && NULL != fdo && NULL != fdefines)
1688 rewritecfile(fdi,fdo,pd,lc,fdefines);
1690 if (NULL != fdi)
1691 fclose(fdi);
1692 if (NULL != fdo)
1693 fclose(fdo);
1694 if (NULL != fdefines)
1695 fclose(fdefines);
1697 free_pragma_description(pd);
1699 free(lc);
1700 free(filename);
1701 return 0;
1705 int main(int argc, char **argv)
1707 int retval = 0;
1708 char option;
1710 if( argc < 2 )
1712 fprintf( stderr, "Usage: %s [-h|-t|-c|-R] <parameter>\n", argv[0] );
1713 fprintf( stderr, " -h help\n -t genfunctable\n -c genlibdefs\n -R genarossource\n" );
1714 exit(-1);
1717 if( argv[1][0] == '-' )
1719 argc--;
1720 option = argv[1][1];
1721 argv[1] = malloc( (strlen(argv[0])+4) * sizeof(char) );
1722 sprintf( argv[1], "%s -%c", argv[0], option );
1723 switch( option )
1725 case 'R':
1726 retval = genarossource(argc, &argv[1]);
1727 break;
1728 case 't':
1729 retval = genfunctable( argc, &argv[1] );
1730 break;
1731 case 'c':
1732 retval = genlibdefs( argc, &argv[1] );
1733 break;
1734 case 'h':
1735 default:
1736 fprintf( stdout, "Usage: %s [-h|-t|-c|-R] <parameter>\n", argv[0] );
1737 fprintf( stdout, " -h help\n -t genfunctable\n -c genlibdefs\n -R genarossource\n" );
1738 break;
1742 return retval;