int bcopy_01() { BFILE* in; BFILE* out; int i = 0; in = bfopen("test.txt", "rb"); TEST_EXPECT(in != NULL); out = bfopen("copy.txt", "wb"); TEST_EXPECT(out != NULL); while (!bfeof(in)) { bfputc(bfgetc(in), out); i++; } bfclose(in); bfclose(out); if (i == LENGTH_OF_TEST_FILE) { TEST_SUCCESS; } else { TEST_FAILURE("didn't copy all expected data (total bytes copied incorrect)."); } }
void test2(void) { FILE *in1; bfile *in2; int ch; in1 = fopen("bitfiles.c", "rb"); in2 = bfopen("bitfiles.cc", "rb"); if ((NULL == in1) || (NULL == in2)) { printf("Can't open test files\n"); exit(1); } while (!feof(in1) && !feof(in2->file)) { ch = fgetc(in1); if (ch < ' ') ch = '.'; printf(" '%c' ", ch); for (ch = 0; ch < 8; ch++) printf("%c", "01"[bfread(in2)]); printf(" "); } fclose(in1); bfclose(in2); }
void test1(void) { bfile *out; bfile *in; FILE *in1; FILE *in2; in = bfopen("bitfiles.c", "rb"); out = bfopen("bitfiles.cc", "wb"); if ((NULL == in) || (NULL == out)) { printf("Can't open/create test files\n"); exit(1); } while (!feof(in->file)) bfwrite(bfread(in), out); bfclose(in); bfclose(out); in1 = fopen("bitfiles.c", "rb"); in2 = fopen("bitfiles.cc", "rb"); if ((NULL == in1) || (NULL == in2)) { printf("Can't open test files for verifying\n"); exit(1); } while (!feof(in1) && !feof(in2)) { if (fgetc(in1) != fgetc(in2)) { printf("Files not identical, copy failed!\n"); exit(1); } } if (!feof(in1) || !feof(in2)) { printf("Not same size, copy failed!\n"); exit(1); } fclose(in1); fclose(in2); }
int bopen_01() { BFILE* f; f = bfopen("nonexistant.blah", "rb"); TEST_EXPECT(f == NULL); f = bfopen("test.txt", "rb"); TEST_EXPECT(f != NULL); bfclose(f); TEST_SUCCESS; }
/* Encodes a block of up to BUF samples. The first output is a 32-bit integer showing the size of the block. After that follow 3 blocks of that size containing the delta-encoding of the timestamp, then the humidity data, and the temperature data. Returns the block size or EOF on error. */ int handleblock() { char buf[64]; int ret; int32_t i; struct tm tm = (struct tm) {0}; tm.tm_isdst = -1; int32_t diffs[BUF][3]; time_t oldtime, curtime; int32_t hum, humd, temp, tempd; int32_t oldhum, curhum, oldtemp, curtemp; /* Read up to BUF samples into the diffs[] array */ if(fgets(buf, 64, stdin) == NULL) { return EOF; } ret = sscanf(buf, "%d-%d-%d_%d:%d:%d %d.%d %d.%d\n", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &hum, &humd, &temp, &tempd); if(ret != 10) { return EOF; } tm.tm_year -= 1900; tm.tm_mon -= 1; oldtime = mktime(&tm); oldhum = hum * 10 + humd; oldtemp = temp * 10 + tempd; diffs[0][0] = oldtime; diffs[0][1] = oldhum; diffs[0][2] = oldtemp; for(i = 1; i < BUF; i++) { if(fgets(buf, 64, stdin) == NULL) { /* End of file, stop */ break; } ret = sscanf(buf, "%d-%d-%d_%d:%d:%d %d.%d %d.%d\n", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &hum, &humd, &temp, &tempd); if(ret != 10) { if(ret == 0) { /* Maybe an empty line */ break; } else { /* Other crap */ return EOF; } } tm.tm_year -= 1900; tm.tm_mon -= 1; curtime = mktime(&tm); curhum = hum * 10 + humd; curtemp = temp * 10 + tempd; diffs[i][0] = curtime - oldtime; diffs[i][1] = curhum - oldhum; diffs[i][2] = curtemp - oldtemp; oldtime = curtime; oldhum = curhum; oldtemp = curtemp; } /* Write the block size */ bfwrite(i, 32, bf); /* Pack the timestamps, then the humidity, then the temperature. */ for(int j = 0; j < 3; j++) { for(int k = 0; k < i; k++) { if(pack(diffs[k][j]) == EOF) { return EOF; } } } return i; } int main() { bf = bfopen(stdout); bfwrite(PACKBITS, 8, bf); while(handleblock() != EOF); bfclose(bf); return 0; }
uint16_t aout_write(FILE* out, bool relocatable, bool intermediate) { struct aout_byte* current_outer; struct aout_byte* current_inner; struct lprov_entry* linker_provided = NULL; struct lprov_entry* linker_required = NULL; struct lprov_entry* linker_adjustment = NULL; struct lprov_entry* linker_section = NULL; struct lprov_entry* linker_output = NULL; struct lprov_entry* linker_temp = NULL; uint32_t mem_index, out_index; uint16_t inst; BFILE* temp = NULL; bstring bname, ename; uint16_t eaddr; bool did_find; bool shown_expr_warning = false; bool has_output = false; // Initialize out our extension table. code_offset += textn_init(start); // If relocatable, initialize out our relocation table. if (relocatable) code_offset += treloc_init(start); // First go through and evaluate all expressions that need to be. current_outer = start; out_index = code_offset; while (current_outer != NULL) { if (current_outer->type == AOUT_TYPE_METADATA_ORIGIN) { // Adjust memory address. out_index = current_outer->opcode; } else if (current_outer->type == AOUT_TYPE_METADATA_SECTION) { assert(current_outer->label != NULL); // We're exporting the current address as the beginning // of a section. if (!intermediate) ahalt(ERR_NOT_GENERATING_INTERMEDIATE_CODE, NULL); // Check to make sure outputs haven't previously been emitted. if (has_output) ahalt(ERR_OUTPUT_BEFORE_SECTION, NULL); // Create linker entry. linker_temp = lprov_create(current_outer->label, out_index); linker_temp->next = linker_section; linker_section = linker_temp; printd(LEVEL_VERBOSE, "LINK SECTION %s -> 0x%04X\n", current_outer->label, out_index); } else if (current_outer->type == AOUT_TYPE_METADATA_OUTPUT) { assert(current_outer->label != NULL); // We're exporting the current address as the beginning // of a section. if (!intermediate) ahalt(ERR_NOT_GENERATING_INTERMEDIATE_CODE, NULL); // Create linker entry. has_output = true; linker_temp = lprov_create(current_outer->label, out_index); linker_temp->next = linker_output; linker_output = linker_temp; printd(LEVEL_VERBOSE, "LINK OUTPUT 0x%04X -> %s\n", out_index, current_outer->label); } else if (current_outer->type == AOUT_TYPE_METADATA_EXPORT) { assert(current_outer->label != NULL); // We're exporting the address of this label in the // object table. if (!intermediate) ahalt(ERR_NOT_GENERATING_INTERMEDIATE_CODE, NULL); // Resolve label position. ename = bfromcstr(current_outer->label); eaddr = aout_get_label_address(ename); bdestroy(ename); // Create linker entry. linker_temp = lprov_create(current_outer->label, eaddr); linker_temp->next = linker_provided; linker_provided = linker_temp; printd(LEVEL_VERBOSE, "LINK REPLACE %s -> 0x%04X\n", current_outer->label, eaddr); } else if (current_outer->type == AOUT_TYPE_NORMAL && current_outer->expr != NULL) { if (current_outer->expr->type != EXPR_LABEL) { // This is either just a number or a more complicated expression, so // evaluate it using the preprocessor expression engine. if ((relocatable || intermediate) && !shown_expr_warning) { printd(LEVEL_WARNING, "warning: expressions will not be adjusted at link or relocation time.\n"); printd(LEVEL_WARNING, " ensure labels are not used as part of expressions.\n"); shown_expr_warning = true; } current_outer->raw_used = true; current_outer->raw = expr_evaluate(current_outer->expr, &aout_get_label_address, &ahalt_expression_exit_handler); expr_delete(current_outer->expr); current_outer->expr = NULL; } else { // If this is just a label, we can handle it directly (this allows // us to handle imported labels in intermediate code). current_inner = start; mem_index = code_offset; did_find = false; // Search for .IMPORT directives first. while (current_inner != NULL) { if (current_inner->type == AOUT_TYPE_METADATA_ORIGIN) { // Adjust memory address. mem_index = current_inner->opcode; } else if (current_inner->type == AOUT_TYPE_METADATA_IMPORT) { // An imported label (we don't need to adjust // memory index because the existance of this type // of entry doesn't affect executable size). if (!intermediate) ahalt(ERR_NOT_GENERATING_INTERMEDIATE_CODE, NULL); assert(current_outer->expr->data != NULL); if (strcmp(current_inner->label, ((bstring)current_outer->expr->data)->data) == 0) { // We don't actually know our position yet; // that will be handled by the linker! current_outer->raw = 0xFFFF; expr_delete(current_outer->expr); current_outer->expr = NULL; linker_temp = lprov_create(current_inner->label, out_index); linker_temp->next = linker_required; linker_required = linker_temp; printd(LEVEL_VERBOSE, "LINK REPLACE 0x%04X -> %s\n", out_index, current_inner->label); did_find = true; break; } } // Goto next. current_inner = current_inner->next; } // If it wasn't found in the .IMPORT directives, try searching // labels directly using the expression engine. if (!did_find) { // Replace the label position. current_outer->raw_used = true; current_outer->raw = expr_evaluate(current_outer->expr, &aout_get_label_address, &ahalt_expression_exit_handler); expr_delete(current_outer->expr); current_outer->expr = NULL; did_find = true; // We also need to add this entry to the adjustment // table for the linker since it also needs to adjust // internal label jumps in files when it concatenates // all of the object code together. linker_temp = lprov_create(NULL, out_index); linker_temp->next = linker_adjustment; linker_adjustment = linker_temp; printd(LEVEL_VERBOSE, "LINK ADJUST 0x%04X\n", out_index); } } } if (current_outer->type == AOUT_TYPE_NORMAL && current_outer->label == NULL) out_index += 1; current_outer = current_outer->next; } // If intermediate, we need to write out our linker table // as the absolute first thing in the file. if (intermediate) { fwrite(ldata_objfmt, 1, strlen(ldata_objfmt) + 1, out); objfile_save(out, linker_provided, linker_required, linker_adjustment, linker_section, linker_output); // Adjust the "true origin" for .ORIGIN directivies because // the linker table won't exist in the final result when // linked. true_origin = (uint16_t)ftell(out); } // Write out our extension table. textn_write(out); // If relocatable, write out our relocation table. if (relocatable) treloc_write(out); // Now write to the file. current_outer = start; while (current_outer != NULL) { if (current_outer->type == AOUT_TYPE_METADATA_ORIGIN) { // Adjust origin. fseek(out, true_origin + current_outer->opcode * 2 /* double because the number is in words, not bytes */, SEEK_SET); } else if (current_outer->type == AOUT_TYPE_METADATA_INCBIN) { // Include binary file. bname = ppfind_locate(bautofree(bfromcstr(current_outer->label))); if (bname == NULL) ahalt(ERR_UNABLE_TO_INCBIN, current_outer->label); temp = bfopen((const char*)(bname->data), "rb"); if (temp == NULL) ahalt(ERR_UNABLE_TO_INCBIN, current_outer->label); // Copy binary data. while (!bfeof(temp)) { // TODO: This could be faster if we didn't do it character // by character. fputc(bfgetc(temp), out); } // Finalize. bfclose(temp); bdestroy(bname); } else if (current_outer->type == AOUT_TYPE_NORMAL) { // Update the debugging symbol. dbgfmt_update_symbol_list(¤t_outer->symbols, (uint16_t)((ftell(out) - true_origin) / 2)); // Normal output. if (current_outer->raw_used == true) { inst = current_outer->raw; iwrite(&inst, out); } else if (current_outer->label == NULL) { inst = INSTRUCTION_CREATE(current_outer->opcode, current_outer->a, current_outer->b); iwrite(&inst, out); } } // Goto next in linked list. current_outer = current_outer->next; } fflush(out); return (uint16_t)((ftell(out) - true_origin) / 2); }
int main(int argc, char* argv[]) { bstring ldargs = bfromcstr(""); int i, result; unsigned int match = 0, unmatch = 0; char ca, ce; BFILE* expect; BFILE* actual; // Define arguments. struct arg_lit* show_help = arg_lit0("h", "help", "Show this help."); struct arg_lit* gen_relocatable = arg_lit0("r", "relocatable", "Generate relocatable code."); struct arg_lit* gen_intermediate = arg_lit0("i", "intermediate", "Generate intermediate code for use with the linker."); struct arg_lit* little_endian_mode = arg_lit0(NULL, "little-endian", "Use little endian serialization."); struct arg_file* input_file = arg_file1(NULL, NULL, "<file>", "The input assembly file."); struct arg_file* expect_file = arg_file0("e", "expect", "<file>", "The output file that contains expected output."); struct arg_file* actual_file = arg_file1("a", "actual", "<file>", "The output file where actual output will be placed."); struct arg_file* symbols_file = arg_file0("s", "debug-symbols", "<file>", "The debugging symbol output file."); struct arg_lit* fail_opt = arg_lit0("f", "fail", "The assembler is expected to fail and the actual output file should not exist on completion."); struct arg_file* path = arg_file1("p", NULL, "<path>", "The path to the assembler."); struct arg_lit* verbose = arg_litn("v", NULL, 0, LEVEL_EVERYTHING - LEVEL_DEFAULT, "Increase verbosity."); struct arg_lit* quiet = arg_litn("q", NULL, 0, LEVEL_DEFAULT - LEVEL_SILENT, "Decrease verbosity."); struct arg_end* end = arg_end(20); void* argtable[] = { show_help, gen_relocatable, gen_intermediate, little_endian_mode, symbols_file, input_file, expect_file, actual_file, fail_opt, path, verbose, quiet, end }; // Parse arguments. int nerrors = arg_parse(argc, argv, argtable); version_print(bautofree(bfromcstr("Assembler Test Driver"))); if (nerrors != 0 || show_help->count != 0 || (fail_opt->count == 0 && (expect_file->count == 0 || actual_file->count == 0))) { if (show_help->count != 0 && fail_opt->count == 0 && (expect_file->count == 0 || actual_file->count == 0)) printd(LEVEL_ERROR, "error: you must provide either -f or -e and -a.\n"); if (show_help->count != 0) arg_print_errors(stderr, end, "testasm"); printd(LEVEL_DEFAULT, "syntax:\n testasm"); arg_print_syntax(stderr, argtable, "\n"); printd(LEVEL_DEFAULT, "options:\n"); arg_print_glossary(stderr, argtable, " %-25s %s\n"); arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); return 1; } // Set verbosity level. debug_setlevel(LEVEL_DEFAULT + verbose->count - quiet->count); // Set global path variable. osutil_setarg0(bautofree(bfromcstr(argv[0]))); // Generate the argument list for the assembler. ldargs = bfromcstr(path->filename[0]); binsertch(ldargs, 0, 1, '"'); bconchar(ldargs, '"'); bconchar(ldargs, ' '); // Verbosity options. if (verbose->count > 0) { bconchar(ldargs, '-'); for (i = 0; i < verbose->count; i++) bconchar(ldargs, 'v'); bconchar(ldargs, ' '); } if (quiet->count > 0) { bconchar(ldargs, '-'); for (i = 0; i < quiet->count; i++) bconchar(ldargs, 'q'); bconchar(ldargs, ' '); } // Literal options. if (gen_relocatable->count > 0) { bconchar(ldargs, '-'); for (i = 0; i < gen_relocatable->count; i++) bconchar(ldargs, 'r'); bconchar(ldargs, ' '); } if (gen_intermediate->count > 0) { bconchar(ldargs, '-'); for (i = 0; i < gen_intermediate->count; i++) bconchar(ldargs, 'i'); bconchar(ldargs, ' '); } if (little_endian_mode->count > 0) { for (i = 0; i < little_endian_mode->count; i++) bcatcstr(ldargs, "--little-endian "); } // Unlink the actual file so that if we are expecting // failure, we won't return incorrectly. unlink(actual_file->filename[0]); // Output file. bcatcstr(ldargs, "-o \""); bcatcstr(ldargs, actual_file->filename[0]); bcatcstr(ldargs, "\" "); // Input file. bcatcstr(ldargs, "\""); bcatcstr(ldargs, input_file->filename[0]); bcatcstr(ldargs, "\" "); // Windows needs the whole command wrapped in quotes and slashes to be correct. // See http://stackoverflow.com/questions/2642551/windows-c-system-call-with-spaces-in-command. #ifdef _WIN32 binsertch(ldargs, 0, 1, '"'); bconchar(ldargs, '"'); #endif // Now run the assembler! result = system(ldargs->data); if (result != 0 && fail_opt->count == 0) { // Assembler returned error exit code. printd(LEVEL_ERROR, "error: expected success but assembler returned non-zero exit code (%i).\n", result); return 1; } else if (result == 0 && fail_opt->count >= 1) { // Assembler returned zero when failure was expected. printd(LEVEL_ERROR, "error: expected failure but assembler returned zero exit code.\n"); return 1; } else if (result != 0 && fail_opt->count >= 1) { // Assembler failed and we expected it to. Return success only // if the output file does not exist. actual = bfopen(actual_file->filename[0], "rb"); if (actual != NULL) { printd(LEVEL_ERROR, "error: expected failure but actual output file exists.\n"); bfclose(actual); return 1; } return 0; } // Open expect data. expect = bfopen(expect_file->filename[0], "rb"); if (expect == NULL) { // The expect file was not provided. printd(LEVEL_ERROR, "error: path to expect file does not exist.\n"); return 1; } // Open actual data. actual = bfopen(actual_file->filename[0], "rb"); if (actual == NULL) { // The expect file was not provided. bfclose(expect); printd(LEVEL_ERROR, "error: expected data but actual output file does not exist after running assembler.\n"); return 1; } // Now compare raw bytes. while (true) { if (!bfeof(actual) && !bfeof(expect)) { ca = bfgetc(actual); ce = bfgetc(expect); if (ca == ce) match++; else { printd(LEVEL_WARNING, "warning: byte at 0x%04X is different (got 0x%02X, expected 0x%02X)!\n", bftell(actual), ca, ce); unmatch++; } } else if (!bfeof(actual)) { ca = bfgetc(actual); printd(LEVEL_ERROR, "error: actual output contained trailing byte 0x%02X.\n", (unsigned char)ca); unmatch++; } else if (!bfeof(expect)) { ce = bfgetc(expect); printd(LEVEL_ERROR, "error: expected actual output to contain 0x%02X.\n", (unsigned char)ce); unmatch++; } else break; } if (unmatch > 0) { printd(LEVEL_ERROR, "error: actual output differs from expected output in content (%f%%, %i bytes different).\n", 100.f / (unmatch + match) * unmatch, unmatch); if (bftell(actual) != bftell(expect)) printd(LEVEL_ERROR, "error: actual output differs from expected output in length (%i bytes larger).\n", bftell(actual) - bftell(expect)); bfclose(actual); bfclose(expect); return 1; } // Close files and delete actual because we have // succeeded. bfclose(actual); bfclose(expect); unlink(actual_file->filename[0]); return 0; }