/* load_prog.c */ #include #include #include #include #include #include #include "getline.h" #include "pcode.h" #include "vec/vec.h" /* Ordered list of chunk types in the input file. */ typedef enum chunk { CHUNK_DATA , /* Initial program data. */ CHUNK_TEXT , /* Program text. */ CHUNK_CARDS, /* Input cards. */ CHUNK_COUNT /* Dummy enum entry equal to 1 + final chunk. */ } chunk_t; static int read_word(interp_t* interp, word_t* output, int eof) { char* buf = NULL, *ws = " \f\r\t\v", *p, *q; size_t len = 0; int ret, retval; word_t word; again: /* Read one line. */ ++interp->input_line; if ((ret = getline(&buf, &len, interp->input)) == -1) { if (!feof(interp->input)) { /* Something really bad. */ perror("getline"); retval = -1; goto final; } else if (eof) { /* EOF is an error. */ fprintf(stderr, "Premature end-of-file"); goto error; } else { /* EOF is acceptable. */ retval = 0; goto final; } } /* Trim newline. Note that getline() never returns 0. */ if (buf[ret - 1] == '\n') buf[ret - 1] = 0; p = buf + strspn(buf, ws) ; /* Skip initial space. */ if (*p == 0 || *p == '#') goto again ; /* Blank or comment line. */ if (*p != '-' && *p != '+') goto bogon ; /* Bad character. */ /* Load word, eliminating embedded whitespace. */ q = word.str; *q++ = *p++; /* Sign. */ for (; q != word.str + sizeof(word.str); ++p) { if (*p == 0 || *p == '#') goto trunc ; /* Truncated line. */ if (isspace(*p)) ; /* Whitespace. */ else if (*p == '.') *q++ = '0' ; /* Period. */ else if (isdigit(*p)) *q++ = *p ; /* Digit. */ else goto bogon ; /* Bad character. */ } /* Check remainder of line. */ p += strspn(p, ws) ; /* Skip more whitespace. */ if (*p != 0 && *p != '#') goto bogon ; /* Bad character. */ else ; /* Done. Fall through. */ /* Success. */ *output = word; retval = 1; goto final; trunc: fprintf(stderr, "Not enough digits"); goto error; bogon: fprintf(stderr, "Invalid character `%c' (position %d)", *p, p - buf + 1); goto error; error: fprintf(stderr, " at input line %d\n", interp->input_line); retval = -1; goto final; final: free(buf); return retval; } static int read_chunk(interp_t* interp, chunk_t type) { char sign; int i, retval = 0, value; word_t word; data_t* data = interp->vm.data; insn_t* text = interp->vm.text; data_t card; switch (type) { case CHUNK_DATA: case CHUNK_TEXT: for (i = 0; i < 1001; ++i) { /* Get the next word. */ if (read_word(interp, &word, 1) < 0) return -1; /* Check for sentinel value, or lack thereof. */ if (memcmp(word.str, "+9999999999", 11) == 0) { /* Reached sentinel. */ retval = i; break; } else if (i == 1000) { /* Uh oh, no sentinel. */ fprintf(stderr, "Missing sentinel or input data too long\n"); return -1; } /* There is no reason for sscanf()/mpz_set_str() to fail. */ switch (type) { case CHUNK_DATA: /* Data value. */ mpz_init(data[i].val); gmp_sscanf(word.str, "%Zd", data[i].val); break; case CHUNK_TEXT: /* Instruction. */ sscanf(word.str, "%c%1d%3d%3d%3d", &sign, &value, &text[i].arg[0], &text[i].arg[1], &text[i].arg[2]); text[i].opcode = value * 2 + (sign == '-' ? 1 : 0); } } /* Initialize any remaining memory. */ switch (type) { case CHUNK_DATA: for (; i < 1000; ++i) mpz_init(data[i].val); break; case CHUNK_TEXT: for (; i < 1000; ++i) text[i].opcode = 18; } /* Done! */ return retval; case CHUNK_CARDS: for (i = 0;; ++i) { switch (read_word(interp, &word, 0)) { case -1: return -1; case 0: return i; case 1: mpz_init(card.val); gmp_sscanf(word.str, "%Zd", card.val); VEC_UNSHIFT1(data_t, &interp->vm.cards, card); } } } } int load_prog(interp_t* interp) { chunk_t type; /* Load all chunks from the input file. */ interp->input_line = 0; VEC_INIT(data_t, &interp->vm.cards); for (type = 0; type < CHUNK_COUNT; ++type) { if (read_chunk(interp, type) < 0) return -1; } /* Done with input file, so close it. */ fclose(interp->input); interp->input = NULL; return 0; } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */