| /* |
| * |
| * Copyright Martin von Loewis, 1994 |
| * |
| */ |
| |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <sys/fcntl.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <windows.h> |
| /* #include <neexe.h> */ |
| #include "parser.h" |
| #include "y.tab.h" |
| |
| char usage[]="winerc -bdvc -p prefix -o outfile < infile \n" |
| " -b Create a C array from a binary .res file\n" |
| " -c Add 'const' prefix to C constants\n" |
| " -d Output debugging information\n" |
| " -p prefix Give a prefix for the generated names\n" |
| " -v Show each resource as it is processed\n" |
| " -o file Output to file.c and file.h\n"; |
| |
| |
| /*might be overwritten by command line*/ |
| char *prefix="_Resource"; |
| int verbose,constant; |
| gen_res* g_start; |
| FILE *header,*code; |
| char hname[256],sname[256]; |
| |
| int transform_binary_file(void); |
| int yyparse(void); |
| |
| static void *xmalloc (size_t size) |
| { |
| void *res; |
| |
| res = malloc (size ? size : 1); |
| if (res == NULL) |
| { |
| fprintf (stderr, "Virtual memory exhausted.\n"); |
| exit (1); |
| } |
| return res; |
| } |
| |
| |
| int main(int argc,char *argv[]) |
| { |
| extern int yydebug; |
| extern char* optarg; |
| char* tmpc; |
| int optc,lose,ret,binary; |
| lose=binary=0; |
| while((optc=getopt(argc,argv,"bcdp:vo:"))!=EOF) |
| switch(optc) |
| { |
| /* bison will print state transitions on stderr */ |
| case 'b':binary=1; |
| break; |
| case 'd':yydebug=1; |
| setbuf(stdout,0); |
| setbuf(stderr,0); |
| break; |
| case 'p':prefix=strdup(optarg); |
| if(!isalpha(*prefix))*prefix='_'; |
| for(tmpc=prefix;*tmpc;tmpc++) |
| if( !isalnum(*tmpc) && *tmpc!='_') |
| *tmpc='_'; |
| break; |
| case 'c':constant=1;break; |
| case 'v':verbose=1; |
| setbuf(stderr,0); |
| break; |
| case 'o':set_out_file(optarg);break; |
| default: lose++;break; |
| } |
| if(lose)return fprintf(stderr,usage),1; |
| if(!header)header=stdout; |
| if(!code)code=stdout; |
| if(binary) |
| ret=transform_binary_file(); |
| else |
| ret=yyparse(); |
| fclose(header); |
| fclose(code); |
| return ret; |
| } |
| |
| void set_out_file(char *prefix) |
| { |
| sprintf(sname,"%s.c",prefix); |
| code=fopen(sname,"w"); |
| sprintf(hname,"%s.h",prefix); |
| header=fopen(hname,"w"); |
| } |
| |
| int transform_binary_file() |
| { |
| int i,c; |
| fprintf(header,"#define APPLICATION_HAS_RESOURCES 1\n"); |
| fprintf(code,"char _Application_resources[]={"); |
| for(i=0;;i++) |
| { |
| c=getchar(); |
| if(c==-1)break; |
| if(i%16==0)fputc('\n',code); |
| fprintf(code,"%3d,",c); |
| } |
| fprintf(code,"\n 0};\nint _Application_resources_size=%d;\n",i); |
| return 0; |
| } |
| |
| /* SunOS' memcpy is wrong for overlapping arrays */ |
| char *save_memcpy(char *d,char* s,int l) |
| { |
| if(d<s) |
| for(;l;l--)*d++=*s++; |
| else |
| for(d+=l-1,s+=l-1;l;l--)*d--=*s--; |
| return d; |
| } |
| |
| /*allow unaligned access*/ |
| void put_WORD(unsigned char* p,WORD w) |
| { |
| *p=w&0xFF; |
| *(p+1)=w>>8; |
| } |
| |
| void put_DWORD(unsigned char* p,DWORD d) |
| { |
| put_WORD(p,d&0xFFFF); |
| put_WORD(p+2,d>>16); |
| } |
| |
| WORD get_WORD(unsigned char* p) |
| { |
| return *p|(*(p+1)<<8); |
| } |
| |
| DWORD get_DWORD(unsigned char* p) |
| { |
| return get_WORD(p)|(get_WORD(p+2)<<16); |
| } |
| |
| |
| /*create a new gen_res, initial size 100*/ |
| gen_res *new_res() |
| { gen_res* ret=xmalloc(sizeof(gen_res)+100); |
| int i; |
| for(i=0;i<sizeof(gen_res)+100;i++)*((char*)ret+i)='\0'; |
| ret->g_next=g_start; |
| ret->g_prev=0; |
| g_start=ret; |
| ret->space=100; |
| return ret; |
| } |
| |
| /*double the space*/ |
| gen_res* grow(gen_res* res) |
| { |
| res=realloc(res,sizeof(gen_res)+2*res->space); |
| if(!res) |
| fprintf(stderr,"Out of memory\n"),exit(1); |
| if(!res->g_prev)g_start=res; |
| else res->g_prev->g_next=res; |
| if(res->g_next)res->g_next->g_prev=res; |
| res->space=2*res->space; |
| return res; |
| } |
| |
| |
| /* insert bytes at offset 0, increase num_entries */ |
| gen_res* insert_at_beginning(gen_res* res,char* entry,int size) |
| { |
| while(res->size+size>res->space)res=grow(res); |
| save_memcpy(res->res+size,res->res,res->size); |
| save_memcpy(res->res,entry,size); |
| res->size+=size; |
| res->num_entries++; |
| return res; |
| } |
| |
| /* insert length characters from bytes into res, starting at start */ |
| gen_res* insert_bytes(gen_res* res,char* bytes,int start,int length) |
| { |
| while(res->size+length>res->space)res=grow(res); |
| save_memcpy(res->res+start+length,res->res+start,res->size-start); |
| save_memcpy(res->res+start,bytes,length); |
| res->size+=length; |
| return res; |
| } |
| |
| /*delete len bytes from res, starting at start*/ |
| gen_res* delete_bytes(gen_res* res,int start,int len) |
| { |
| save_memcpy(res->res+start,res->res+start+len,res->size-start-len); |
| res->size-=len; |
| return res; |
| } |
| |
| /*create a new style*/ |
| rc_style *new_style() |
| { |
| rc_style *ret=xmalloc(sizeof(rc_style)); |
| /*initially, no bits have to be reset*/ |
| ret->and=-1; |
| /*initially, no bits are set*/ |
| ret->or=WS_CHILD | WS_VISIBLE; |
| return ret; |
| } |
| |
| /* entries are inserted at the beginning, starting from the last one */ |
| gen_res* add_accelerator(int ev, int id, int flags, gen_res* prev) |
| { |
| char accel_entry[5]; |
| if(prev->num_entries==0)flags|=0x80; /* last entry */ |
| accel_entry[0]=flags; |
| put_WORD(accel_entry+1,ev); |
| put_WORD(accel_entry+3,id); |
| return insert_at_beginning(prev,accel_entry,5); |
| } |
| |
| |
| /* create an integer from the event, taking things as "^c" into account |
| add this as new entry */ |
| gen_res* add_string_accelerator(char *ev, int id, int flags, gen_res* prev) |
| { |
| int event; |
| if(*ev=='^') |
| event=ev[1]-'a'; |
| else |
| event=ev[0]; |
| return add_accelerator(event,id,flags,prev); |
| } |
| |
| /*is there a difference between ASCII and VIRTKEY accelerators? */ |
| |
| gen_res* add_ascii_accelerator(int ev, int id, int flags, gen_res* prev) |
| { |
| return add_accelerator(ev,id,flags,prev); |
| } |
| |
| gen_res* add_vk_accelerator(int ev, int id, int flags, gen_res* prev) |
| { |
| return add_accelerator(ev,id,flags,prev); |
| } |
| |
| /* create a new dialog header, set all items to 0 */ |
| gen_res* new_dialog() |
| { gen_res* ret=new_res(); |
| ret->size=16; /*all strings "\0", no font*/ |
| return ret; |
| } |
| |
| /* the STYLE option was specified */ |
| gen_res* dialog_style(rc_style* style, gen_res* attr) |
| { |
| /* default dialog styles? Do we need style->and? */ |
| /* DS_SETFONT might have been specified before */ |
| put_DWORD(attr->res,get_DWORD(attr->res)|style->or); |
| return attr; |
| } |
| |
| /* menu name is at offset 13 */ |
| int dialog_get_menu(gen_res* attr) |
| { |
| return 13; |
| } |
| |
| /* the class is after the menu name */ |
| int dialog_get_class(gen_res* attr) |
| { |
| int offs=dialog_get_menu(attr); |
| while(attr->res[offs])offs++; |
| offs++; |
| return offs; |
| } |
| |
| /* the caption is after the class */ |
| int dialog_get_caption(gen_res* attr) |
| { |
| int offs=dialog_get_class(attr); |
| while(attr->res[offs])offs++; |
| offs++; |
| return offs; |
| } |
| |
| /* the fontsize, if present, is after the caption, followed by the font name */ |
| int dialog_get_fontsize(gen_res* attr) |
| { |
| int offs=dialog_get_caption(attr); |
| while(attr->res[offs])offs++; |
| offs++; |
| return offs; |
| } |
| |
| |
| /* the CAPTION option was specified */ |
| gen_res* dialog_caption(char* cap, gen_res*attr) |
| { |
| /* we don't need the terminating 0 as it's already there */ |
| return insert_bytes(attr,cap,dialog_get_caption(attr),strlen(cap)); |
| } |
| |
| |
| /* the FONT option was specified, set the DS_SETFONT flag */ |
| gen_res* dialog_font(short size,char* font,gen_res *attr) |
| { |
| char c_size[2]; |
| int offs=dialog_get_fontsize(attr); |
| put_DWORD(attr->res,get_DWORD(attr->res)|DS_SETFONT); |
| put_WORD(c_size,size); |
| attr=insert_bytes(attr,c_size,offs,2); |
| offs+=2; |
| /* as there is no font name by default, copy the '\0' */ |
| return insert_bytes(attr,font,offs,strlen(font)+1); |
| } |
| |
| gen_res* dialog_class(char* cap, gen_res*attr) |
| { |
| return insert_bytes(attr,cap,dialog_get_class(attr),strlen(cap)); |
| } |
| |
| gen_res* dialog_menu(char* cap, gen_res*attr) |
| { |
| return insert_bytes(attr,cap,dialog_get_menu(attr),strlen(cap)); |
| } |
| |
| /* set the dialogs id, position, extent, and style */ |
| gen_res* create_control_desc(int id,int x,int y,int cx, int cy,rc_style *style) |
| { gen_res* ret=new_res(); |
| int s=WS_VISIBLE|WS_CHILD; /*defaults styles for any control*/ |
| put_WORD(ret->res+0,x); |
| put_WORD(ret->res+2,y); |
| put_WORD(ret->res+4,cx); |
| put_WORD(ret->res+6,cy); |
| put_WORD(ret->res+8,id); |
| if(style)s=(s|style->or)&style->and; |
| put_DWORD(ret->res+10,s); |
| ret->size=17; /*empty strings, unused byte*/ |
| return ret; |
| } |
| |
| /* insert the control's label */ |
| gen_res* label_control_desc(char* label,gen_res* cd) |
| { |
| int offs; |
| if(cd->res[14]&0x80)offs=15; /* one-character class */ |
| else { |
| for(offs=14;cd->res[offs];offs++); |
| offs++; |
| } |
| return insert_bytes(cd,label,offs,strlen(label)); |
| } |
| |
| /* a CONTROL was specified */ |
| gen_res* create_generic_control(char* label,int id,char* class, |
| rc_style*style,int x,int y,int cx,int cy) |
| { char cl; |
| gen_res* ret=new_res(); |
| put_WORD(ret->res+0,x); |
| put_WORD(ret->res+2,y); |
| put_WORD(ret->res+4,cx); |
| put_WORD(ret->res+6,cy); |
| put_WORD(ret->res+8,id); |
| put_DWORD(ret->res+10,style->or); |
| ret->size=17; |
| ret=insert_bytes(ret,label,15,strlen(label)); |
| /* is it a predefined class? */ |
| cl=0; |
| if(!strcmp(class,"BUTTON"))cl=CT_BUTTON; |
| if(!strcmp(class,"EDIT"))cl=CT_EDIT; |
| if(!strcmp(class,"STATIC"))cl=CT_STATIC; |
| if(!strcmp(class,"LISTBOX"))cl=CT_LISTBOX; |
| if(!strcmp(class,"SCROLLBAR"))cl=CT_SCROLLBAR; |
| if(!strcmp(class,"COMBOBOX"))cl=CT_COMBOBOX; |
| if(cl)ret->res[14]=cl; |
| else ret=insert_bytes(ret,class,14,strlen(class)); |
| return ret; |
| } |
| |
| /* insert cd into rest, set the type, add flags */ |
| gen_res* add_control(int type,int flags,gen_res*cd,gen_res* rest) |
| { |
| put_DWORD(cd->res+10,get_DWORD(cd->res+10)|flags); |
| cd->res[14]=type; |
| return insert_at_beginning(rest,cd->res,cd->size); |
| } |
| |
| /* an ICON control was specified, whf contains width, height, and flags */ |
| gen_res* add_icon(char* name,int id,int x,int y,gen_res* whf,gen_res* rest) |
| { |
| put_WORD(whf->res+0,x); |
| put_WORD(whf->res+2,y); |
| put_WORD(whf->res+8,id); |
| whf=label_control_desc(name,whf); |
| return add_control(CT_STATIC,SS_ICON,whf,rest); |
| } |
| |
| /* insert the generic control into rest */ |
| gen_res* add_generic_control(gen_res* ctl, gen_res* rest) |
| { |
| return insert_at_beginning(rest,ctl->res,ctl->size); |
| } |
| |
| /* create a dialog resource by inserting the header into the controls. |
| Set position and extent */ |
| gen_res* make_dialog(gen_res* header,int x,int y,int cx,int cy,gen_res* ctls) |
| { |
| header->res[4]=ctls->num_entries; |
| header->type=dlg; |
| put_WORD(header->res+5,x); |
| put_WORD(header->res+7,y); |
| put_WORD(header->res+9,cx); |
| put_WORD(header->res+11,cy); |
| return insert_bytes(header,ctls->res,header->size,ctls->size); |
| } |
| |
| /* create {0x15,0x16,0xFF} from '15 16 FF' */ |
| gen_res *hex_to_raw(char *hex, gen_res*rest) |
| { |
| char r2[16]; |
| int i; |
| for(i=0;*hex!='\'';i++)r2[i]=strtoul(hex,&hex,16); |
| return insert_bytes(rest,r2,0,i); |
| } |
| |
| /* create a bitmap resource */ |
| gen_res *make_bitmap(gen_res* res) |
| { |
| res=delete_bytes(res,0,14); /* skip bitmap file header*/ |
| res->type=bmp; |
| return res; |
| } |
| |
| gen_res *make_icon(gen_res* res) |
| { |
| res->type=ico; |
| return res; |
| } |
| |
| gen_res *make_cursor(gen_res* res) |
| { |
| res->type=cur; |
| return res; |
| } |
| |
| /* load resource bytes from the file name */ |
| gen_res *load_file(char* name) |
| { |
| gen_res *res; |
| struct stat st; |
| int f=open(name,O_RDONLY); |
| if(!f)perror(name); |
| fstat(f,&st); |
| res=new_res(); |
| while(res->space<st.st_size)res=grow(res); |
| read(f,res->res,st.st_size); |
| res->size=st.st_size; |
| close(f); |
| return res; |
| } |
| |
| /* insert a normal menu item into res, starting from the last item */ |
| gen_res *add_menuitem(char* name,int id,int flags,gen_res *res) |
| { |
| char item[4]; |
| if(res->num_entries==0)flags|=MF_END; |
| put_WORD(item,flags); |
| put_WORD(item+2,id); |
| res=insert_at_beginning(res,name,strlen(name)+1); |
| res=insert_bytes(res,item,0,4); |
| return res; |
| } |
| |
| /* insert a popup item into res */ |
| gen_res *add_popup(char *name,short flags, gen_res* body, gen_res*res) |
| { |
| char c_flags[2]; |
| flags|=MF_POPUP; |
| if(res->num_entries==0)flags|=MF_END; |
| put_WORD(c_flags,flags); |
| res=insert_at_beginning(res,body->res,body->size); |
| res=insert_bytes(res,name,0,strlen(name)+1); |
| res=insert_bytes(res,c_flags,0,2); |
| return res; |
| } |
| |
| /* prefix the menu header into res */ |
| gen_res *make_menu(gen_res* res) |
| { |
| static char header[4]={0,0,0,0}; |
| res=insert_at_beginning(res,header,4); |
| res->type=men; |
| return res; |
| } |
| |
| /* link top-level resources */ |
| gen_res *add_resource(gen_res* first,gen_res *rest) |
| { |
| if(first) |
| { |
| first->next=rest; |
| return first; |
| } |
| else |
| return rest; |
| } |
| |
| typedef struct str_tbl_elm{ |
| int group; |
| struct str_tbl_elm *next; |
| char* strings[16]; |
| } str_tbl_elm; |
| |
| str_tbl_elm* string_table=NULL; /* sorted by group */ |
| |
| void add_str_tbl_elm(int id,char* str) |
| { |
| int group=(id>>4)+1; |
| int idx=id & 0x000f; |
| |
| str_tbl_elm** elm=&string_table; |
| while(*elm && (*elm)->group<group) elm=&(*elm)->next; |
| if(!*elm || (*elm)->group!=group) |
| { |
| str_tbl_elm* new=xmalloc(sizeof(str_tbl_elm)); |
| new->group=group; |
| new->next=*elm; |
| *elm=new; |
| } |
| (*elm)->strings[idx]=str; |
| } |
| |
| gen_res* add_string_table(gen_res* t) |
| { |
| str_tbl_elm* ste; |
| int size,i; |
| gen_res* res; |
| unsigned char* p; |
| char* q; |
| |
| if(!string_table) return t; |
| for(ste=string_table; ste; ste=ste->next) |
| { |
| for(size=0,i=0; i<16; i++) |
| size += ste->strings[i] ? strlen(ste->strings[i])+1 : 1; |
| res=new_res(); |
| while(res->space<size)res=grow(res); |
| res->type=str; |
| res->n.i_name=ste->group; |
| res->n_type=0; |
| res->size=size; |
| for(p=res->res,i=0; i<16; i++) |
| if((q=ste->strings[i])==NULL) |
| *p++ = 0; |
| else |
| { |
| *p++ = strlen(q); |
| while(*q) *p++ = *q++; |
| } |
| t=add_resource(res,t); |
| } |
| return t; |
| } |
| |
| char *get_typename(gen_res* t) |
| { |
| switch(t->type){ |
| case acc:return "ACCELERATOR"; |
| case bmp:return "BITMAP"; |
| case cur:return "CURSOR"; |
| case dlg:return "DIALOG"; |
| case fnt:return "FONT"; |
| case ico:return "ICON"; |
| case men:return "MENU"; |
| case rdt:return "RCDATA"; |
| case str:return "STRINGTABLE"; |
| default: return "UNKNOWN"; |
| } |
| } |
| |
| /* create strings like _Sysres_DIALOG_2 */ |
| char *get_resource_name(gen_res*it) |
| { |
| static char buf[1000]; |
| if(it->n_type) |
| sprintf(buf,"%s_%s_%s",prefix,get_typename(it),it->n.s_name); |
| else |
| sprintf(buf,"%s_%s_%d",prefix,get_typename(it),it->n.i_name); |
| return buf; |
| } |
| |
| #define ISCONSTANT (constant ? "const " : "") |
| |
| /* create the final output */ |
| void create_output(gen_res* top) |
| { |
| gen_res *it; |
| |
| top=add_string_table(top); |
| |
| fprintf( header, "/* %s\n" |
| " * This file is automatically generated. Do not edit!\n" |
| " */\n\n" |
| "#include \"resource.h\"\n", hname ); |
| |
| /* Declare the resources */ |
| |
| for (it=top;it;it=it->next) |
| fprintf( header,"extern %sstruct resource %s;\n", |
| ISCONSTANT, get_resource_name(it) ); |
| fprintf( header,"\nextern %sstruct resource * %s%s_Table[];\n", |
| ISCONSTANT, ISCONSTANT, prefix ); |
| |
| /* Print the resources bytes */ |
| |
| fprintf( code, "/* %s\n" |
| " * This file is automatically generated. Do not edit!\n" |
| " */\n\n" |
| "#include \"%s\"\n", sname, hname ); |
| |
| for(it=top;it;it=it->next) |
| { |
| int i; |
| fprintf( code, "static %sunsigned char %s__bytes[] = {\n", |
| ISCONSTANT, get_resource_name(it) ); |
| for (i=0;i<it->size-1;i++) |
| { |
| fprintf(code,"0x%02x, ",it->res[i]); |
| if ((i&7)==7)fputc('\n',code); |
| } |
| fprintf(code,"0x%02x };\n\n",it->res[i]); |
| } |
| |
| /* Print the resources */ |
| for (it=top;it;it=it->next) |
| { |
| int type; |
| switch(it->type) |
| { |
| case acc:type=(int)RT_ACCELERATOR;break; |
| case bmp:type=(int)RT_BITMAP;break; |
| case cur:type=(int)RT_CURSOR;break; |
| case dlg:type=(int)RT_DIALOG;break; |
| case fnt:type=(int)RT_FONT;break; |
| case ico:type=(int)RT_ICON;break; |
| case men:type=(int)RT_MENU;break; |
| case rdt:type=(int)RT_RCDATA;break; |
| case str:type=(int)RT_STRING;break; |
| default:fprintf(stderr,"Unknown restype\n");type=-1;break; |
| } |
| if(it->n_type) |
| fprintf(code,"%sstruct resource %s = {0,%d,\"%s\",%s__bytes,%d};\n", |
| ISCONSTANT, get_resource_name(it), type, it->n.s_name, |
| get_resource_name(it), it->size ); |
| else |
| fprintf(code,"%sstruct resource %s = {%d,%d,\"@%d\",%s__bytes,%d};\n", |
| ISCONSTANT, get_resource_name(it), it->n.i_name, type, |
| it->n.i_name, get_resource_name(it), it->size ); |
| } |
| |
| /* Print the resource table (NULL terminated) */ |
| |
| fprintf(code,"\n%sstruct resource * %s%s_Table[] = {\n", |
| ISCONSTANT, ISCONSTANT, prefix); |
| for (it=top;it;it=it->next) |
| fprintf( code, " &%s,\n", get_resource_name(it) ); |
| fprintf( code, " 0\n};\n" ); |
| |
| /* Perform autoregistration */ |
| fprintf( code, |
| "#ifdef WINELIB\n" |
| "static void DoIt() WINE_CONSTRUCTOR;\n" |
| "static void DoIt()\n" |
| "{\n" |
| "\tLIBRES_RegisterResources(%s_Table);\n" |
| "}\n\n" |
| "#ifndef HAVE_WINE_CONSTRUCTOR\n" |
| "void LIBWINE_Register_%s(){\n" |
| "\tDoIt();\n" |
| "}\n" |
| "#endif\n" |
| "#endif /*WINELIB*/\n" |
| ,prefix,prefix); |
| } |
| |
| gen_res* make_font(gen_res* res) |
| { |
| fprintf(stderr,"Fonts not supported\n"); |
| return NULL; |
| } |
| |
| gen_res* make_raw(gen_res* res) |
| { |
| fprintf(stderr,"RCData not supported\n"); |
| return NULL; |
| } |
| |
| gen_res* int_to_raw(int i,gen_res* res) |
| { |
| fprintf(stderr,"IntToRaw not supported\n"); |
| return NULL; |
| } |
| |
| /* translate "Hello,\\tworld!\\10" to "Hello,\tworld!\n" */ |
| char *parse_c_string(char *in) |
| { |
| char *out=xmalloc(strlen(in)-1); |
| char *it; |
| char tmp[5],*tend; |
| for(it=out,in++;*in;in++) |
| if(*in=='\\') |
| switch(*++in) |
| {case 't':*it++='\t';break; |
| case 'r':*it++='\r';break; |
| case 'n':*it++='\n';break; |
| case 'a':*it++='\a';break; |
| case '0': |
| memset(tmp,0,5);/*make sure it doesn't use more than 4 chars*/ |
| memcpy(tmp,in,4); |
| *it++=strtoul(tmp,&tend,0); |
| in+=tend-tmp-1; |
| break; |
| case '1':case '2':case '3':case '4':case '5': |
| case '6':case '7':case '8':case '9': |
| memset(tmp,0,5); |
| memcpy(tmp,in,3); |
| *it++=strtoul(tmp,&tend,10); |
| in+=tend-tmp-1; |
| break; |
| case 'x': |
| memset(tmp,0,5); |
| memcpy(tmp,++in,2); |
| *it++=strtoul(tmp,&tend,16); |
| in+=tend-tmp-1; |
| break; |
| default:*it++=*in; |
| } |
| else |
| *it++=*in; |
| *(it-1)='\0'; |
| return out; |
| } |