108 lines
2.3 KiB
C
108 lines
2.3 KiB
C
#include <ctype.h> /* isdigit(3) */
|
|
#include <stdio.h> /* fprintf(3), getc(3), putc(3) */
|
|
#include <stdlib.h> /* stdin, stderr, stdout */
|
|
#include <sysexits.h> /* EX_DATAERR, EX_OK */
|
|
|
|
/* Unicode compatibility coming someday */
|
|
#define GETC getc
|
|
#define PUTC putc
|
|
|
|
#define NUMBER_TERMINATOR '*'
|
|
|
|
/* syntax error if a decoding file ends with [char][char] */
|
|
/* if(STRICT) just print it out */
|
|
#define STRICT 0
|
|
|
|
static int decode(FILE *input, FILE *output){
|
|
int c[2];
|
|
unsigned int num;
|
|
enum{NUMBER_PARSING = 1} state;
|
|
|
|
c[0] = EOF;
|
|
|
|
/* reading -> (current char == prev char)> number parsing -> reading */
|
|
while((c[1] = GETC(input)) != EOF)
|
|
if(state == NUMBER_PARSING){
|
|
if(isdigit(c[1]))
|
|
num = num * 10 + c[1] - '0';
|
|
else if(c[1] == NUMBER_TERMINATOR){
|
|
for(; num > 0; --num)
|
|
PUTC(c[0], output);
|
|
c[0] = EOF; c[1] = EOF;
|
|
state = 0;
|
|
}else
|
|
return EX_DATAERR;
|
|
}else if(c[1] == c[0]){
|
|
num = 0;
|
|
state = NUMBER_PARSING;
|
|
}else{
|
|
if(c[0] != EOF)
|
|
PUTC(c[0], output);
|
|
c[0] = c[1];
|
|
}
|
|
|
|
if(state == NUMBER_PARSING && !STRICT)
|
|
/* it doesn't make sense to put this in a loop */
|
|
{ PUTC(c[0], output); PUTC(c[0], output); }
|
|
else if(state == NUMBER_PARSING)
|
|
return EX_DATAERR;
|
|
else if(c[0] != EOF)
|
|
PUTC(c[0], output);
|
|
|
|
return EX_OK;
|
|
}
|
|
|
|
static int encode(FILE *input, FILE *output){
|
|
int c[2];
|
|
unsigned int num;
|
|
enum{COUNTING = 1} state;
|
|
|
|
/* It was more fun to use gotos than to use sane structure. */
|
|
for(c[0] = EOF, num = 2, state = 0; ;)
|
|
if((c[1] = GETC(input)) == EOF){
|
|
if(state == COUNTING)
|
|
goto dump;
|
|
else
|
|
goto place;
|
|
}else if(state == COUNTING){
|
|
if(c[1] != c[0]){
|
|
dump: PUTC(c[0], output);
|
|
fprintf(output,
|
|
"%d%c",
|
|
num, NUMBER_TERMINATOR
|
|
);
|
|
num = 2;
|
|
state = 0;
|
|
goto next;
|
|
}else
|
|
++num;
|
|
}else if(c[1] == c[0])
|
|
state = COUNTING;
|
|
else{ /* c[1] != c[0] */
|
|
if(c[0] != EOF) /* c[0] will be EOF at first */
|
|
place: PUTC(c[0], output);
|
|
next: if(c[1] == EOF)
|
|
return EX_OK;
|
|
c[0] = c[1];
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[]){
|
|
int r;
|
|
|
|
if(argc != 1){
|
|
fprintf(stderr,
|
|
"Usage: %s\n",
|
|
argv[0] == NULL ?
|
|
(f == decode ? "decode" : "encode")
|
|
: argv[0]
|
|
);
|
|
return EX_USAGE;
|
|
}
|
|
|
|
if((r = f(stdin, stdout)) == EX_DATAERR)
|
|
fprintf(stderr, "%s: syntax error.\n", argv[0]);
|
|
|
|
return r;
|
|
}
|