Release 961023
[wine] / rc / winerc.c
1 /*
2  *
3  * Copyright  Martin von Loewis, 1994
4  *
5  */
6
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <sys/stat.h>
11 #include <sys/fcntl.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include "windows.h"
16 #include "parser.h"
17 #include "y.tab.h"
18
19 char usage[]="winerc -bdvc -p prefix -o outfile < infile \n"
20         "   -b            Create a C array from a binary .res file\n"
21         "   -c            Add 'const' prefix to C constants\n"
22         "   -d            Output debugging information\n"
23         "   -p prefix     Give a prefix for the generated names\n"
24         "   -v            Show each resource as it is processed\n"
25         "   -o file       Output to file.c and file.h\n";
26
27
28 /*might be overwritten by command line*/
29 char *prefix="_Resource";
30 int verbose,constant;
31 gen_res* g_start;
32 FILE *header,*code;
33 char hname[256],sname[256];
34
35 int transform_binary_file(void);
36 int yyparse(void);
37
38 static void *xmalloc (size_t size)
39 {
40     void *res;
41
42     res = malloc (size ? size : 1);
43     if (res == NULL)
44     {
45         fprintf (stderr, "Virtual memory exhausted.\n");
46         exit (1);
47     }
48     return res;
49 }
50
51
52 int main(int argc,char *argv[])
53 {  
54         extern int yydebug;
55         extern char* optarg;
56         char* tmpc;
57         int optc,lose,ret,binary;
58         lose=binary=0;
59         while((optc=getopt(argc,argv,"bcdp:vo:"))!=EOF)
60                 switch(optc)
61                 {
62                         /* bison will print state transitions on stderr */
63                         case 'b':binary=1;
64                                          break;
65                         case 'd':yydebug=1;
66                                          setbuf(stdout,0);
67                                          setbuf(stderr,0);
68                                         break;
69                         case 'p':prefix=strdup(optarg);
70                                          if(!isalpha(*prefix))*prefix='_';
71                                          for(tmpc=prefix;*tmpc;tmpc++)
72                                           if( !isalnum(*tmpc) && *tmpc!='_')
73                                            *tmpc='_';
74                                         break;
75                         case 'c':constant=1;break;
76                         case 'v':verbose=1;
77                                          setbuf(stderr,0);
78                                         break;
79                         case 'o':set_out_file(optarg);break;
80                         default: lose++;break;
81                 }
82         if(lose)return fprintf(stderr,usage),1;
83         if(!header)header=stdout;
84         if(!code)code=stdout;
85         if(binary)
86                 ret=transform_binary_file();
87         else
88                 ret=yyparse();
89         fclose(header);
90         fclose(code);
91         return ret;
92 }
93
94 void set_out_file(char *prefix)
95 {
96         sprintf(sname,"%s.c",prefix);
97         code=fopen(sname,"w");
98         sprintf(hname,"%s.h",prefix);
99         header=fopen(hname,"w");
100 }
101
102 int transform_binary_file()
103 {
104         int i,c;
105         fprintf(header,"#define APPLICATION_HAS_RESOURCES 1\n");
106         fprintf(code,"char _Application_resources[]={");
107         for(i=0;;i++)
108         {
109                 c=getchar();
110                 if(c==-1)break;
111                 if(i%16==0)fputc('\n',code);
112                 fprintf(code,"%3d,",c);
113         }
114         fprintf(code,"\n  0};\nint _Application_resources_size=%d;\n",i);
115         return 0;
116 }
117
118 /* SunOS' memcpy is wrong for overlapping arrays */
119 char *save_memcpy(char *d,char* s,int l)
120 {
121         if(d<s)
122                 for(;l;l--)*d++=*s++;
123         else
124                 for(d+=l-1,s+=l-1;l;l--)*d--=*s--;
125         return d;
126 }
127
128 /*allow unaligned access*/
129 void put_WORD(unsigned char* p,WORD w)
130 {
131         *p=w&0xFF;
132         *(p+1)=w>>8;
133 }
134
135 void put_DWORD(unsigned char* p,DWORD d)
136 {
137         put_WORD(p,d&0xFFFF);
138         put_WORD(p+2,d>>16);
139 }
140
141 WORD get_WORD(unsigned char* p)
142 {
143         return *p|(*(p+1)<<8);
144 }
145
146 DWORD get_DWORD(unsigned char* p)
147 {
148         return get_WORD(p)|(get_WORD(p+2)<<16);
149 }
150
151
152 /*create a new gen_res, initial size 100*/
153 gen_res *new_res()
154 {       gen_res* ret=xmalloc(sizeof(gen_res)+100);
155         int i;
156         for(i=0;i<sizeof(gen_res)+100;i++)*((char*)ret+i)='\0';
157         ret->g_next=g_start;
158         ret->g_prev=0;
159         g_start=ret;
160         ret->space=100;
161         return ret;
162 }
163
164 /*double the space*/
165 gen_res* grow(gen_res* res)
166 {
167         res=realloc(res,sizeof(gen_res)+2*res->space);
168         if(!res)
169                 fprintf(stderr,"Out of memory\n"),exit(1);
170         if(!res->g_prev)g_start=res;
171         else res->g_prev->g_next=res;
172         if(res->g_next)res->g_next->g_prev=res;
173         res->space=2*res->space;
174         return res;
175 }
176
177
178 /* insert bytes at offset 0, increase num_entries */
179 gen_res* insert_at_beginning(gen_res* res,char* entry,int size)
180 {
181         while(res->size+size>res->space)res=grow(res);
182         save_memcpy(res->res+size,res->res,res->size);
183         save_memcpy(res->res,entry,size);
184         res->size+=size;
185         res->num_entries++;
186         return res;
187 }
188
189 /* insert length characters from bytes into res, starting at start */
190 gen_res* insert_bytes(gen_res* res,char* bytes,int start,int length)
191 {
192         while(res->size+length>res->space)res=grow(res);
193         save_memcpy(res->res+start+length,res->res+start,res->size-start);
194         save_memcpy(res->res+start,bytes,length);
195         res->size+=length;
196         return res;
197 }
198
199 /*delete len bytes from res, starting at start*/
200 gen_res* delete_bytes(gen_res* res,int start,int len)
201 {
202         save_memcpy(res->res+start,res->res+start+len,res->size-start-len);
203         res->size-=len;
204         return res;
205 }
206
207 /*create a new style*/
208 rc_style *new_style()
209 {
210         rc_style *ret=xmalloc(sizeof(rc_style));
211         /*initially, no bits have to be reset*/
212         ret->and=-1;
213         /*initially, no bits are set*/
214         ret->or=WS_CHILD | WS_VISIBLE;
215         return ret;
216 }
217
218 /* entries are inserted at the beginning, starting from the last one */
219 gen_res* add_accelerator(int ev, int id, int flags, gen_res* prev)
220 {
221         char accel_entry[5];
222         if(prev->num_entries==0)flags|=0x80; /* last entry */
223         accel_entry[0]=flags;
224         put_WORD(accel_entry+1,ev);
225         put_WORD(accel_entry+3,id);
226         return insert_at_beginning(prev,accel_entry,5);
227 }
228
229
230 /* create an integer from the event, taking things as "^c" into account
231    add this as new entry */
232 gen_res* add_string_accelerator(char *ev, int id, int flags, gen_res* prev)
233 {
234         int event;
235         if(*ev=='^')
236                 event=ev[1]-'a';
237         else
238                 event=ev[0];
239         return add_accelerator(event,id,flags,prev);
240 }
241
242 /*is there a difference between ASCII and VIRTKEY accelerators? */
243
244 gen_res* add_ascii_accelerator(int ev, int id, int flags, gen_res* prev)
245 {
246         return add_accelerator(ev,id,flags,prev);
247 }
248
249 gen_res* add_vk_accelerator(int ev, int id, int flags, gen_res* prev)
250 {
251         return add_accelerator(ev,id,flags,prev);
252 }
253
254 /* create a new dialog header, set all items to 0 */
255 gen_res* new_dialog()
256 {       gen_res* ret=new_res();
257         ret->size=16; /*all strings "\0", no font*/
258         return ret;
259 }
260
261 /* the STYLE option was specified */
262 gen_res* dialog_style(rc_style* style, gen_res* attr)
263 {
264         /* default dialog styles? Do we need style->and? */
265         /* DS_SETFONT might have been specified before */
266         put_DWORD(attr->res,get_DWORD(attr->res)|style->or);
267         return attr;
268 }
269
270 /* menu name is at offset 13 */
271 int dialog_get_menu(gen_res* attr)
272 {
273         return 13;
274 }
275
276 /* the class is after the menu name */
277 int dialog_get_class(gen_res* attr)
278 {
279         int offs=dialog_get_menu(attr);
280         while(attr->res[offs])offs++;
281         offs++;
282         return offs;
283 }
284
285 /* the caption is after the class */
286 int dialog_get_caption(gen_res* attr)
287 {
288         int offs=dialog_get_class(attr);
289         while(attr->res[offs])offs++;
290         offs++;
291         return offs;
292 }
293
294 /* the fontsize, if present, is after the caption, followed by the font name */
295 int dialog_get_fontsize(gen_res* attr)
296 {
297         int offs=dialog_get_caption(attr);
298         while(attr->res[offs])offs++;
299         offs++;
300         return offs;
301 }
302
303
304 /* the CAPTION option was specified */
305 gen_res* dialog_caption(char* cap, gen_res*attr)
306 {
307         /* we don't need the terminating 0 as it's already there */
308         return insert_bytes(attr,cap,dialog_get_caption(attr),strlen(cap));
309 }
310
311
312 /* the FONT option was specified, set the DS_SETFONT flag */
313 gen_res* dialog_font(short size,char* font,gen_res *attr)
314 {
315         char c_size[2];
316         int offs=dialog_get_fontsize(attr);
317         put_DWORD(attr->res,get_DWORD(attr->res)|DS_SETFONT);
318         put_WORD(c_size,size);
319         attr=insert_bytes(attr,c_size,offs,2);
320         offs+=2;
321         /* as there is no font name by default, copy the '\0' */
322         return insert_bytes(attr,font,offs,strlen(font)+1);
323 }
324
325 gen_res* dialog_class(char* cap, gen_res*attr)
326 {
327         return insert_bytes(attr,cap,dialog_get_class(attr),strlen(cap));
328 }
329
330 gen_res* dialog_menu(char* cap, gen_res*attr)
331 {
332         return insert_bytes(attr,cap,dialog_get_menu(attr),strlen(cap));
333 }
334
335 /* set the dialogs id, position, extent, and style */
336 gen_res* create_control_desc(int id,int x,int y,int cx, int cy,rc_style *style)
337 {       gen_res* ret=new_res();
338         int s=WS_VISIBLE|WS_CHILD; /*defaults styles for any control*/
339         put_WORD(ret->res+0,x);
340         put_WORD(ret->res+2,y);
341         put_WORD(ret->res+4,cx);
342         put_WORD(ret->res+6,cy);
343         put_WORD(ret->res+8,id);
344         if(style)s=(s|style->or)&style->and;
345         put_DWORD(ret->res+10,s);
346         ret->size=17; /*empty strings, unused byte*/
347         return ret;
348 }
349
350 /* insert the control's label */
351 gen_res* label_control_desc(char* label,gen_res* cd)
352 {
353         int offs;
354         if(cd->res[14]&0x80)offs=15; /* one-character class */
355         else {
356                 for(offs=14;cd->res[offs];offs++);
357                 offs++;
358         }
359         return insert_bytes(cd,label,offs,strlen(label));
360 }
361
362 /* a CONTROL was specified */
363 gen_res* create_generic_control(char* label,int id,char* class,
364         rc_style*style,int x,int y,int cx,int cy)
365 {       char cl;
366         gen_res* ret=new_res();
367         put_WORD(ret->res+0,x);
368     put_WORD(ret->res+2,y);
369     put_WORD(ret->res+4,cx);
370     put_WORD(ret->res+6,cy);
371     put_WORD(ret->res+8,id);
372         put_DWORD(ret->res+10,style->or);
373         ret->size=17;
374         ret=insert_bytes(ret,label,15,strlen(label));
375         /* is it a predefined class? */
376         cl=0;
377         if(!strcmp(class,"BUTTON"))cl=CT_BUTTON;
378         if(!strcmp(class,"EDIT"))cl=CT_EDIT;
379         if(!strcmp(class,"STATIC"))cl=CT_STATIC;
380         if(!strcmp(class,"LISTBOX"))cl=CT_LISTBOX;
381         if(!strcmp(class,"SCROLLBAR"))cl=CT_SCROLLBAR;
382         if(!strcmp(class,"COMBOBOX"))cl=CT_COMBOBOX;
383         if(cl)ret->res[14]=cl;
384         else ret=insert_bytes(ret,class,14,strlen(class));
385         return ret;
386 }
387
388 /* insert cd into rest, set the type, add flags */
389 gen_res* add_control(int type,int flags,gen_res*cd,gen_res* rest)
390 {
391         put_DWORD(cd->res+10,get_DWORD(cd->res+10)|flags);
392         cd->res[14]=type;
393         return insert_at_beginning(rest,cd->res,cd->size);
394 }
395
396 /* an ICON control was specified, whf contains width, height, and flags */
397 gen_res* add_icon(char* name,int id,int x,int y,gen_res* whf,gen_res* rest)
398 {
399         put_WORD(whf->res+0,x);
400         put_WORD(whf->res+2,y);
401         put_WORD(whf->res+8,id);
402         whf=label_control_desc(name,whf);
403         return add_control(CT_STATIC,SS_ICON,whf,rest);
404 }
405
406 /* insert the generic control into rest */
407 gen_res* add_generic_control(gen_res* ctl, gen_res* rest)
408 {
409         return insert_at_beginning(rest,ctl->res,ctl->size);
410 }
411
412 /* create a dialog resource by inserting the header into the controls.
413    Set position and extent */
414 gen_res* make_dialog(gen_res* header,int x,int y,int cx,int cy,gen_res* ctls)
415 {
416         header->res[4]=ctls->num_entries;
417         header->type=dlg;
418         put_WORD(header->res+5,x);
419         put_WORD(header->res+7,y);
420         put_WORD(header->res+9,cx);
421         put_WORD(header->res+11,cy);
422         return insert_bytes(header,ctls->res,header->size,ctls->size);
423 }
424
425 /* create {0x15,0x16,0xFF} from '15 16 FF' */
426 gen_res *hex_to_raw(char *hex, gen_res*rest)
427 {
428         char r2[16];
429     int i;
430         for(i=0;*hex!='\'';i++)r2[i]=strtoul(hex,&hex,16);
431         return insert_bytes(rest,r2,0,i);
432 }
433
434 /* create a bitmap resource */
435 gen_res *make_bitmap(gen_res* res)
436 {
437         res=delete_bytes(res,0,14); /* skip bitmap file header*/
438         res->type=bmp;
439         return res;
440 }
441
442 gen_res *make_icon(gen_res* res)
443 {
444         res->type=ico;
445         return res;
446 }
447
448 gen_res *make_cursor(gen_res* res)
449 {
450         res->type=cur;
451         return res;
452 }
453
454 /* load resource bytes from the file name */
455 gen_res *load_file(char* name)
456 {
457         gen_res *res;
458         struct stat st;
459         int f=open(name,O_RDONLY);
460         if(!f)perror(name);
461         fstat(f,&st);
462         res=new_res();
463         while(res->space<st.st_size)res=grow(res);
464         read(f,res->res,st.st_size);
465         res->size=st.st_size;
466         close(f);
467         return res;
468 }
469
470 /* insert a normal menu item into res, starting from the last item */
471 gen_res *add_menuitem(char* name,int id,int flags,gen_res *res)
472 {
473         char item[4];
474         if(res->num_entries==0)flags|=MF_END;
475         put_WORD(item,flags);
476         put_WORD(item+2,id);
477         res=insert_at_beginning(res,name,strlen(name)+1);
478         res=insert_bytes(res,item,0,4);
479         return res;
480 }
481
482 /* insert a popup item into res */
483 gen_res *add_popup(char *name,short flags, gen_res* body, gen_res*res)
484 {
485         char c_flags[2];
486         flags|=MF_POPUP;
487         if(res->num_entries==0)flags|=MF_END;
488         put_WORD(c_flags,flags);
489         res=insert_at_beginning(res,body->res,body->size);
490         res=insert_bytes(res,name,0,strlen(name)+1);
491         res=insert_bytes(res,c_flags,0,2);
492         return res;
493 }
494
495 /* prefix the menu header into res */
496 gen_res *make_menu(gen_res* res)
497 {
498         static char header[4]={0,0,0,0};
499         res=insert_at_beginning(res,header,4);
500         res->type=men;
501         return res;
502 }
503
504 /* link top-level resources */
505 gen_res *add_resource(gen_res* first,gen_res *rest)
506 {
507     if(first)
508     {
509         first->next=rest;
510         return first;
511     }
512     else
513         return rest;
514 }
515
516 typedef struct str_tbl_elm{
517         int group;
518         struct str_tbl_elm *next;
519         char* strings[16];
520 } str_tbl_elm;
521
522 str_tbl_elm* string_table=NULL; /* sorted by group */
523
524 void add_str_tbl_elm(int id,char* str)
525 {
526   int group=(id>>4)+1;
527   int idx=id & 0x000f;
528
529   str_tbl_elm** elm=&string_table;
530   while(*elm && (*elm)->group<group) elm=&(*elm)->next;
531   if(!*elm || (*elm)->group!=group)
532   {
533     str_tbl_elm* new=xmalloc(sizeof(str_tbl_elm));
534     new->group=group;
535     new->next=*elm;
536     *elm=new;
537   }
538   (*elm)->strings[idx]=str;
539 }
540
541 gen_res* add_string_table(gen_res* t)
542 {
543   str_tbl_elm* ste;
544   int size,i;
545   gen_res* res;
546   unsigned char* p;
547   char* q;
548
549   if(!string_table) return t;
550   for(ste=string_table; ste; ste=ste->next)
551   {
552     for(size=0,i=0; i<16; i++)
553       size += ste->strings[i] ? strlen(ste->strings[i])+1 : 1;
554     res=new_res();
555     while(res->space<size)res=grow(res);
556     res->type=str;
557     res->n.i_name=ste->group;
558     res->n_type=0;
559     res->size=size;
560     for(p=res->res,i=0; i<16; i++)
561       if((q=ste->strings[i])==NULL)
562         *p++ = 0;
563       else
564       {
565         *p++ = strlen(q);
566         while(*q) *p++ = *q++;
567       }
568     t=add_resource(res,t);
569   }
570   return t;
571 }
572
573 char *get_typename(gen_res* t)
574 {
575         switch(t->type){
576         case acc:return "ACCELERATOR";
577         case bmp:return "BITMAP";
578         case cur:return "CURSOR";
579         case dlg:return "DIALOG";
580         case fnt:return "FONT";
581         case ico:return "ICON";
582         case men:return "MENU";
583         case rdt:return "RCDATA";
584         case str:return "STRINGTABLE";
585         default: return "UNKNOWN";
586         }
587 }
588
589 /* create strings like _Sysres_DIALOG_2 */
590 char *get_resource_name(gen_res*it)
591 {
592         static char buf[1000];
593         if(it->n_type)
594                 sprintf(buf,"%s_%s_%s",prefix,get_typename(it),it->n.s_name);
595         else
596                 sprintf(buf,"%s_%s_%d",prefix,get_typename(it),it->n.i_name);
597         return buf;
598 }
599
600 #define ISCONSTANT      (constant ? "const " : "")
601
602 /* create the final output */
603 void create_output(gen_res* top)
604 {
605     gen_res *it;
606
607     top=add_string_table(top);
608
609     fprintf( header, "/* %s\n"
610                      " * This file is automatically generated. Do not edit!\n"
611                      " */\n\n"
612                      "#include \"resource.h\"\n", hname );
613
614     /* Declare the resources */
615
616     for (it=top;it;it=it->next)
617         fprintf( header,"extern %sstruct resource %s;\n",
618                  ISCONSTANT, get_resource_name(it) );
619     fprintf( header,"\nextern %sstruct resource * %s%s_Table[];\n",
620              ISCONSTANT, ISCONSTANT, prefix );
621
622     /* Print the resources bytes */
623
624     fprintf( code, "/* %s\n"
625                    " * This file is automatically generated. Do not edit!\n"
626                    " */\n\n"
627                    "#include \"%s\"\n", sname, hname );
628
629     for(it=top;it;it=it->next)
630     {
631         int i;
632         fprintf( code, "static %sunsigned char %s__bytes[] = {\n",
633                  ISCONSTANT, get_resource_name(it) );
634         for (i=0;i<it->size-1;i++)
635         {
636             fprintf(code,"0x%02x, ",it->res[i]);
637             if ((i&7)==7)fputc('\n',code);
638         }
639         fprintf(code,"0x%02x };\n\n",it->res[i]);
640     }
641
642     /* Print the resources */
643     for (it=top;it;it=it->next)
644     {
645         int type;
646         switch(it->type)
647         {
648         case acc:type=(int)RT_ACCELERATOR;break;
649         case bmp:type=(int)RT_BITMAP;break;
650         case cur:type=(int)RT_CURSOR;break;
651         case dlg:type=(int)RT_DIALOG;break;
652         case fnt:type=(int)RT_FONT;break;
653         case ico:type=(int)RT_ICON;break;
654         case men:type=(int)RT_MENU;break;
655         case rdt:type=(int)RT_RCDATA;break;
656         case str:type=(int)RT_STRING;break;
657         default:fprintf(stderr,"Unknown restype\n");type=-1;break;
658         }
659         if(it->n_type)
660             fprintf(code,"%sstruct resource %s = {0,%d,\"%s\",%s__bytes,%d};\n",
661                     ISCONSTANT, get_resource_name(it), type, it->n.s_name,
662                     get_resource_name(it), it->size );
663         else
664             fprintf(code,"%sstruct resource %s = {%d,%d,\"@%d\",%s__bytes,%d};\n",
665                     ISCONSTANT, get_resource_name(it), it->n.i_name, type,
666                     it->n.i_name, get_resource_name(it), it->size );
667     }
668
669     /* Print the resource table (NULL terminated) */
670
671     fprintf(code,"\n%sstruct resource * %s%s_Table[] = {\n",
672             ISCONSTANT, ISCONSTANT, prefix);
673     for (it=top;it;it=it->next)
674         fprintf( code, "  &%s,\n", get_resource_name(it) );
675     fprintf( code, "  0\n};\n" );
676
677         /* Perform autoregistration */
678         fprintf( code, 
679                 "#ifdef WINELIB\n"
680                 "static void DoIt() WINE_CONSTRUCTOR;\n"
681                 "static void DoIt()\n"
682                 "{\n"
683                 "\tLIBRES_RegisterResources(%s_Table);\n"
684                 "}\n\n"
685                 "#ifndef HAVE_WINE_CONSTRUCTOR\n"
686                 "void LIBWINE_Register_%s(){\n"
687                 "\tDoIt();\n"
688                 "}\n"
689                 "#endif\n"
690                 "#endif /*WINELIB*/\n"
691                 ,prefix,prefix);
692 }
693
694 gen_res* make_font(gen_res* res)
695 {
696         fprintf(stderr,"Fonts not supported\n");
697         return NULL;
698 }
699
700 gen_res* make_raw(gen_res* res)
701 {
702         fprintf(stderr,"RCData not supported\n");
703         return NULL;
704 }
705
706 gen_res* int_to_raw(int i,gen_res* res)
707 {
708         fprintf(stderr,"IntToRaw not supported\n");
709         return NULL;
710 }
711
712 /* translate "Hello,\\tworld!\\10" to "Hello,\tworld!\n" */
713 char *parse_c_string(char *in)
714 {
715         char *out=xmalloc(strlen(in)-1);
716         char *it;
717         char tmp[5],*tend;
718         for(it=out,in++;*in;in++)
719         if(*in=='\\')
720                 switch(*++in)
721                 {case 't':*it++='\t';break;
722                  case 'r':*it++='\r';break;
723                  case 'n':*it++='\n';break;
724                  case 'a':*it++='\a';break;
725                  case '0':
726                         memset(tmp,0,5);/*make sure it doesn't use more than 4 chars*/
727                         memcpy(tmp,in,4);
728                         *it++=strtoul(tmp,&tend,0);
729                         in+=tend-tmp-1;
730                         break;
731                  case '1':case '2':case '3':case '4':case '5':
732                  case '6':case '7':case '8':case '9':
733                         memset(tmp,0,5);
734                         memcpy(tmp,in,3);
735                         *it++=strtoul(tmp,&tend,10);
736                         in+=tend-tmp-1;
737                         break;
738                  case 'x':
739                         memset(tmp,0,5);
740                         memcpy(tmp,++in,2);
741                         *it++=strtoul(tmp,&tend,16);
742                         in+=tend-tmp-1;
743                         break;
744                  default:*it++=*in;
745                 }
746         else
747                 *it++=*in;
748         *(it-1)='\0';
749         return out;
750 }