diff options
author | Jakob Kaivo <jkk@ung.org> | 2019-03-13 21:05:34 -0400 |
---|---|---|
committer | Jakob Kaivo <jkk@ung.org> | 2019-03-13 21:05:34 -0400 |
commit | 39ecba0032be794a1f4d66f61e09e4910270330f (patch) | |
tree | 9b68f25a597891afce13825328e837da168dfada |
migrate to gitlab
-rw-r--r-- | Makefile | 33 | ||||
-rw-r--r-- | as/Makefile | 11 | ||||
-rw-r--r-- | as/as.l | 25 | ||||
-rw-r--r-- | as/as.y | 36 | ||||
-rw-r--r-- | as/x86.h | 38 | ||||
-rw-r--r-- | c89.l | 95 | ||||
-rw-r--r-- | c89.y | 525 | ||||
-rw-r--r-- | c99.l | 15 | ||||
-rw-r--r-- | c99.y | 29 | ||||
-rw-r--r-- | trigraph.c | 53 |
10 files changed, 860 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4e77ce4 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +.POSIX: + +.SILENT: all clean .util.mk + +CFLAGS=-std=gnu99 -D_XOPEN_SOURCE=700 -g + +all: .util.mk + make -f .util.mk --no-print-directory + +deps: clean .util.mk + +clean: + [ -f .util.mk ] && make -k --no-print-directory -f .util.mk clean || true + rm -f .util.mk + +.util.mk: . Makefile + printf '.POSIX:\n\n' > $@ + printf '.SUFFIXES: .cat .msg\n\n' >> $@ + printf 'default: all\n\n' >> $@ + printf 'CFLAGS=$(CFLAGS)\n' >> $@ + printf 'UTILITY=%s\n' "$$(basename -s .c $$(grep -l ^main *.c | head -n1))" >> $@ + printf 'SOURCES=%s\n' "$$(ls -1 *.c | tr '\n' ' ')" >> $@ + printf 'HEADERS=%s\n' "$$(ls -1 *.h 2>/dev/null | tr '\n' ' ')" >> $@ + printf 'OBJECTS=%s\n' "$$(ls -1 *.c | sed -e 's/\.c$$/.o/' | tr '\n' ' ')" >> $@ + printf 'L10N=%s\n' "$$(ls -1 *.msg 2>/dev/null | sed -e 's/\.msg$$/\.cat/' | tr '\n' ' ')" >> $@ + printf 'L11N=' >> $@ + sed -ne '/^\/\*\*cat/,/cat\*\*\//p;' *.c | head -n1 | awk '{print $$2 ".cat"}' >> $@ + printf '\n' >> $@ + printf 'all: $$(UTILITY) $$(L10N)\n\n' >> $@ + printf '$$(UTILITY): $$(OBJECTS) $$(HEADERS)\n\n' >> $@ + printf '.msg.cat:\n\tgencat $$@ $$<\n\n' >> $@ + printf ".c.cat:\n\tsed -ne '/^\/\*\*cat/,/cat\*\*\//p;' $$< | grep -v ^/ | grep -v ^\* | gencat \$$@ -\n\n" >> $@ + printf 'clean:\n\trm -f *.o $$(L10N) $$(UTILITY)\n\n' >> $@ diff --git a/as/Makefile b/as/Makefile new file mode 100644 index 0000000..99fe9de --- /dev/null +++ b/as/Makefile @@ -0,0 +1,11 @@ +as: as.yy.o as.tab.o + c99 -o $@ as.yy.o as.tab.o -ly -ll + +as.yy.c: as.l as.tab.h + lex -t as.l > $@ + +as.tab.h as.tab.c: as.y + yacc -d -b as as.y + +clean: + rm -f as *.o as.yy.c as.tab.c as.tab.h @@ -0,0 +1,25 @@ +%{ +#include <inttypes.h> +#include "as.tab.h" +%} + +NONDIGIT [_a-zA-Z] +DIGIT [0-9] +IDENTIFIERS [_a-zA-Z0-9] + +%x COMMENT + +%% + +{DIGIT}+ { yylval.n = strtoumax(yytext, NULL, 10); return NUMBER; } + +{NONDIGIT}{IDENTIFIERS}* { yylval.s = yytext; return TOKEN; } + +:|,|\. { return yytext[0]; } +\n { return NEWLINE; } + +; { BEGIN COMMENT; } +<COMMENT>. ; +<COMMENT>\n { BEGIN INITIAL; return NEWLINE; } + +. ; @@ -0,0 +1,36 @@ +%{ +#include <inttypes.h> +%} + +%union { + char *s; + uintmax_t n; +}; + +%token<n> NUMBER +%token<s> TOKEN +%token NEWLINE + +%% + +program + : /* empty */ + | instruction NEWLINE +; + +instruction + : bare_instruction + | TOKEN ':' bare_instruction +; + +bare_instruction + : TOKEN + | TOKEN operand + | TOKEN operand ',' operand + | TOKEN operand ',' operand ',' operand +; + +operand + : TOKEN + | NUMBER +; diff --git a/as/x86.h b/as/x86.h new file mode 100644 index 0000000..56bf639 --- /dev/null +++ b/as/x86.h @@ -0,0 +1,38 @@ +struct { +const char *mnemonic; +const char *opcode; +char x32; +char x64; +} x86_opcodes[] = { +{ "aaa", "37", 1, 0 }, + +{ "aad", "d5 0a", 1, 0 }, +{ "aad imm8", "db ib", 1, 0 }, + +{ "aam", "d4 0a", 1, 0 }, +{ "aam imm8", "d4 ib", 1, 0 }, + +{ "aas", "3f" , 1, 0 }, + +{ "adc al, imm8", "14 ib", 1, 1 }, +{ "adc ax, immm16", "15 iw", 1, 1 }, +{ "adc eax, imm32", "15 id", 1, 1 }, +{ "adc rax, imm32", "rex.w 15 id", 0, 1 }, +{ "adc r/m8, imm8", "80 /2 ib", 1, 1 }, +{ "adc r/m8*, imm8", "rex 80 /2 ib", 0, 1 }, +{ "adc r/m16, imm16", "81 /2 iw", 1, 1 }, +{ "adc r/m32, imm32", "81 /2 id", 1, 1 }, +{ "adc r/m64, imm32", "rex.w 81 /2 id", 0, 1 }, +{ "adc r/m16, imm8", "83 /2 ib", 1, 1 }, +{ "adc r/m32, imm8", "83 /2 ib", 1, 1 }, +{ "adc r/m64, imm8", "rex.w 83 /2 ib", 0, 1 }, +{ "adc r/m8, r8", "10 /r", 1, 1 }, +{ "adc r/m8*, r8*", "rex 10 /r", 0, 1 }, +{ "adc r/m16, r16", "11 /r", 1, 1 }, +{ "adc r/m32, r32", "11 /r", 1, 1 }, +{ "adc r/m64, r64", "rex.w 11 /r", 0, 1 }, +{ "adc r8, r/m8", "12 /r", 1, 1 }, +{ "adc r8*, r/m8*", "rex 12 /r", 0, 1 }, +{ "adc r16, r/m16", "13 /r", 1, 1 }, +{ "adc r32, r/m32", "13 /r", 1, 1 }, +{ "adc r64, r/m64", "rex.w 13 /r", 0, 1 }, @@ -0,0 +1,95 @@ +%{ + +%} + +DIGIT [0-9] +UPPER [A-Z] +LOWER [a-z] +NONDIGIT [_a-zA-Z] + +%% + /* keywords */ +"auto" { return AUTO; } +"break" { return BREAK; } +"case" { return CASE; } +"char" { return CHAR; } +"const" { return CONST; } +"continue" { return CONTINUE; } +"default" { return DEFAULT; } +"do" { return DO; } +"double" { return DOUBLE; } +"else" { return ELSE; } +"enum" { return ENUM; } +"extern" { return EXTERN; } +"float" { return FLOAT; } +"for" { return FOR; } +"goto" { return GOTO; } +"if" { return IF; } +"int" { return INT; } +"long" { return LONG; } +"register" { return REGISTER; } +"return" { return RETURN; } +"short" { return SHORT; } +"signed" { return SIGNED; } +"sizeof" { return SIZEOF; } +"static" { return STATIC; } +"struct" { return STRUCT; } +"switch" { return SWITCH; } +"typedef" { return TYPEDEF; } +"union" { return UNION; } +"unsigned" { return UNSIGNED; } +"void" { return VOID; } +"volatile" { return VOLATILE; } +"while" { return WHILE; } + + /* operators */ +"[" { return LBRACKET; } +"]" { return RBRACKET; } +"(" { return LPAREN; } +")" { return RPAREN; } +"." { return DOT; } +"->" { return ARROW; } +"++" { return INCREMENT; } +"--" { return DECREMENT; } +"&" { return AMPERSAND; } +"*" { return STAR; } +"+" { return PLUS; } +"-" { return MINUS; } +"~" { return TILDE; } +"!" { return BANG; } +"/" { return SLASH; } +"%" { return PERCENT; } +"<<" { return LSHIFT; } +">>" { return RSHIFT; } +"<" { return LESSTHAN; } +">" { return GREATERTHAN; } +"<=" { return LESSEQUAL; } +">=" { return GREATEREQUAL; } +"==" { return ISEQUAL; } +"!=" { return NOTEQUAL; } +"^" { return CARET; } +"|" { return PIPE; } +"&&" { return ANDAND; } +"||" { return OROR; } +"?" { return QUESTION; } +":" { return COLON; } +"=" { return EQUALS; } +"*=" { return STAREQUALS; } +"/=" { return SLASHEQUALS; } +"%=" { return PERCENTEQUALS; } +"+=" { return PLUSEQUALS; } +"-=" { return MINUSEQUALS; } +"<<=" { return LSHIFTEQUALS; } +">>=" { return RSHIFTEQUALS; } +"&=" { return ANDEQUALS; } +"^=" { return CARETEQUALS; } +"|=" { return PIPEEQUALS; } +"," { return COMMA; } +"#" { return HASH; } +"##" { return HASHHASH; } + + /* additional punctuators */ +"{" { return LBRACE; } +"}" { return RBRACE; } +";" { return SEMICOLON; } +"..." { return DOTDOTDOT; } @@ -0,0 +1,525 @@ +%{ +#include <stdio.h> + +int yylex(void); + +void yyerror(char *str) +{ + printf("WUT?: %s\n" str); +} +%} + +%union { +} + + /* keywords */ +%token AUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN + FLOAT FOR GOTO IF INT LONG REGISTER RETURN SHORT SIGNED SIZEOF STATIC + STRUCT SWITCH TYPEDEF UNION UNSIGNED VOID VOLATILE WHILE + + /* operators */ +%token LBRACKET RBRACKET LPAREN RPAREN DOT ARROW INCREMENT DECREMENT AMPERSAND + STAR PLUS MINUS TILDE BANG SLASH PERCENT LSHIFT RSHIFT LESSTHAN + GREATERTHAN LESSEQUAL GREATEREQUAL ISEQUAL NOTEQUAL CARET PIPE ANDAND + OROR QUESTION COLON EQUALS STAREQUALS SLASHEQUALS PERCENTEQUALS + PLUSEQUALS MINUSEQUALS LSHIFTEQUALS RSHIFTEQUALS ANDEQUALS CARETEQUALS + PIPEEQUALS COMMA HASH HASHHASH + + /* additional punctuators */ +%token LBRACE RBRACE SEMICOLON DOTDOTDOT + +%% + +primary-expression: + identifier + | constant + | string-literal + | LPAREN expression RPAREN + ; + +postfix-expression: + primary-expression + | postfix-expression LBRACKET expression RBRACKET + | postfix-expression LPAREN argument-expression-list RPAREN + | postfix-expression LPRAEN RPAREN + | postfix-expression DOT identifier + | postfix-expression ARROW identifier + | postfix-expression INCREMENT + | postfix-expression DECREMENT + ; + +argument-expression-list: + assignment-expression + | argument-expression-list COMMA assignment-expression + ; + +unary-expression: + postfix-expression + | INCREMENT unary-expression + | DECREMENT unary-expression + | unary-operator cast-expression + | SIZEOF unary-expression + | SIZEOF LPAREN type-name RPAREN + ; + +unary-operator: + AMPERSAND + | STAR + | PLUS + | MINUS + | TILDE + | BANG + ; + +cast-expression: + unary-expression + | LPAREN type-name RPAREN cast-expression + ; + +multiplicative-expression: + cast-expression + | multiplicative-expression STAR cast-expression + | multiplicative-expression SLASH cast-expression + | multiplicative-expression PERCENT cast-expression + ; + +additive-expression: + multiplicative-expression + | additive-expression PLUS multiplicative-expression + | additive-expression MINUS multiplicative-expression + ; + +shift-expression: + additive-expression + | shift-expression LSHIFT additive-expression + | shift-expression RSHIFT additive-expression + ; + +relational-expression: + shift-expression + | relational-expression LESSTHAN shift-expression + | relational-expression GREATERTHAN shift-expression + | relational-expression LESSEQUAL shift-expression + | relational-expression GREATEREQUAL shift-expression + ; + +equality-expression: + relational-expression + | equality-expression ISEQUAL relational-expression + | equality-expression NOTEQUAL relational-expression + ; + +AND-expression: + equality-expression + | AND-expression AMPERSAND equality-expression + ; + +exclusive-OR-expression: + AND-expression + | exclusive-OR-expression CARET AND-expression + ; + +inclusive-OR-expression: + exclusive-OR-expression + | inclusive-OR-expression PIPE exclusive-OR-expression + ; + +logical-AND-expression: + inclusive-OR-expression + | logical-AND-expression ANDAND inclusive-OR-expression + ; + +logical-OR-expression: + logical-AND-expression + | logical-OR-expression OROR logical-AND-expression + ; + +conditional-expression: + logical-OR-expression + | logical-OR-expression QUESTION expression COLON conditional-expression + ; + +assignment-expression: + conditional-expression + | unary-expression assignment-operator assignment-expression + ; + +assignment-operator: + EQUALS + | STAREQUALS + | SLASHEQUALS + | PERCENTEQUALS + | PLUSEQUALS + | MINUSEQUALS + | LSHIFTEQUALS + | RSHIFTEQUALS + | ANDEQUALS + | CARETEQUALS + | PIPEEQUALS + ; + +expression: + assignment-expression + | expression COMMA assignment-expression + ; + +constant-expression: + conditional-expression + ; + +declaration: + declaration-specifiers SEMICOLON + | declaration-specifiers init-declarator-list SEMICOLON + ; + +declaration-specifiers: + storage-class-specifier + | storage-class-specifier declaration-specifiers + | type-specifier + | type-specifier declaration-specifiers + | type-qualifier + | type-qualifier declaration-specifiers + ; + +init-declarator-list: + init-declarataor + | init-declarator-list COMMA init-declarator + ; + +init-declarator: + declarator + | declarator EQUALS initializer + ; + +storage-class-specifier: + TYPEDEF + | EXTERN + | STATIC + | AUTO + | REGISTER + ; + +type-specifier: + VOID + | CHAR + | SHORT + | INT + | LONG + | FLOAT + | DOUBLE + | SIGNED + | UNSIGNED + | struct-or-union-specifier + | enum-specifier + | typedef-name + ; + +struct-or-union-specifier: + struct-or-union LBRACE struct-declaration-list RBRACE + | struct-or-union identifier LBRACE struct-declaration-list RBRACE + | struct-or-union identifier + ; + +struct-or-union: + STRUCT + | UNION + ; + +struct-declaration-list: + struct-declaration + | struct-declaration-list struct declaration + ; + +struct-declaration: + specifier-qualifier-list struct-declarator-list SEMICOLON + ; + +specifier-qualifier-list: + type-specifier + | type-specifier specifier-qualifier-list + | type-qualifier + | type-qualifier specifier-qualifier-list + ; + +struct-declarator-list: + struct-declarator + | struct-declarator-list COMMA struct-declarator + ; + +struct-declarator: + declarator + | COMMA constant-expression + | declarator COMMA constant-expression + ; + +enum-specifier: + ENUM LBRACE enumerator-list RBRACE + | ENUM identifier LBRACE enumerator-list RBRACE + | ENUM identifier + ; + +enumerator-list: + enumerator + | enumerator-list COMMA enumerator + ; + +enumerator: + enumeration-constant + | enumeration-constant EQUALS constant-expression + ; + +type-qualifier: + CONST + | VOID + ; + +declarator: + direct-declarator + | pointer direct-declarator + ; + +direct-declarator: + identifier + | LPAREN declarator RPAREN + | direct-declarator LBRACKET RBRACKET + | direct-declarator LBRACKET constant-expression RBRACKET + | direct-declarator LPAREN parameter-type-list RPAREN + | direct-declarator LPAREN RPAREN + | direct-declarator LPAREN identifier-list RPAREN + ; + +pointer: + STAR + | STAR type-qualifier-list + | STAR pointer + | STAR type-qualifier-list pointer + ; + +type-qualifier-list: + type-qualifier + | type-qualifier-list type-qualifier + ; + +parameter-type-list: + parameter-list + | parameter-list COMMA DOTDOTDOT + ; + +parameter-list: + parameter-declaration + | parameter-list COMMA parameter-declaration + ; + +parameter-declaration: + declaration-specifiers declarator + | declaration-specifiers + | declaration-specifiers abstract-declarator + ; + +identifier-list: + identifier + | identifier-list COMMA identifier + ; + +type-name: + specifier-qualifier-list + | specifier-qualifier-list abstract-declarator + ; + +abstract-declarator: + pointer + | direct-abstract-declarator + | point direct-abstract-declarator + ; + +direct-abstract-declarator: + LPAREN abstract-declarator RPAREN + | LBRACKET RBRACKET + | LBRACKET constant-expression RBRACKET + | direct-abstract-declarator LBRACKET RBRACKET + | direct-abstract-declarator LBRACKET constant-expression RBRACKET + | LPAREN RPAREN + | LPAREN parameter-type-list RPAREN + | direct-abstract-declarator LPAREN RPAREN + | direct-abstract-declarator LPAREN parameter-type-list RPAREN + ; + +typdef-name: + identifier + ; + +initializer: + assignment-expression + | LBRACE initializer-list RBRACE + | LBRACE initializer-list COMMA RBRACE + ; + +initializer-list: + initializer + | initializer-list COMMA initializer + ; + +statement: + labeled-statement + | compound-statement + | expression-statement + | selection-statement + | iteration-statement + | jump-statement + ; + +labeled-statement: + identifier COLON statement + | CASE constant-expression COLON statement + | DEFAULT COLON statement + ; + +compound-statement: + LBRACE RBRACE + | LBRACE declaration-list RBRACE + | LBRACE statement-list RBRACE + | LBRACE declaration-list statement-list RBRACE + ; + +declaration-list: + declaration + | declaration-list declaration + ; + +statement-list: + statement + | statement-list statement + ; + +expression-statement: + SEMICOLON + | expression SEMICOLON + ; + +selection-statement: + IF LPAREN expression RPAREN statement + | IF LPAREN expression RPAREN statement ELSE statement + | SWITCH LPAREN expression RPAREN statement + ; + +iteration-statement: + WHILE LPAREN expression RPAREN statement + | DO statement WHILE LPAREN expression RPAREN SEMICOLON + | FOR LPAREN SEMICOLON SEMICOLON RPAREN statement + | FOR LPAREN expression SEMICOLON SEMICOLON RPAREN statement + | FOR LPAREN SEMICOLON expression SEMICOLON RPAREN statement + | FOR LPAREN SEMICOLON SEMICOLON expression RPAREN statement + | FOR LPAREN expression SEMICOLON expression SEMICOLON RPAREN statement + | FOR LPAREN SEMICOLON expression SEMICOLON expression RPAREN statement + | FOR LPAREN expression SEMICOLON SEMICOLON expression RPAREN statement + | FOR LPAREN expression SEMICOLON expression SEMICOLON expression RPAREN statement + ; + +jump-statement: + GOTO identifier SEMICOLON + | CONTINUE SEMICOLON + | BREAK SEMICOLON + | RETURN SEMICOLON + | RETURN expression SEMICOLON + ; + +translation-unit: + external-declaration + | translation-unit external-declaration + ; + +external-declaration: + function-definition + | declaration + ; + +function-definition: + declarator compound-statement + | declaration-specifiers declarator compound-statement + | declarator declaration-list compound-statement + | declaration-specifiers declarator declaration-list compound-statement + ; + +preprocessing-file: + /* optional */ + | group + ; + +group: + group-part + | group group-part + ; + +group-part: + new-line + | pp-tokens new-line + | if-section + | control-line + ; + +if-section: + if-group endif-line + | if-group elif-groups endif-line + | if-group else-group endif-line + | if-group elif-groups else-group endif-line + ; + +if-group: + HASH IF constant-expression new-line + | HASH IF constant-expression new-line group + | HASH IFDEF identifier new-line + | HASH IFDEF identifier new-line group + | HASH IFNDEF identifier new-line + | HASH IFNDEF identifier new-line group + ; + +elif-groups: + elif-group + | elif-groups elif-group + ; + +elif-group: + HASH ELIF constant-expression new-line + | HASH ELIF constant-expression new-line group + ; + +else-group: + HASH ELSE new-line + | HASH ELSE new-line group + ; + +endif-line: + HASH ENDIF new-line + ; + +control-line: + HASH INCLUDE pp-tokens new-line + | HASH DEFINE identifier replacement-list new-line + | HASH DEFINE identifier lparen RPAREN replacement-list new-line + | HASH DEFINE identifier lparen identifier-list RPAREN replacement-list new-line + | HASH UNDEF identifier new-line + | HASH LINE pp-tokens new-line + | HASH ERROR new-line + | HASH ERROR pp-tokens new-line + | HASH PRAGMA new-line + | HASH PRAGMA pp-tokens new-line + | HASH new-line + ; + +lparen: + LPAREN /* without preceding white space */ + ; + +replacement-list: + /* optional */ + | pp-tokens + ; + +pp-tokens: + preprocessing-token + | pp-tokens preprocessing-token + ; + +new-line: + NEWLINE + ; @@ -0,0 +1,15 @@ +%{ +#include "c99.tab.h" +%} + +%% +; { printf ("SEMICOLON\n"); return SEMICOLON; } +"+" { printf ("PLUS\n"); return PLUS; } +"-" { printf ("MINUS\n"); return MINUS; } +int { printf ("INT\n"); return INT; } +"\".*\"" { printf ("string literal:%s\n", yytext); return STRING; } +[a-zA-z_]+ { printf ("identifier: %s\n", yytext); return IDENTIFIER; } +"/*[.]**/" { printf ("COMMENT\n"); return COMMENT; } +%% + +int yywrap (void) { return 0; } @@ -0,0 +1,29 @@ +%{ +int yylex(void); +void yyerror(char const *); +%} + +%token IDENTIFIER COMMENT +%token STRING NUMBER + + /* C89 keywords */ +%token AUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE +%token ENUM EXTERN FLOAT FOR GOTO IF INT LONG REGISTER +%token RETURN SHORT SIGNED SIZEOF STATIC STRUCT SWITCH TYPEDEF UNION +%token UNSIGNED VOID VOLATILE WHILE + + /* Punctuation */ +%token PLUS MINUS STAR SLASH +%token COMMA SEMICOLON +%token QUESTION COLON +%token LPAREN RPAREN +%token LBRACE RBRACE +%token LBRACKET RBRACKET + + /* C99 keywords */ +%token INLINE RESTRICT _BOOL _COMPLEX _IMAGINARY + +%% + +input: %empty + ; diff --git a/trigraph.c b/trigraph.c new file mode 100644 index 0000000..b80b198 --- /dev/null +++ b/trigraph.c @@ -0,0 +1,53 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + FILE *in = stdin; + + if (argc > 2) { + printf("usage: %s [file]\n", argv[0]); + return 1; + } + + if (argc == 2) { + in = fopen(argv[1], "r"); + if (in == NULL) { + perror(argv[1]); + return 1; + } + } + + int c; + int q = 0; + while ((c = fgetc(in)) != EOF) { + if (q == 2) { + switch (c) { + case '=': putchar('#'); break; + case '/': putchar('\\'); break; + case '\'': putchar('^'); break; + case '(': putchar('['); break; + case ')': putchar(']'); break; + case '!': putchar('|'); break; + case '<': putchar('{'); break; + case '>': putchar('}'); break; + case '-': putchar('~'); break; + case '?': putchar('?'); break; + + default: + printf("??%c", c); + break; + } + + if (c != '?') { + q = 0; + } + } else if (c == '?') { + q++; + } else { + putchar(c); + q = 0; + } + } + + return 0; +} |