Compare commits

...

18 Commits

Author SHA1 Message Date
Anthony J. Bentley
c1213f536b Merge branch 'master' of https://github.com/stag019/rgbds 2014-11-04 17:36:45 -07:00
Anthony J. Bentley
82de716454 Mark error functions as _Noreturn. 2014-11-04 17:30:00 -07:00
stag019
a64d725a8d The actual way the macro bug should have been fixed. 2014-11-04 18:09:22 -05:00
stag019
80e2129f22 Merge https://github.com/bentley/rgbds
Conflicts:
	include/lib/types.h
	src/asm/symbol.c
2014-11-02 01:00:20 -05:00
stag019
af70f03933 A few more small changes to charmap.c. 2014-10-31 19:01:21 -04:00
stag019
004bc2e50e Fix a few charmap bugs maybe? 2014-10-31 10:48:54 -04:00
Christophe Staïesse
25efb00769 fix a bug in the lexer involving double quote escaping and semicolons
The bug showed up when a semicolon was located anywhere after \".

These three test cases are syntaxically correct but didn't compile:

1)
SECTION "HOME", HOME
	db "\";"

2)
SECTION "HOME", HOME
	db "\""
	nop
	;

3)
SECTION "HOME", HOME
	db "\"" ;

The problem was located in yy_create_buffer(). Basicaly, this function loads an
entire source file, uniformizes EOL terminators and filters out comments without
touching literal strings.

However, bounds of literal strings were wrongly guessed because \" was
interpreted as two characters (and so the double quote was not escaped).

In test 1, the string terminates early and so ;" is filtered out as it was a
comment and so the assembler complains of an unterminated string.
In test 2 and 3, the string is in fact interpreted as two strings, the second
one terminates at EOF in these cases and so comments are not filtered out and
that makes the assembler complains.

A special case must be taken into account:

4)
SECTION "HOME", HOME
	db "\\" ;

So we need to ignore \\ as well.

Note that there is still a problem left: in yy_create_buffer() a string may
span multiple lines but not in the lexer. However in this case I think the lexer
would quit at the first newline so there should be nothing to worry about.
2014-10-10 16:50:11 +02:00
stag019
af506985e5 Added license to charmap.c 2014-02-03 20:46:52 -05:00
stag019
240d2a7f14 Merge https://github.com/bentley/rgbds 2014-01-29 00:18:10 -05:00
stag019
34656f9e5d Fix a bug where the first charmap entry wasn't added correctly. 2013-12-28 00:35:05 -05:00
stag019
c61c112218 Remove GNU-specific <getopt.h>. getopt() is defined in <unistd.h> in POSIX, which adding #define _XOPEN_SOURCE 500 causes GCC to include. 2013-12-23 14:57:06 -05:00
stag019
55974bc743 Only define _MAX_PATH is it isn't already defined. 2013-12-23 14:52:37 -05:00
stag019
94005513a4 Comment out unused variable dest. 2013-12-23 14:50:37 -05:00
stag019
36edec6231 Add out_BinaryFileSlice() definition to output.h. 2013-12-23 14:47:37 -05:00
stag019
7d176245d8 Remove all implicit definitions of compiler provided functions.
<strings.h> cause strncasecmp to be define.
2013-12-23 14:40:53 -05:00
stag019
c65d58c589 Move local includes below system includes. 2013-12-23 14:37:56 -05:00
stag019
1f9fd0f060 This fixes an error with using long label names in macros. If the label name you're using is longer than the string length of the literal macro text, a syntax error would occur. This fix makes sure it at least allocates enough bytes for the largest allowed label name. 2013-12-22 20:56:31 -05:00
stag019
1218da79a9 Character maps. 2013-12-22 20:55:14 -05:00
18 changed files with 309 additions and 21 deletions

View File

@@ -16,6 +16,7 @@ yacc_pre := \
rgbasm_obj := \ rgbasm_obj := \
src/asm/asmy.o \ src/asm/asmy.o \
src/asm/charmap.o \
src/asm/fstack.o \ src/asm/fstack.o \
src/asm/globlex.o \ src/asm/globlex.o \
src/asm/lexer.o \ src/asm/lexer.o \
@@ -93,7 +94,8 @@ src/asm/asmy.y: ${yacc_pre}
mingw: mingw:
$Qenv PATH=/usr/local/mingw32/bin:/bin:/usr/bin:/usr/local/bin \ $Qenv PATH=/usr/local/mingw32/bin:/bin:/usr/bin:/usr/local/bin \
make CC=gcc CFLAGS="-I/usr/local/mingw32/include \ make CC=gcc CFLAGS="-I/usr/local/mingw32/include \
-D__progname=\\\"\\\" ${CFLAGS}" -D__progname=\\\"\\\" \
-D_Noreturn='__attribute__((noreturn))' ${CFLAGS}"
$Qmv rgbasm rgbasm.exe $Qmv rgbasm rgbasm.exe
$Qmv rgblink rgblink.exe $Qmv rgblink rgblink.exe
$Qmv rgbfix rgbfix.exe $Qmv rgbfix rgbfix.exe

