528 lines
9.9 KiB
C
528 lines
9.9 KiB
C
typedef enum {
|
|
Nop=100000, /* ; do nothing */
|
|
New1, /* ; reset and begin new test */
|
|
Open, /* ; open test file */
|
|
Read, /* [askfor] [get] ; read bytes into buffer */
|
|
Write, /* [expect] [bytestream] ; write to file (expect 0 = ignore)*/
|
|
Compare, /* [bytestream] ; compare buffer to given bytes */
|
|
Verify, /* [bytestream] ; compare file to given bytes */
|
|
Seek, /* [offset] [whence] ; seek in file */
|
|
Tell, /* [offset] ; compare file positions */
|
|
BufSize, /* [size] ; change the stdio buffer size */
|
|
Flush, /* ; flush the stdio stream */
|
|
Text, /* ; switch file to text mode */
|
|
Binary, /* ; switch file to binary mode */
|
|
Rep, /* [count] ; repeat 'R' bytes (used in bytestream) */
|
|
Fill, /* [posn] ; fill 'F' until given byte position */
|
|
Start, /* ; for Seek */
|
|
Current, /* ; for Seek */
|
|
End, /* ; for Seek, or end of byte stream */
|
|
Max } Opcode;
|
|
|
|
#define New New1,__LINE__
|
|
|
|
/* Byte streams are just inserted into the command stream, and must
|
|
end in an End code. */
|
|
|
|
int commands[] = {
|
|
#ifndef __DJGPP__
|
|
New,
|
|
Write, 1605, Rep, 1600, 'h', 'e', 'l', 'l', 'o', End,
|
|
Tell, 1605,
|
|
|
|
Open,
|
|
BufSize, 16,
|
|
Read, 1605, 1605,
|
|
Compare, Rep, 1600, 'h', 'e', 'l', 'l', 'o', End,
|
|
Tell, 1605,
|
|
Flush,
|
|
Tell, 1605,
|
|
Seek, 1000, Start,
|
|
Tell, 1000,
|
|
|
|
New,
|
|
Text,
|
|
Write, 2, 'x', 10, End,
|
|
Verify, 'x', 13, 10, End,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 2, 'x', 10, End,
|
|
Verify, 'x', 10, End,
|
|
|
|
BufSize, 16,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, Fill, 15, 13, 10, 'x', End,
|
|
Text, Open,
|
|
Read, 17, 17,
|
|
Compare, Fill, 15, 10, 'x', End,
|
|
Tell, 18,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, Fill, 14, 13, 10, 'x', End,
|
|
Text, Open,
|
|
Read, 16, 16,
|
|
Compare, Fill, 14, 10, 'x', End,
|
|
Tell, 17,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, 13, 10, 'a', 'b', End,
|
|
Text, Open,
|
|
Read, 2, 2,
|
|
Compare, 10, 'a', End,
|
|
Tell, 3,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, 10, 'a', 'b', End,
|
|
Text, Open,
|
|
Read, 2, 2,
|
|
Compare, 10, 'a', End,
|
|
Tell, 2,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, 13, 'a', 'b', End,
|
|
Text, Open,
|
|
Read, 2, 2,
|
|
Compare, 13, 'a', End,
|
|
Tell, 2,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, 13, 13, 10, 'a', End,
|
|
Text, Open,
|
|
Read, 2, 2,
|
|
Compare, 13, 10, End,
|
|
Tell, 3,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, 13, 10, 'a', 13, End,
|
|
Text, Open,
|
|
Read, 2, 2,
|
|
Compare, 10, 'a', End,
|
|
Tell, 3,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, 13, 10, 'a', 10, End,
|
|
Text, Open,
|
|
Read, 2, 2,
|
|
Compare, 10, 'a', End,
|
|
Tell, 3,
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, 13, 13, 13, 10, 'a', 10, 10, 10, End,
|
|
Text, Open,
|
|
Read, 4, 4,
|
|
Compare, 13, 13, 10, 'a', End,
|
|
Tell, 5,
|
|
#endif
|
|
|
|
New,
|
|
Binary,
|
|
Write, 0, 13, 13, 13, 10, 'a', 'b', 13, 10, 13, 10, End,
|
|
Text, Open,
|
|
Read, 4, 4,
|
|
Compare, 13, 13, 10, 'a', End,
|
|
Tell, 5,
|
|
|
|
};
|
|
|
|
/*==========================================================================*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif
|
|
#ifndef O_TEXT
|
|
#define O_TEXT 0
|
|
#endif
|
|
|
|
int errors = 0;
|
|
|
|
#define num_commands (int)(sizeof(commands)/sizeof(commands[0]))
|
|
|
|
int pc;
|
|
int askfor, get, expect, count, posn, whence, size;
|
|
|
|
typedef struct {
|
|
unsigned char *bytes;
|
|
int max, count;
|
|
} Buffer;
|
|
|
|
Buffer rw_buf={0,0,0}, cmp_buf={0,0,0}, vfy_buf={0,0,0};
|
|
|
|
void
|
|
expand_buf(Buffer *buf, int len)
|
|
{
|
|
if (buf->max < len)
|
|
{
|
|
buf->max = len+20;
|
|
if (buf->bytes)
|
|
buf->bytes = (unsigned char *)realloc(buf->bytes, buf->max);
|
|
else
|
|
buf->bytes = (unsigned char *)malloc(buf->max);
|
|
}
|
|
}
|
|
|
|
void
|
|
get_bytestream(Buffer *buf)
|
|
{
|
|
int tpc;
|
|
int len = 0, rep, byte;
|
|
unsigned char *bp;
|
|
|
|
for (tpc = pc+1; tpc < num_commands && commands[tpc] != End; tpc++)
|
|
{
|
|
switch (commands[tpc])
|
|
{
|
|
case Rep:
|
|
len += commands[tpc+1];
|
|
tpc ++;
|
|
break;
|
|
case Fill:
|
|
if (len < commands[tpc+1])
|
|
len = commands[tpc+1];
|
|
tpc ++;
|
|
break;
|
|
default:
|
|
len ++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
expand_buf(buf, len);
|
|
|
|
len = 0;
|
|
bp = buf->bytes;
|
|
|
|
for (tpc = pc+1; tpc < num_commands && commands[tpc] != End; tpc++)
|
|
{
|
|
switch (commands[tpc])
|
|
{
|
|
case Rep:
|
|
rep = commands[++tpc];
|
|
byte = 'R';
|
|
while (rep--) *bp++ = byte;
|
|
break;
|
|
case Fill:
|
|
rep = commands[++tpc];
|
|
byte = 'F';
|
|
while (bp-buf->bytes < rep) *bp++ = byte;
|
|
break;
|
|
default:
|
|
*bp++ = commands[tpc];
|
|
break;
|
|
}
|
|
}
|
|
buf->count = bp - buf->bytes;
|
|
pc = tpc;
|
|
}
|
|
|
|
char dataname[] = "crlf.dat";
|
|
|
|
int verbose=0;
|
|
void
|
|
v(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
if (!verbose) return;
|
|
va_start(ap, fmt);
|
|
vfprintf(stdout, fmt, ap);
|
|
va_end(ap);
|
|
printf("\n");
|
|
}
|
|
|
|
void
|
|
vp(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
if (!verbose) return;
|
|
printf("%08x: ", pc);
|
|
va_start(ap, fmt);
|
|
vfprintf(stdout, fmt, ap);
|
|
va_end(ap);
|
|
printf("\n");
|
|
}
|
|
|
|
void
|
|
errorq(int use_errno, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
fprintf(stderr, "crlf: Error at pc=%d: ", pc);
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
fprintf(stderr, "\n");
|
|
if (use_errno)
|
|
perror("The error was");
|
|
errors++;
|
|
}
|
|
|
|
void
|
|
error(int use_errno, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
fprintf(stderr, "crlf: Error at pc=%d: ", pc);
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
fprintf(stderr, "\n");
|
|
if (use_errno)
|
|
perror("The error was");
|
|
fprintf(stderr, "FAIL\n");
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
display_buf(const char *which, Buffer *buf, int ofs)
|
|
{
|
|
int i;
|
|
fprintf(stderr, "%s %04x:", which, ofs);
|
|
for (i=0; i<8; i++)
|
|
if (i+ofs < buf->count)
|
|
{
|
|
unsigned char b = buf->bytes[i+ofs];
|
|
fprintf(stderr, " %02x", b);
|
|
if (isgraph(b))
|
|
fprintf(stderr, " %c ", b);
|
|
else
|
|
fprintf(stderr, " . "/*, b*/);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
void
|
|
compare_bufs(const char *name, Buffer *actual, Buffer *expected)
|
|
{
|
|
int i, got_one=0;
|
|
for (i=0; i<actual->count && i<expected->count; i++)
|
|
if (actual->bytes[i] != expected->bytes[i])
|
|
{
|
|
errorq(0, "%s: byte mismatch at offset 0x%x", name, i);
|
|
got_one = 1;
|
|
break;
|
|
}
|
|
if (!got_one)
|
|
{
|
|
if (actual->count < expected->count)
|
|
errorq(0, "%s: too few bytes (0x%x vs 0x%x)", name,
|
|
actual->count, expected->count);
|
|
else if (actual->count > expected->count)
|
|
errorq(0, "%s: too many bytes (0x%x vs 0x%x)", name,
|
|
actual->count, expected->count);
|
|
else
|
|
return;
|
|
}
|
|
|
|
i -= 4;
|
|
if (i<0) i = 0;
|
|
display_buf("Actual ", actual, i);
|
|
display_buf("Expected", expected, i);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
const char *readmode = "rb";
|
|
const char *writemode = "wb";
|
|
FILE *file = 0;
|
|
int i;
|
|
struct stat st;
|
|
const char *str = "";
|
|
|
|
while (argc > 1 && argv[1][0] == '-')
|
|
{
|
|
if (strcmp(argv[1], "-v") == 0)
|
|
verbose++;
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
size = 0;
|
|
|
|
for (pc=0; pc<num_commands; pc++)
|
|
{
|
|
switch (commands[pc])
|
|
{
|
|
case Nop:
|
|
vp("Nop");
|
|
break;
|
|
|
|
case New1:
|
|
i = commands[++pc];
|
|
vp("New %d", i);
|
|
if (file) fclose(file);
|
|
file = 0;
|
|
remove(dataname);
|
|
break;
|
|
|
|
case Open:
|
|
vp("Open");
|
|
if (file) fclose(file);
|
|
file = 0;
|
|
break;
|
|
|
|
case Read:
|
|
if (!file)
|
|
{
|
|
file = fopen(dataname, readmode);
|
|
if (size)
|
|
setvbuf(file, 0, _IOFBF, size);
|
|
}
|
|
if (!file)
|
|
error(1, "cannot open %s, mode %s", dataname, readmode);
|
|
askfor = commands[++pc];
|
|
get = commands[++pc];
|
|
vp("Read %d %d", askfor, get);
|
|
expand_buf(&rw_buf, askfor);
|
|
if (askfor == 1)
|
|
{
|
|
i = getc(file);
|
|
rw_buf.bytes[0] = i;
|
|
i = (i == EOF) ? 0 : 1;
|
|
}
|
|
else
|
|
i = fread(rw_buf.bytes, 1, askfor, file);
|
|
if (i != get)
|
|
error(0, "read wrong number of bytes (%d vs %d)", i, get);
|
|
rw_buf.count = i;
|
|
break;
|
|
|
|
case Write:
|
|
if (!file)
|
|
{
|
|
file = fopen(dataname, writemode);
|
|
if (size)
|
|
setvbuf(file, 0, _IOFBF, size);
|
|
}
|
|
if (!file)
|
|
error(1, "cannot open %s, mode %s\n", dataname, writemode);
|
|
expect = commands[++pc];
|
|
get_bytestream(&rw_buf);
|
|
vp("Write %d %d", rw_buf.count, expect);
|
|
if (askfor == 1)
|
|
{
|
|
i = putc(rw_buf.bytes[0], file);
|
|
i = (i == EOF) ? 0 : 1;
|
|
}
|
|
else
|
|
i = fwrite(rw_buf.bytes, 1, rw_buf.count, file);
|
|
if (expect && (i != expect))
|
|
error(0, "wrote wrong number of bytes (%d vs %d)", i, expect);
|
|
break;
|
|
|
|
case Compare:
|
|
get_bytestream(&cmp_buf);
|
|
vp("Compare %d/%d", rw_buf.count, cmp_buf.count);
|
|
compare_bufs("Compare", &rw_buf, &cmp_buf);
|
|
break;
|
|
|
|
case Verify:
|
|
if (file) fclose(file);
|
|
file = 0;
|
|
get_bytestream(&cmp_buf);
|
|
vp("Verify %s", dataname);
|
|
if (stat(dataname, &st))
|
|
error(1, "Can't stat %s", dataname);
|
|
expand_buf(&vfy_buf, st.st_size);
|
|
i = open(dataname, O_RDONLY|O_BINARY, 0);
|
|
vfy_buf.count = read(i, vfy_buf.bytes, st.st_size);
|
|
close(i);
|
|
compare_bufs("Verify", &vfy_buf, &cmp_buf);
|
|
break;
|
|
|
|
case Seek:
|
|
posn = commands[++pc];
|
|
whence = commands[++pc];
|
|
switch (whence)
|
|
{
|
|
case Start:
|
|
whence = SEEK_SET;
|
|
str = "Start";
|
|
break;
|
|
case Current:
|
|
whence = SEEK_CUR;
|
|
str = "Current";
|
|
break;
|
|
case End:
|
|
whence = SEEK_END;
|
|
str = "End";
|
|
break;
|
|
}
|
|
vp("Seek 0x%x %s", posn, str);
|
|
i = fseek(file, posn, whence);
|
|
if (i)
|
|
error(1, "fseek failed");
|
|
break;
|
|
|
|
case Tell:
|
|
posn = commands[++pc];
|
|
vp("Tell 0x%x", posn);
|
|
i = ftell(file);
|
|
if (i != posn)
|
|
error(0, "ftell failed, got 0x%x expected 0x%x", i, posn);
|
|
break;
|
|
|
|
case BufSize:
|
|
size = commands[++pc];
|
|
vp("BufSize 0x%x", size);
|
|
if (file)
|
|
{
|
|
fflush(file);
|
|
setvbuf(file, 0, _IOFBF, size);
|
|
}
|
|
break;
|
|
|
|
case Flush:
|
|
vp("Flush");
|
|
if (file)
|
|
fflush(file);
|
|
break;
|
|
|
|
case Text:
|
|
vp("Text");
|
|
readmode = "rt";
|
|
writemode = "wt";
|
|
break;
|
|
|
|
case Binary:
|
|
vp("Binary");
|
|
readmode = "rb";
|
|
writemode = "wb";
|
|
break;
|
|
|
|
default:
|
|
printf("Invalid command code %d at offset %d\n", commands[pc], pc);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (file) fclose(file);
|
|
|
|
if (errors)
|
|
printf("FAIL: %d error%s\n", errors, errors==1?"":"s");
|
|
else
|
|
{
|
|
printf("PASS\n");
|
|
unlink (dataname);
|
|
}
|
|
return errors;
|
|
}
|