18
include/asm/charmap.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef ASMOTOR_ASM_CHARMAP_H
#define ASMOTOR_ASM_CHARMAP_H
#define MAXCHARMAPS 512
#define CHARMAPLENGTH 8
struct Charmap {
int count;
char input[MAXCHARMAPS][CHARMAPLENGTH + 1];
char output[MAXCHARMAPS];
};
int readUTF8Char(char *destination, char *source);
void charmap_Sort();
int charmap_Add(char *input, UBYTE output);
int charmap_Convert(char **input);
#endif

View File

@@ -12,6 +12,7 @@ struct Section {
ULONG nBank; ULONG nBank;
struct Section *pNext; struct Section *pNext;
struct Patch *pPatches; struct Patch *pPatches;
struct Charmap *charmap;
UBYTE *tData; UBYTE *tData;
}; };
@@ -20,12 +21,14 @@ void out_SetFileName(char *s);
void out_NewSection(char *pzName, ULONG secttype); void out_NewSection(char *pzName, ULONG secttype);
void out_NewAbsSection(char *pzName, ULONG secttype, SLONG org, SLONG bank); void out_NewAbsSection(char *pzName, ULONG secttype, SLONG org, SLONG bank);
void out_AbsByte(int b); void out_AbsByte(int b);
void out_AbsByteGroup(char *s, int length);
void out_RelByte(struct Expression * expr); void out_RelByte(struct Expression * expr);
void out_RelWord(struct Expression * expr); void out_RelWord(struct Expression * expr);
void out_PCRelByte(struct Expression * expr); void out_PCRelByte(struct Expression * expr);
void out_WriteObject(void); void out_WriteObject(void);
void out_Skip(int skip); void out_Skip(int skip);
void out_BinaryFile(char *s); void out_BinaryFile(char *s);
void out_BinaryFileSlice(char *s, SLONG start_pos, SLONG length);
void out_String(char *s); void out_String(char *s);
void out_AbsLong(SLONG b); void out_AbsLong(SLONG b);
void out_RelLong(struct Expression * expr); void out_RelLong(struct Expression * expr);

View File

@@ -1,7 +1,9 @@
#ifndef ASMOTOR_ASM_TYPES_H #ifndef ASMOTOR_ASM_TYPES_H
#define ASMOTOR_ASM_TYPES_H #define ASMOTOR_ASM_TYPES_H
#ifndef _MAX_PATH
#define _MAX_PATH 512 #define _MAX_PATH 512
#endif
typedef unsigned char UBYTE; typedef unsigned char UBYTE;
typedef signed char SBYTE; typedef signed char SBYTE;

View File

@@ -1,7 +1,9 @@
#ifndef ASMOTOR_LINK_LINK_H #ifndef ASMOTOR_LINK_LINK_H
#define ASMOTOR_LINK_LINK_H #define ASMOTOR_LINK_LINK_H
#ifndef _MAX_PATH
#define _MAX_PATH 512 #define _MAX_PATH 512
#endif
#include "link/types.h" #include "link/types.h"

View File

@@ -1,7 +1,9 @@
#ifndef ASMOTOR_LINK_TYPES_H #ifndef ASMOTOR_LINK_TYPES_H
#define ASMOTOR_LINK_TYPES_H #define ASMOTOR_LINK_TYPES_H
#ifndef _MAX_PATH
#define _MAX_PATH 512 #define _MAX_PATH 512
#endif
typedef unsigned char UBYTE; typedef unsigned char UBYTE;
typedef signed char SBYTE; typedef signed char SBYTE;

204
src/asm/charmap.c Normal file
View File

@@ -0,0 +1,204 @@
/*
* Copyright © 2013 stag019 <stag019@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "asm/asm.h"
#include "asm/charmap.h"
#include "asm/main.h"
#include "asm/output.h"
struct Charmap globalCharmap = {0};
extern struct Section *pCurrentSection;
int
readUTF8Char(char *destination, char *source)
{
int size;
UBYTE first;
first = source[0];
if(first >= 0xFC)
{
size = 6;
}
else if(first >= 0xF8)
{
size = 5;
}
else if(first >= 0xF0)
{
size = 4;
}
else if(first >= 0xE0)
{
size = 3;
}
else if(first >= 0xC0)
{
size = 2;
}
else if(first != '\0')
{
size = 1;
}
else
{
size = 0;
}
strncpy(destination, source, size);
destination[size] = 0;
return size;
}
int
charmap_Add(char *input, UBYTE output)
{
int i, input_length;
char temp1i[CHARMAPLENGTH + 1], temp2i[CHARMAPLENGTH + 1], temp1o = 0, temp2o = 0;
struct Charmap *charmap;
if(pCurrentSection)
{
if(pCurrentSection -> charmap)
{
charmap = pCurrentSection -> charmap;
}
else
{
if((charmap = (struct Charmap *) calloc(1, sizeof(struct Charmap))) == NULL)
{
fatalerror("Not enough memory for charmap");
}
pCurrentSection -> charmap = charmap;
}
}
else
{
charmap = &globalCharmap;
}
if(nPass == 2)
{
return charmap -> count;
}
if(charmap -> count > MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
{
return -1;
}
input_length = strlen(input);
if(input_length > 1)
{
i = 0;
while(i < charmap -> count + 1)
{
if(input_length > strlen(charmap -> input[i]))
{
memcpy(temp1i, charmap -> input[i], CHARMAPLENGTH + 1);
memcpy(charmap -> input[i], input, input_length);
temp1o = charmap -> output[i];
charmap -> output[i] = output;
i++;
break;
}
i++;
}
while(i < charmap -> count + 1)
{
memcpy(temp2i, charmap -> input[i], CHARMAPLENGTH + 1);
memcpy(charmap -> input[i], temp1i, CHARMAPLENGTH + 1);
memcpy(temp1i, temp2i, CHARMAPLENGTH + 1);
temp2o = charmap -> output[i];
charmap -> output[i] = temp1o;
temp1o = temp2o;
i++;
}
memcpy(charmap -> input[charmap -> count + 1], temp1i, CHARMAPLENGTH + 1);
charmap -> output[charmap -> count + 1] = temp1o;
}
else
{
memcpy(charmap -> input[charmap -> count], input, input_length);
charmap -> output[charmap -> count] = output;
}
return ++charmap -> count;
}
int
charmap_Convert(char **input)
{
struct Charmap *charmap;
char outchar[CHARMAPLENGTH + 1];
char *buffer;
int i, j, length;
if(pCurrentSection && pCurrentSection -> charmap)
{
charmap = pCurrentSection -> charmap;
}
else
{
charmap = &globalCharmap;
}
if((buffer = (char *) malloc(strlen(*input))) == NULL)
{
fatalerror("Not enough memory for buffer");
}
length = 0;
while(**input)
{
j = 0;
for(i = 0; i < charmap -> count; i++)
{
j = strlen(charmap -> input[i]);
if(memcmp(*input, charmap -> input[i], j) == 0)
{
outchar[0] = charmap -> output[i];
outchar[1] = 0;
break;
}
j = 0;
}
if(!j)
{
j = readUTF8Char(outchar, *input);
}
if(!outchar[0])
{
buffer[length++] = 0;
}
else
{
for(i = 0; outchar[i]; i++)
{
buffer[length++] = outchar[i];
}
}
*input += j;
}
*input = buffer;
return length;
}

View File

@@ -284,7 +284,7 @@ fstk_RunMacro(char *s)
pCurrentMacro = sym; pCurrentMacro = sym;
CurrentFlexHandle = CurrentFlexHandle =
yy_scan_bytes(pCurrentMacro->pMacro, yy_scan_bytes(pCurrentMacro->pMacro,
pCurrentMacro->ulMacroSize); strlen(pCurrentMacro->pMacro));
yy_switch_to_buffer(CurrentFlexHandle); yy_switch_to_buffer(CurrentFlexHandle);
return (1); return (1);
} else } else

View File

@@ -109,7 +109,7 @@ ascii2bin(char *s)
ULONG ULONG
ParseFixedPoint(char *s, ULONG size) ParseFixedPoint(char *s, ULONG size)
{ {
char dest[256]; //char dest[256];
ULONG i = 0, dot = 0; ULONG i = 0, dot = 0;
while (size && dot != 2) { while (size && dot != 2) {
@@ -117,13 +117,13 @@ ParseFixedPoint(char *s, ULONG size)
dot += 1; dot += 1;
if (dot < 2) { if (dot < 2) {
dest[i] = s[i]; //dest[i] = s[i];
size -= 1; size -= 1;
i += 1; i += 1;
} }
} }
dest[i] = 0; //dest[i] = 0;
yyunputbytes(size); yyunputbytes(size);
@@ -308,6 +308,7 @@ struct sLexInitString staticstrings[] = {
{"rsset", T_POP_RSSET}, {"rsset", T_POP_RSSET},
{"incbin", T_POP_INCBIN}, {"incbin", T_POP_INCBIN},
{"charmap", T_POP_CHARMAP},
{"fail", T_POP_FAIL}, {"fail", T_POP_FAIL},
{"warn", T_POP_WARN}, {"warn", T_POP_WARN},

View File

@@ -1,3 +1,10 @@
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include "asm/asm.h" #include "asm/asm.h"
#include "asm/lexer.h" #include "asm/lexer.h"
#include "asm/types.h" #include "asm/types.h"
@@ -8,11 +15,6 @@
#include "asmy.h" #include "asmy.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
struct sLexString { struct sLexString {
char *tzName; char *tzName;
ULONG nToken; ULONG nToken;
@@ -164,7 +166,10 @@ yy_create_buffer(FILE * f)
if (*mem == '\"') if (*mem == '\"')
instring = 1 - instring; instring = 1 - instring;
if (instring) { if (mem[0] == '\\' &&
(mem[1] == '\"' || mem[1] == '\\')) {
mem += 2;
} else if (instring) {
mem += 1; mem += 1;
} else { } else {
if ((mem[0] == 10 && mem[1] == 13) if ((mem[0] == 10 && mem[1] == 13)

View File

@@ -5,8 +5,8 @@
* *
*/ */
#define _XOPEN_SOURCE 500
#include <math.h> #include <math.h>
#include <getopt.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>

View File

@@ -11,6 +11,7 @@
#include <string.h> #include <string.h>
#include "asm/asm.h" #include "asm/asm.h"
#include "asm/charmap.h"
#include "asm/output.h" #include "asm/output.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/mylink.h" #include "asm/mylink.h"
@@ -637,6 +638,7 @@ out_FindSection(char *pzName, ULONG secttype, SLONG org,
pSect->nBank = bank; pSect->nBank = bank;
pSect->pNext = NULL; pSect->pNext = NULL;
pSect->pPatches = NULL; pSect->pPatches = NULL;
pSect->charmap = NULL;
pPatchSymbols = NULL; pPatchSymbols = NULL;
if ((pSect->tData = malloc(SECTIONCHUNK)) != NULL) { if ((pSect->tData = malloc(SECTIONCHUNK)) != NULL) {
@@ -709,6 +711,14 @@ out_AbsByte(int b)
nPC += 1; nPC += 1;
pPCSymbol->nValue += 1; pPCSymbol->nValue += 1;
} }
void
out_AbsByteGroup(char *s, int length)
{
checkcodesection(length);
while (length--)
out_AbsByte(*s++);
}
/* /*
* RGBAsm - OUTPUT.C - Outputs an objectfile * RGBAsm - OUTPUT.C - Outputs an objectfile
* *

View File

@@ -5,6 +5,7 @@
* *
*/ */
#define _XOPEN_SOURCE 500
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>

View File

@@ -1,13 +1,16 @@
%{ %{
#define _XOPEN_SOURCE 500
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h>
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/asm.h" #include "asm/asm.h"
#include "asm/charmap.h"
#include "asm/output.h" #include "asm/output.h"
#include "asm/mylink.h" #include "asm/mylink.h"
#include "asm/fstack.h" #include "asm/fstack.h"
@@ -66,6 +69,21 @@ ULONG str2int( char *s )
return( r ); return( r );
} }
ULONG str2int2( char *s, int length )
{
int i;
ULONG r=0;
i = (length - 4 < 0 ? 0 : length - 4);
while(i < length)
{
r<<=8;
r|=(UBYTE)(s[i]);
i++;
}
return( r );
}
ULONG isWhiteSpace( char s ) ULONG isWhiteSpace( char s )
{ {
return( s==' ' || s=='\t' || s=='\0' || s=='\n' ); return( s==' ' || s=='\t' || s=='\0' || s=='\n' );
@@ -423,6 +441,7 @@ void if_skip_to_endc( void )
%token T_POP_ENDM %token T_POP_ENDM
%token T_POP_RSRESET T_POP_RSSET %token T_POP_RSRESET T_POP_RSSET
%token T_POP_INCBIN T_POP_REPT %token T_POP_INCBIN T_POP_REPT
%token T_POP_CHARMAP
%token T_POP_SHIFT %token T_POP_SHIFT
%token T_POP_ENDR %token T_POP_ENDR
%token T_POP_FAIL %token T_POP_FAIL

View File

@@ -88,6 +88,7 @@ simple_pseudoop : include
| rsreset | rsreset
| rsset | rsset
| incbin | incbin
| charmap
| rept | rept
| shift | shift
| fail | fail
@@ -277,6 +278,24 @@ incbin : T_POP_INCBIN string
} }
; ;
charmap : T_POP_CHARMAP string ',' string
{
if(charmap_Add($2, $4[0] & 0xFF) == -1)
{
fprintf(stderr, "Error parsing charmap. Either you've added too many (%i), or the input character length is too long (%i)' : %s\n", MAXCHARMAPS, CHARMAPLENGTH, strerror(errno));
yyerror("Error parsing charmap.");
}
}
| T_POP_CHARMAP string ',' const
{
if(charmap_Add($2, $4 & 0xFF) == -1)
{
fprintf(stderr, "Error parsing charmap. Either you've added too many (%i), or the input character length is too long (%i)' : %s\n", MAXCHARMAPS, CHARMAPLENGTH, strerror(errno));
yyerror("Error parsing charmap.");
}
}
;
printt : T_POP_PRINTT string printt : T_POP_PRINTT string
{ {
if( nPass==1 ) if( nPass==1 )
@@ -336,7 +355,7 @@ constlist_8bit : constlist_8bit_entry
constlist_8bit_entry : { out_Skip( 1 ); } constlist_8bit_entry : { out_Skip( 1 ); }
| const_8bit { out_RelByte( &$1 ); } | const_8bit { out_RelByte( &$1 ); }
| string { out_String( $1 ); } | string { char *s; int length; s = $1; length = charmap_Convert(&s); out_AbsByteGroup(s, length); free(s); }
; ;
constlist_16bit : constlist_16bit_entry constlist_16bit : constlist_16bit_entry
@@ -391,7 +410,7 @@ relocconst : T_ID
| T_NUMBER | T_NUMBER
{ rpn_Number(&$$,$1); $$.nVal = $1; } { rpn_Number(&$$,$1); $$.nVal = $1; }
| string | string
{ ULONG r; r=str2int($1); rpn_Number(&$$,r); $$.nVal=r; } { char *s; int length; ULONG r; s = $1; length = charmap_Convert(&s); r = str2int2(s, length); free(s); rpn_Number(&$$,r); $$.nVal=r; }
| T_OP_LOGICNOT relocconst %prec NEG | T_OP_LOGICNOT relocconst %prec NEG
{ rpn_LOGNOT(&$$,&$2); } { rpn_LOGNOT(&$$,&$2); }
| relocconst T_OP_LOGICOR relocconst | relocconst T_OP_LOGICOR relocconst

10
src/extern/err.c vendored
View File

@@ -27,7 +27,7 @@
#include "extern/err.h" #include "extern/err.h"
#ifndef __MINGW32__ #ifndef __MINGW32__
extern char *__progname; char *__progname;
#endif #endif
void rgbds_vwarn(const char *fmt, va_list ap) void rgbds_vwarn(const char *fmt, va_list ap)
@@ -47,13 +47,13 @@ void rgbds_vwarnx(const char *fmt, va_list ap)
putc('\n', stderr); putc('\n', stderr);
} }
void rgbds_verr(int status, const char *fmt, va_list ap) _Noreturn void rgbds_verr(int status, const char *fmt, va_list ap)
{ {
vwarn(fmt, ap); vwarn(fmt, ap);
exit(status); exit(status);
} }
void rgbds_verrx(int status, const char *fmt, va_list ap) _Noreturn void rgbds_verrx(int status, const char *fmt, va_list ap)
{ {
vwarnx(fmt, ap); vwarnx(fmt, ap);
exit(status); exit(status);
@@ -75,7 +75,7 @@ void rgbds_warnx(const char *fmt, ...)
va_end(ap); va_end(ap);
} }
void rgbds_err(int status, const char *fmt, ...) _Noreturn void rgbds_err(int status, const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
@@ -83,7 +83,7 @@ void rgbds_err(int status, const char *fmt, ...)
va_end(ap); va_end(ap);
} }
void rgbds_errx(int status, const char *fmt, ...) _Noreturn void rgbds_errx(int status, const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);

View File

@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <getopt.h> #define _XOPEN_SOURCE 500
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>

View File

@@ -1,4 +1,4 @@
#include <getopt.h> #define _XOPEN_SOURCE 500
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>