diff --git a/src/main.c b/src/main.c index ea110da2..f480b315 100644 --- a/src/main.c +++ b/src/main.c @@ -135,72 +135,6 @@ static void usage(void) } -static void update_progress_tty (int percent, double etime, char *hdr) -{ - static char hashes[51]; - static char *header; - static int last = 0; - int i; - - setvbuf(stderr, (char*)NULL, _IONBF, 0); - - hashes[50] = 0; - - memset (hashes, ' ', 50); - for (i=0; i>1)*2; - - setvbuf(stderr, (char*)NULL, _IONBF, 0); - - if (hdr) { - avrdude_message(MSG_INFO, "\n%s | ", hdr); - last = 0; - done = 0; - } - else { - while ((cnt > last) && (done == 0)) { - avrdude_message(MSG_INFO, "#"); - cnt -= 2; - } - } - - if ((percent == 100) && (done == 0)) { - avrdude_message(MSG_INFO, " | 100%% %0.2fs\n\n", etime); - last = 0; - done = 1; - } - else - last = (percent>>1)*2; /* Make last a multiple of 2. */ - - setvbuf(stderr, (char*)NULL, _IOLBF, 0); -} - static void list_programmers_callback(const char *name, const char *desc, const char *cfgname, int cfglineno, void *cookie) @@ -760,18 +694,8 @@ int main(int argc, char * argv []) } #endif - if (quell_progress == 0) { - if (isatty (STDERR_FILENO)) - update_progress = update_progress_tty; - else { - update_progress = update_progress_no_tty; - /* disable all buffering of stderr for compatibility with - software that captures and redirects output to a GUI - i.e. Programmers Notepad */ - setvbuf( stderr, NULL, _IONBF, 0 ); - setvbuf( stdout, NULL, _IONBF, 0 ); - } - } + if (quell_progress == 0) + terminal_setup_update_progress(); /* * Print out an identifying string so folks can tell what version diff --git a/src/term.c b/src/term.c index dc771273..f02f95c5 100644 --- a/src/term.c +++ b/src/term.c @@ -25,7 +25,10 @@ #include #include #include +#include #include +#include +#include #if defined(HAVE_LIBREADLINE) # include @@ -90,14 +93,17 @@ static int cmd_pgm (PROGRAMMER * pgm, struct avrpart * p, static int cmd_verbose (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]); +static int cmd_quell (PROGRAMMER * pgm, struct avrpart * p, + int argc, char *argv[]); + struct command cmd[] = { - { "dump", cmd_dump, "dump memory : %s " }, + { "dump", cmd_dump, "%s [ | ... | | ...]" }, { "read", cmd_dump, "alias for dump" }, - { "write", cmd_write, "write memory : %s ... " }, + { "write", cmd_write, "%s [[,] {[,]} | [,] {[,]} ...]" }, { "erase", cmd_erase, "perform a chip erase" }, { "sig", cmd_sig, "display device signature bytes" }, { "part", cmd_part, "display the current part information" }, - { "send", cmd_send, "send a raw command : %s " }, + { "send", cmd_send, "send a raw command: %s " }, { "parms", cmd_parms, "display adjustable parameters (STK500 and Curiosity Nano only)" }, { "vtarg", cmd_vtarg, "set (STK500 and Curiosity Nano only)" }, { "varef", cmd_varef, "set (STK500 only)" }, @@ -106,12 +112,13 @@ struct command cmd[] = { { "spi", cmd_spi, "enter direct SPI mode" }, { "pgm", cmd_pgm, "return to programming mode" }, { "verbose", cmd_verbose, "change verbosity" }, + { "quell", cmd_quell, "set quell level for progress bars" }, { "help", cmd_help, "help" }, { "?", cmd_help, "help" }, { "quit", cmd_quit, "quit" } }; -#define NCMDS (sizeof(cmd)/sizeof(struct command)) +#define NCMDS ((int)(sizeof(cmd)/sizeof(struct command))) @@ -119,19 +126,22 @@ static int spi_mode = 0; static int nexttok(char * buf, char ** tok, char ** next) { - char * q, * n; + unsigned char *q, *n; - q = buf; - while (isspace((int)*q)) + q = (unsigned char *) buf; + while (isspace(*q)) q++; /* isolate first token */ n = q; uint8_t quotes = 0; - while (*n && (!isspace((int)*n) || quotes)) { - if (*n == '\"') + while (*n && (!isspace(*n) || quotes)) { + // poor man's quote and escape processing + if (*n == '"' || *n == '\'') quotes++; - else if (isspace((int)*n) && *(n-1) == '\"') + else if(*n == '\\' && n[1]) + n++; + else if (isspace(*n) && (n > q+1) && (n[-1] == '"' || n[-1] == '\'')) break; n++; } @@ -142,11 +152,11 @@ static int nexttok(char * buf, char ** tok, char ** next) } /* find start of next token */ - while (isspace((int)*n)) + while (isspace(*n)) n++; - *tok = q; - *next = n; + *tok = (char *) q; + *next = (char *) n; return 0; } @@ -156,8 +166,8 @@ static int hexdump_line(char * buffer, unsigned char * p, int n, int pad) { char * hexdata = "0123456789abcdef"; char * b = buffer; - int32_t i = 0; - int32_t j = 0; + int i = 0; + int j = 0; for (i=0; i sizeof b? sizeof b: n; + + memcpy(b, p, n); + for (int i = 0; i < n; i++) + buffer[i] = isascii(b[i]) && isspace(b[i])? ' ': + isascii(b[i]) && isgraph(b[i])? b[i]: '.'; for (i = n; i < pad; i++) buffer[i] = ' '; @@ -210,10 +219,10 @@ static int hexdump_buf(FILE * f, int startaddr, unsigned char * buf, int len) char dst1[80]; char dst2[80]; - int32_t addr = startaddr; + int addr = startaddr; unsigned char * p = (unsigned char *)buf; while (len) { - int32_t n = 16; + int n = 16; if (n > len) n = len; hexdump_line(dst1, p, n, 48); @@ -231,44 +240,47 @@ static int hexdump_buf(FILE * f, int startaddr, unsigned char * buf, int len) static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[]) { - if (argc < 2) { - avrdude_message(MSG_INFO, "Usage: %s [ ]\n" - " %s [ <...>]\n" - " %s <...>\n" - " %s \n", - argv[0], argv[0], argv[0], argv[0]); - return -1; + if (argc < 2 || argc > 4) { + terminal_message(MSG_INFO, + "Usage: %s \n" + " %s ...\n" + " %s \n" + " %s ...\n" + " %s \n", + argv[0], argv[0], argv[0], argv[0], argv[0]); + return -1; } enum { read_size = 256 }; - static char prevmem[128] = {0x00}; + static char prevmem[AVR_MEMDESCLEN] = {0x00}; char * memtype = argv[1]; AVRMEM * mem = avr_locate_mem(p, memtype); if (mem == NULL) { - avrdude_message(MSG_INFO, "\"%s\" memory type not defined for part \"%s\"\n", - memtype, p->desc); + terminal_message(MSG_INFO, "%s (dump): %s memory type not defined for part %s\n", + progname, memtype, p->desc); return -1; } - uint32_t maxsize = mem->size; + int maxsize = mem->size; // Get start address if present char * end_ptr; - static uint32_t addr = 0; - if (argc == 4) { + static int addr = 0; + + if (argc >= 3 && strcmp(argv[2], "...") != 0) { addr = strtoul(argv[2], &end_ptr, 0); if (*end_ptr || (end_ptr == argv[2])) { - avrdude_message(MSG_INFO, "%s (%s): can't parse address \"%s\"\n", - progname, argv[0], argv[2]); + terminal_message(MSG_INFO, "%s (dump): can't parse address %s\n", + progname, argv[2]); return -1; } else if (addr >= maxsize) { - avrdude_message(MSG_INFO, "%s (%s): address 0x%05lx is out of range for %s memory\n", - progname, argv[0], addr, mem->desc); + terminal_message(MSG_INFO, "%s (dump): address 0x%05lx is out of range for %s memory\n", + progname, (long) addr, mem->desc); return -1; } } // Get no. bytes to read if present - static int32_t len = read_size; + static int len = read_size; if (argc >= 3) { memset(prevmem, 0x00, sizeof(prevmem)); if (strcmp(argv[argc - 1], "...") == 0) { @@ -278,8 +290,8 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p, } else if (argc == 4) { len = strtol(argv[3], &end_ptr, 0); if (*end_ptr || (end_ptr == argv[3])) { - avrdude_message(MSG_INFO, "%s (%s): can't parse length \"%s\"\n", - progname, argv[0], argv[3]); + terminal_message(MSG_INFO, "%s (dump): can't parse length %s\n", + progname, argv[3]); return -1; } } else { @@ -304,19 +316,19 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p, uint8_t * buf = malloc(len); if (buf == NULL) { - avrdude_message(MSG_INFO, "%s (dump): out of memory\n", progname); + terminal_message(MSG_INFO, "%s (dump): out of memory\n", progname); return -1; } report_progress(0, 1, "Reading"); - for (uint32_t i = 0; i < len; i++) { - int32_t rc = pgm->read_byte(pgm, p, mem, addr + i, &buf[i]); + for (int i = 0; i < len; i++) { + int rc = pgm->read_byte(pgm, p, mem, addr + i, &buf[i]); if (rc != 0) { - avrdude_message(MSG_INFO, "error reading %s address 0x%05lx of part %s\n", - mem->desc, addr + i, p->desc); + terminal_message(MSG_INFO, "%s (dump): error reading %s address 0x%05lx of part %s\n", + progname, mem->desc, (long) addr + i, p->desc); if (rc == -1) - avrdude_message(MSG_INFO, "read operation not supported on memory type \"%s\"\n", - mem->desc); + terminal_message(MSG_INFO, "%*sread operation not supported on memory type %s\n", + (int) strlen(progname)+9, "", mem->desc); return -1; } report_progress(i, len, NULL); @@ -334,49 +346,290 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p, } +// Convert the next n hex digits of s to a hex number +static unsigned int tohex(const unsigned char *s, unsigned int n) { + int ret, c; + + ret = 0; + while(n--) { + ret *= 16; + c = *s++; + ret += c >= '0' && c <= '9'? c - '0': c >= 'a' && c <= 'f'? c - 'a' + 10: c - 'A' + 10; + } + + return ret; +} + +/* + * Create a utf-8 character sequence from a single unicode character. + * Permissive for some invalid unicode sequences but not for those with + * high bit set). Returns numbers of characters written (0-6). + */ +static int wc_to_utf8str(unsigned int wc, unsigned char *str) { + if(!(wc & ~0x7fu)) { + *str = (char) wc; + return 1; + } + if(!(wc & ~0x7ffu)) { + *str++ = (char) ((wc >> 6) | 0xc0); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 2; + } + if(!(wc & ~0xffffu)) { + *str++ = (char) ((wc >> 12) | 0xe0); + *str++ = (char) (((wc >> 6) & 0x3f) | 0x80); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 3; + } + if(!(wc & ~0x1fffffu)) { + *str++ = (char) ((wc >> 18) | 0xf0); + *str++ = (char) (((wc >> 12) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 6) & 0x3f) | 0x80); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 4; + } + if(!(wc & ~0x3ffffffu)) { + *str++ = (char) ((wc >> 24) | 0xf8); + *str++ = (char) (((wc >> 18) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 12) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 6) & 0x3f) | 0x80); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 5; + } + if(!(wc & ~0x7fffffffu)) { + *str++ = (char) ((wc >> 30) | 0xfc); + *str++ = (char) (((wc >> 24) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 18) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 12) & 0x3f) | 0x80); + *str++ = (char) (((wc >> 6) & 0x3f) | 0x80); + *str++ = (char) ((wc & 0x3f) | 0x80); + return 6; + } + return 0; +} + +// Unescape C-style strings, destination d must hold enough space (and can be source s) +static unsigned char *unescape(unsigned char *d, const unsigned char *s) { + unsigned char *ret = d; + int n, k; + + while(*s) { + switch (*s) { + case '\\': + switch (*++s) { + case 'n': + *d = '\n'; + break; + case 't': + *d = '\t'; + break; + case 'a': + *d = '\a'; + break; + case 'b': + *d = '\b'; + break; + case 'e': // Non-standard ESC + *d = 27; + break; + case 'f': + *d = '\f'; + break; + case 'r': + *d = '\r'; + break; + case 'v': + *d = '\v'; + break; + case '?': + *d = '?'; + break; + case '`': + *d = '`'; + break; + case '"': + *d = '"'; + break; + case '\'': + *d = '\''; + break; + case '\\': + *d = '\\'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': // 1-3 octal digits + n = *s - '0'; + for(k = 0; k < 2 && s[1] >= '0' && s[1] <= '7'; k++) // Max 2 more octal characters + n *= 8, n += s[1] - '0', s++; + *d = n; + break; + case 'x': // Unlimited hex digits + for(k = 0; isxdigit(s[k + 1]); k++) + continue; + if(k > 0) { + *d = tohex(s + 1, k); + s += k; + } else { // No hex digits after \x? copy \x + *d++ = '\\'; + *d = 'x'; + } + break; + case 'u': // Exactly 4 hex digits and valid unicode + if(isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) && + (n = wc_to_utf8str(tohex(s+1, 4), d))) { + d += n - 1; + s += 4; + } else { // Invalid \u sequence? copy \u + *d++ = '\\'; + *d = 'u'; + } + break; + case 'U': // Exactly 6 hex digits and valid unicode + if(isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) && isxdigit(s[5]) && isxdigit(s[6]) && + (n = wc_to_utf8str(tohex(s+1, 6), d))) { + d += n - 1; + s += 6; + } else { // Invalid \U sequence? copy \U + *d++ = '\\'; + *d = 'U'; + } + break; + default: // Keep the escape sequence (C would warn and remove \) + *d++ = '\\'; + *d = *s; + } + break; + + default: // Not an escape sequence: just copy the character + *d = *s; + } + d++; + s++; + } + *d = *s; // Terminate + + return ret; +} + + +static size_t maxstrlen(int argc, char **argv) { + size_t max = 0; + + for(int i=0; i max) + max = strlen(argv[i]); + + return max; +} + + +// Change data item p of size bytes from big endian to little endian and vice versa +static void change_endian(void *p, int size) { + uint8_t tmp, *w = p; + + for(int i=0; i \n" - " write <...>\n\n" - " Add a suffix to manually specify the size for each field:\n" - " HH/hh: 8-bit, H/h/S/s: 16-bit, L/l: 32-bit, LL/ll: 64-bit, F/f: 32-bit float\n"); + terminal_message(MSG_INFO, + "Usage: write [,] {[,]} \n" + " write [,] {[,]} ...\n" + "\n" + "Ellipsis ... writes bytes padded by repeating the last item.\n" + "\n" + " can be hexadecimal, octal or decimal integers, floating point numbers\n" + "or C-style strings and characters. For integers, an optional case-insensitive\n" + "suffix specifies the data size: HH 8 bit, H/S 16 bit, L 32 bit, LL 64 bit.\n" + "Suffix D indicates a 64-bit double, F a 32-bit float, whilst a floating point\n" + "number without suffix defaults to 32-bit float. Hexadecimal floating point\n" + "notation is supported. An ambiguous trailing suffix, eg, 0x1.8D, is read as\n" + "no-suffix float where D is part of the mantissa; use a zero exponent 0x1.8p0D\n" + "to clarify.\n" + "\n" + "An optional U suffix makes integers unsigned. Ordinary 0x hex integers are\n" + "always treated as unsigned. +0x or -0x hex numbers are treated as signed\n" + "unless they have a U suffix. Unsigned integers cannot be larger than 2^64-1.\n" + "If n is an unsigned integer then -n is also a valid unsigned integer as in C.\n" + "Signed integers must fall into the [-2^63, 2^63-1] range or a correspondingly\n" + "smaller range when a suffix specifies a smaller type. Out of range signed\n" + "numbers trigger a warning.\n" + "\n" + "Ordinary 0x hex integers with n hex digits (counting leading zeros) use the\n" + "smallest size of 1, 2, 4 and 8 bytes that can accommodate any n-digit hex\n" + "integer. If an integer suffix specifies a size explicitly the corresponding\n" + "number of least significant bytes are written. Otherwise, signed and unsigned\n" + "integers alike occupy the smallest of 1, 2, 4, or 8 bytes needed to\n" + "accommodate them in their respective representation.\n" + ); return -1; } - int32_t i; + int i; uint8_t write_mode; // Operation mode, "standard" or "fill" uint8_t start_offset; // Which argc argument - int32_t len; // Number of bytes to write to memory + int len; // Number of bytes to write to memory char * memtype = argv[1]; // Memory name string AVRMEM * mem = avr_locate_mem(p, memtype); if (mem == NULL) { - avrdude_message(MSG_INFO, "\"%s\" memory type not defined for part \"%s\"\n", - memtype, p->desc); + terminal_message(MSG_INFO, "%s (write): %s memory type not defined for part %s\n", + progname, memtype, p->desc); return -1; } - uint32_t maxsize = mem->size; + int maxsize = mem->size; char * end_ptr; - int32_t addr = strtoul(argv[2], &end_ptr, 0); + int addr = strtoul(argv[2], &end_ptr, 0); if (*end_ptr || (end_ptr == argv[2])) { - avrdude_message(MSG_INFO, "%s (write): can't parse address \"%s\"\n", - progname, argv[2]); + terminal_message(MSG_INFO, "%s (write): can't parse address %s\n", + progname, argv[2]); return -1; } if (addr > maxsize) { - avrdude_message(MSG_INFO, "%s (write): address 0x%05lx is out of range for %s memory\n", - progname, addr, memtype); + terminal_message(MSG_INFO, "%s (write): address 0x%05lx is out of range for %s memory\n", + progname, (long) addr, memtype); return -1; } // Allocate a buffer guaranteed to be large enough - uint8_t * buf = calloc(mem->size + 0x10 + strlen(argv[argc - 2]), sizeof(uint8_t)); + uint8_t * buf = calloc(mem->size + 8 + maxstrlen(argc-3, argv+3)+1, sizeof(uint8_t)); if (buf == NULL) { - avrdude_message(MSG_INFO, "%s (write): out of memory\n", progname); + terminal_message(MSG_INFO, "%s (write): out of memory\n", progname); return -1; } @@ -386,8 +639,8 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p, start_offset = 4; len = strtoul(argv[3], &end_ptr, 0); if (*end_ptr || (end_ptr == argv[3])) { - avrdude_message(MSG_INFO, "%s (write ...): can't parse address \"%s\"\n", - progname, argv[3]); + terminal_message(MSG_INFO, "%s (write ...): can't parse length %s\n", + progname, argv[3]); free(buf); return -1; } @@ -400,141 +653,186 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p, // Structure related to data that is being written to memory struct Data { // Data info - int32_t bytes_grown; + int bytes_grown; uint8_t size; - bool is_float; - bool is_signed; - char * str_ptr; + char *str_ptr; // Data union union { float f; + double d; int64_t ll; + uint64_t ull; uint8_t a[8]; }; } data = { .bytes_grown = 0, .size = 0, - .is_float = false, - .is_signed = false, .str_ptr = NULL, - .ll = 0 + .ull = 1 }; - for (i = start_offset; i < len + start_offset; i++) { - data.is_float = false; - data.size = 0; + if(sizeof(long long) != sizeof(int64_t) || (data.a[0]^data.a[7]) != 1) + terminal_message(MSG_INFO, "%s (write): assumption on data types not met? " + "Check source and recompile\n", progname); + bool is_big_endian = data.a[7]; + for (i = start_offset; i < len + start_offset; i++) { // Handle the next argument if (i < argc - start_offset + 3) { + char *argi = argv[i]; + size_t arglen = strlen(argi); + + data.size = 0; + // Free string pointer if already allocated if(data.str_ptr) { free(data.str_ptr); data.str_ptr = NULL; } - // Get suffix if present - char suffix = argv[i][strlen(argv[i]) - 1]; - char lsuffix = argv[i][strlen(argv[i]) - 2]; - if ((suffix == 'L' && lsuffix == 'L') || (suffix == 'l' && lsuffix == 'l')) { - argv[i][strlen(argv[i]) - 2] = '\0'; - data.size = 8; - } else if (suffix == 'L' || suffix == 'l') { - argv[i][strlen(argv[i]) - 1] = '\0'; - data.size = 4; - } else if ((suffix == 'F' || suffix == 'f') && - strncmp(argv[i], "0x", 2) != 0 && strncmp(argv[i], "-0x", 3) != 0) { - argv[i][strlen(argv[i]) - 1] = '\0'; - data.size = 4; - } else if ((suffix == 'H' && lsuffix == 'H') || (suffix == 'h' && lsuffix == 'h')) { - argv[i][strlen(argv[i]) - 2] = '\0'; - data.size = 1; - } else if (suffix == 'H' || suffix == 'h' || suffix == 'S' || suffix == 's') { - argv[i][strlen(argv[i]) - 1] = '\0'; - data.size = 2; - } else if (suffix == '\'') { - data.size = 1; - } + // Remove trailing comma to allow cut and paste of lists + if(arglen > 0 && argi[arglen-1] == ',') + argi[--arglen] = 0; - // Try integers - data.ll = strtoll(argv[i], &end_ptr, 0); - if (*end_ptr || (end_ptr == argv[i])) { - // Try float - data.f = strtof(argv[i], &end_ptr); - data.is_float = true; - if (*end_ptr || (end_ptr == argv[i])) { - data.is_float = false; - // Try single character - if (argv[i][0] == '\'' && argv[i][2] == '\'') { - data.ll = argv[i][1]; - } else { - // Try string that starts and ends with quotes - if (argv[i][0] == '\"' && argv[i][strlen(argv[i]) - 1] == '\"') { - data.str_ptr = calloc(strlen(argv[i]), sizeof(char)); - if (data.str_ptr == NULL) { - avrdude_message(MSG_INFO, "%s (write str): out of memory\n", progname); - return -1; - } - // Strip start and end quotes - strncpy(data.str_ptr, argv[i] + 1, strlen(argv[i]) - 2); - } else { - avrdude_message(MSG_INFO, "\n%s (write): can't parse data '%s'\n", - progname, argv[i]); - free(buf); - if(data.str_ptr != NULL) - free(data.str_ptr); - return -1; + // Try integers and assign data size + errno = 0; + data.ull = strtoull(argi, &end_ptr, 0); + if (!(end_ptr == argi || errno)) { + unsigned int nu=0, nl=0, nh=0, ns=0, nx=0; + char *p; + + // Parse suffixes: ULL, LL, UL, L ... UHH, HH + for(p=end_ptr; *p; p++) + switch(toupper(*p)) { + case 'U': nu++; break; + case 'L': nl++; break; + case 'H': nh++; break; + case 'S': ns++; break; + default: nx++; + } + + if(nx==0 && nu<2 && nl<3 && nh<3 && ns<2) { // Could be valid integer suffix + if(nu==0 || toupper(*end_ptr) == 'U' || toupper(p[-1]) == 'U') { // If U, then must be at start or end + bool is_hex = strncasecmp(argi, "0x", 2) == 0; // Ordinary hex: 0x... without explicit +/- sign + bool is_signed = !(nu || is_hex); // Neither explicitly unsigned nor ordinary hex + bool is_outside_int64_t = 0; + bool is_out_of_range = 0; + int nhexdigs = p-argi-2; + + if(is_signed) { // Is input in range for int64_t? + errno = 0; (void) strtoll(argi, NULL, 0); + is_outside_int64_t = errno == ERANGE; } + + if(nl==0 && ns==0 && nh==0) { // No explicit data size + // Ordinary hex numbers have implicit size given by number of hex digits, including leading zeros + if(is_hex) { + data.size = nhexdigs > 8? 8: nhexdigs > 4? 4: nhexdigs > 2? 2: 1; + + } else if(is_signed) { + // Smallest size that fits signed representation + data.size = + is_outside_int64_t? 8: + data.ll < INT32_MIN || data.ll > INT32_MAX? 8: + data.ll < INT16_MIN || data.ll > INT16_MAX? 4: + data.ll < INT8_MIN || data.ll > INT8_MAX? 2: 1; + + } else { + // Smallest size that fits unsigned representation + data.size = + data.ull > UINT32_MAX? 8: + data.ull > UINT16_MAX? 4: + data.ull > UINT8_MAX? 2: 1; + } + } else if(nl==0 && nh==2 && ns==0) { // HH + data.size = 1; + if(is_outside_int64_t || (is_signed && (data.ll < INT8_MIN || data.ll > INT8_MAX))) { + is_out_of_range = 1; + data.ll = (int8_t) data.ll; + } + } else if(nl==0 && ((nh==1 && ns==0) || (nh==0 && ns==1))) { // H or S + data.size = 2; + if(is_outside_int64_t || (is_signed && (data.ll < INT16_MIN || data.ll > INT16_MAX))) { + is_out_of_range = 1; + data.ll = (int16_t) data.ll; + } + } else if(nl==1 && nh==0 && ns==0) { // L + data.size = 4; + if(is_outside_int64_t || (is_signed && (data.ll < INT32_MIN || data.ll > INT32_MAX))) { + is_out_of_range = 1; + data.ll = (int32_t) data.ll; + } + } else if(nl==2 && nh==0 && ns==0) { // LL + data.size = 8; + } + + if(is_outside_int64_t || is_out_of_range) + terminal_message(MSG_INFO, "%s (write): %s out of int%d_t range, " + "interpreted as %d-byte %lld; consider 'U' suffix\n", + progname, argi, data.size*8, data.size, data.ll); } } } - // Print warning if data size might be ambiguous - bool is_hex = (strncmp(argv[i], "0x", 2) == 0); - bool is_neg_hex = (strncmp(argv[i], "-0x", 3) == 0); - bool leading_zero = (strncmp(argv[i], "0x0", 3) == 0); - int8_t hex_digits = (strlen(argv[i]) - 2); - if(!data.size // No pre-defined size - && (is_neg_hex // Hex with - sign in front - || (is_hex && leading_zero && (hex_digits & (hex_digits - 1))) // Hex with 3, 5, 6 or 7 digits - || (!is_hex && !data.is_float && llabs(data.ll) > 0xFF && strlen(argv[i]) > 2))) // Base10 int greater than 255 - { - avrdude_message(MSG_INFO, "Warning: no size suffix specified for \"%s\". " - "Writing %d byte(s)\n", - argv[i], - llabs(data.ll) > UINT32_MAX ? 8 : - llabs(data.ll) > UINT16_MAX || data.is_float ? 4 : \ - llabs(data.ll) > UINT8_MAX ? 2 : 1); - } - // Flag if signed integer and adjust size - if (data.ll < 0 && !data.is_float) { - data.is_signed = true; - if (data.ll < INT32_MIN) + if(!data.size) { // Try double now that input was rejected as integer + data.d = strtod(argi, &end_ptr); + if (end_ptr != argi && toupper(*end_ptr) == 'D' && end_ptr[1] == 0) data.size = 8; - else if (data.ll < INT16_MIN) + } + + if(!data.size) { // Try float + data.f = strtof(argi, &end_ptr); + if (end_ptr != argi && toupper(*end_ptr) == 'F' && end_ptr[1] == 0) data.size = 4; - else if (data.ll < INT8_MIN) - data.size = 2; - else - data.size = 1; + if (end_ptr != argi && *end_ptr == 0) // no suffix defaults to float but ... + // ... do not accept valid mantissa-only floats that are integer rejects (eg, 078 or ULL overflows) + if (!is_mantissa_only(argi)) + data.size = 4; } + + if(!data.size && arglen > 1) { // Try C-style string or single character + if ((*argi == '\'' && argi[arglen-1] == '\'') || (*argi == '\"' && argi[arglen-1] == '\"')) { + char *s = calloc(arglen-1, 1); + if (s == NULL) { + terminal_message(MSG_INFO, "%s (write str): out of memory\n", progname); + free(buf); + return -1; + } + // Strip start and end quotes, and unescape C string + strncpy(s, argi+1, arglen-2); + unescape((unsigned char *) s, (unsigned char *) s); + if (*argi == '\'') { // Single C-style character + if(*s && s[1]) + terminal_message(MSG_INFO, "%s (write): only using first character of %s\n", + progname, argi); + data.ll = *s; + data.size = 1; + free(s); + } else { // C-style string + data.str_ptr = s; + } + } + } + + if(!data.size && !data.str_ptr) { + terminal_message(MSG_INFO, "%s (write): can't parse data %s\n", + progname, argi); + free(buf); + return -1; + } + + // Assume endianness is the same for double and int, and ensure little endian representation + if(is_big_endian && data.size > 1) + change_endian(data.a, data.size); } + if(data.str_ptr) { - for(int16_t j = 0; j < strlen(data.str_ptr); j++) + for(size_t j = 0; j < strlen(data.str_ptr); j++) buf[i - start_offset + data.bytes_grown++] = (uint8_t)data.str_ptr[j]; - } else { - buf[i - start_offset + data.bytes_grown] = data.a[0]; - if (llabs(data.ll) > 0x000000FF || data.size >= 2 || data.is_float) - buf[i - start_offset + ++data.bytes_grown] = data.a[1]; - if (llabs(data.ll) > 0x0000FFFF || data.size >= 4 || data.is_float) { - buf[i - start_offset + ++data.bytes_grown] = data.a[2]; - buf[i - start_offset + ++data.bytes_grown] = data.a[3]; - } - if (llabs(data.ll) > 0xFFFFFFFF || data.size == 8) { - buf[i - start_offset + ++data.bytes_grown] = data.a[4]; - buf[i - start_offset + ++data.bytes_grown] = data.a[5]; - buf[i - start_offset + ++data.bytes_grown] = data.a[6]; - buf[i - start_offset + ++data.bytes_grown] = data.a[7]; - } + } else if(data.size > 0) { + for(int k=0; k maxsize) { - avrdude_message(MSG_INFO, "%s (write): selected address and # bytes exceed " - "range for %s memory\n", - progname, memtype); + terminal_message(MSG_INFO, "%s (write): selected address and # bytes exceed " + "range for %s memory\n", progname, memtype); free(buf); return -1; } @@ -557,31 +854,31 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p, if(data.str_ptr) free(data.str_ptr); - avrdude_message(MSG_NOTICE, "\nInfo: Writing %d bytes starting from address 0x%02x", - len + data.bytes_grown, addr); + terminal_message(MSG_NOTICE, "Info: writing %d bytes starting from address 0x%02lx", + len + data.bytes_grown, (long) addr); if (write_mode == WRITE_MODE_FILL) - avrdude_message(MSG_NOTICE, ". Remaining space filled with %s", argv[argc - 2]); - avrdude_message(MSG_NOTICE, "\n"); + terminal_message(MSG_NOTICE, "; remaining space filled with %s", argv[argc - 2]); + terminal_message(MSG_NOTICE, "\n"); pgm->err_led(pgm, OFF); bool werror = false; report_progress(0, 1, "Writing"); for (i = 0; i < (len + data.bytes_grown); i++) { - int32_t rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]); + int rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]); if (rc) { - avrdude_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx, rc=%d\n", - progname, buf[i], addr+i, rc); + terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx, rc=%d\n", + progname, buf[i], (long) addr+i, (int) rc); if (rc == -1) - avrdude_message(MSG_INFO, "write operation not supported on memory type \"%s\"\n", - mem->desc); + terminal_message(MSG_INFO, "%*swrite operation not supported on memory type %s\n", + (int) strlen(progname)+10, "", mem->desc); werror = true; } uint8_t b; rc = pgm->read_byte(pgm, p, mem, addr+i, &b); if (b != buf[i]) { - avrdude_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx cell=0x%02x\n", - progname, buf[i], addr+i, b); + terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx cell=0x%02x\n", + progname, buf[i], (long) addr+i, b); werror = true; } @@ -608,20 +905,20 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p, int len; if (pgm->cmd == NULL) { - avrdude_message(MSG_INFO, "The %s programmer does not support direct ISP commands.\n", - pgm->type); + terminal_message(MSG_INFO, "%s (send): the %s programmer does not support direct ISP commands\n", + progname, pgm->type); return -1; } if (spi_mode && (pgm->spi == NULL)) { - avrdude_message(MSG_INFO, "The %s programmer does not support direct SPI transfers.\n", - pgm->type); + terminal_message(MSG_INFO, "%s (send): the %s programmer does not support direct SPI transfers\n", + progname, pgm->type); return -1; } if ((argc > 5) || ((argc < 5) && (!spi_mode))) { - avrdude_message(MSG_INFO, spi_mode? + terminal_message(MSG_INFO, spi_mode? "Usage: send [ [ []]]\n": "Usage: send \n"); return -1; @@ -634,8 +931,8 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p, for (i=1; ichip_erase(pgm, p); return 0; } @@ -690,14 +987,14 @@ static int cmd_sig(PROGRAMMER * pgm, struct avrpart * p, rc = avr_signature(pgm, p); if (rc != 0) { - avrdude_message(MSG_INFO, "error reading signature data, rc=%d\n", - rc); + terminal_message(MSG_INFO, "%s (sig): error reading signature data, rc=%d\n", + progname, rc); } m = avr_locate_mem(p, "signature"); if (m == NULL) { - avrdude_message(MSG_INFO, "signature data not defined for device \"%s\"\n", - p->desc); + terminal_message(MSG_INFO, "%s (sig): signature data not defined for device %s\n", + progname, p->desc); } else { fprintf(stdout, "Device signature = 0x"); @@ -725,9 +1022,8 @@ static int cmd_parms(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[]) { if (pgm->print_parms == NULL) { - avrdude_message(MSG_INFO, "%s (parms): the %s programmer does not support " - "adjustable parameters\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (parms): the %s programmer does not support " + "adjustable parameters\n", progname, pgm->type); return -1; } pgm->print_parms(pgm); @@ -744,23 +1040,23 @@ static int cmd_vtarg(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 2) { - avrdude_message(MSG_INFO, "Usage: vtarg \n"); + terminal_message(MSG_INFO, "Usage: vtarg \n"); return -1; } v = strtod(argv[1], &endp); if (endp == argv[1]) { - avrdude_message(MSG_INFO, "%s (vtarg): can't parse voltage \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (vtarg): can't parse voltage %s\n", + progname, argv[1]); return -1; } if (pgm->set_vtarget == NULL) { - avrdude_message(MSG_INFO, "%s (vtarg): the %s programmer cannot set V[target]\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (vtarg): the %s programmer cannot set V[target]\n", + progname, pgm->type); return -2; } if ((rc = pgm->set_vtarget(pgm, v)) != 0) { - avrdude_message(MSG_INFO, "%s (vtarg): failed to set V[target] (rc = %d)\n", - progname, rc); + terminal_message(MSG_INFO, "%s (vtarg): failed to set V[target] (rc = %d)\n", + progname, rc); return -3; } return 0; @@ -775,7 +1071,7 @@ static int cmd_fosc(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 2) { - avrdude_message(MSG_INFO, "Usage: fosc [M|k] | off\n"); + terminal_message(MSG_INFO, "Usage: fosc [M|k] | off\n"); return -1; } v = strtod(argv[1], &endp); @@ -783,8 +1079,8 @@ static int cmd_fosc(PROGRAMMER * pgm, struct avrpart * p, if (strcmp(argv[1], "off") == 0) v = 0.0; else { - avrdude_message(MSG_INFO, "%s (fosc): can't parse frequency \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (fosc): can't parse frequency %s\n", + progname, argv[1]); return -1; } } @@ -793,13 +1089,13 @@ static int cmd_fosc(PROGRAMMER * pgm, struct avrpart * p, else if (*endp == 'k' || *endp == 'K') v *= 1e3; if (pgm->set_fosc == NULL) { - avrdude_message(MSG_INFO, "%s (fosc): the %s programmer cannot set oscillator frequency\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (fosc): the %s programmer cannot set oscillator frequency\n", + progname, pgm->type); return -2; } if ((rc = pgm->set_fosc(pgm, v)) != 0) { - avrdude_message(MSG_INFO, "%s (fosc): failed to set oscillator frequency (rc = %d)\n", - progname, rc); + terminal_message(MSG_INFO, "%s (fosc): failed to set oscillator frequency (rc = %d)\n", + progname, rc); return -3; } return 0; @@ -814,24 +1110,24 @@ static int cmd_sck(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 2) { - avrdude_message(MSG_INFO, "Usage: sck \n"); + terminal_message(MSG_INFO, "Usage: sck \n"); return -1; } v = strtod(argv[1], &endp); if (endp == argv[1]) { - avrdude_message(MSG_INFO, "%s (sck): can't parse period \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (sck): can't parse period %s\n", + progname, argv[1]); return -1; } v *= 1e-6; /* Convert from microseconds to seconds. */ if (pgm->set_sck_period == NULL) { - avrdude_message(MSG_INFO, "%s (sck): the %s programmer cannot set SCK period\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (sck): the %s programmer cannot set SCK period\n", + progname, pgm->type); return -2; } if ((rc = pgm->set_sck_period(pgm, v)) != 0) { - avrdude_message(MSG_INFO, "%s (sck): failed to set SCK period (rc = %d)\n", - progname, rc); + terminal_message(MSG_INFO, "%s (sck): failed to set SCK period (rc = %d)\n", + progname, rc); return -3; } return 0; @@ -847,39 +1143,39 @@ static int cmd_varef(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 2 && argc != 3) { - avrdude_message(MSG_INFO, "Usage: varef [channel] \n"); + terminal_message(MSG_INFO, "Usage: varef [channel] \n"); return -1; } if (argc == 2) { chan = 0; v = strtod(argv[1], &endp); if (endp == argv[1]) { - avrdude_message(MSG_INFO, "%s (varef): can't parse voltage \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (varef): can't parse voltage %s\n", + progname, argv[1]); return -1; } } else { chan = strtoul(argv[1], &endp, 10); if (endp == argv[1]) { - avrdude_message(MSG_INFO, "%s (varef): can't parse channel \"%s\"\n", - progname, argv[1]); + terminal_message(MSG_INFO, "%s (varef): can't parse channel %s\n", + progname, argv[1]); return -1; } v = strtod(argv[2], &endp); if (endp == argv[2]) { - avrdude_message(MSG_INFO, "%s (varef): can't parse voltage \"%s\"\n", - progname, argv[2]); + terminal_message(MSG_INFO, "%s (varef): can't parse voltage %s\n", + progname, argv[2]); return -1; } } if (pgm->set_varef == NULL) { - avrdude_message(MSG_INFO, "%s (varef): the %s programmer cannot set V[aref]\n", - progname, pgm->type); + terminal_message(MSG_INFO, "%s (varef): the %s programmer cannot set V[aref]\n", + progname, pgm->type); return -2; } if ((rc = pgm->set_varef(pgm, chan, v)) != 0) { - avrdude_message(MSG_INFO, "%s (varef): failed to set V[aref] (rc = %d)\n", - progname, rc); + terminal_message(MSG_INFO, "%s (varef): failed to set V[aref] (rc = %d)\n", + progname, rc); return -3; } return 0; @@ -891,13 +1187,13 @@ static int cmd_help(PROGRAMMER * pgm, struct avrpart * p, { int i; - fprintf(stdout, "Valid commands:\n\n"); + fprintf(stdout, "Valid commands:\n"); for (i=0; iinitialize(pgm, p); return 0; } - avrdude_message(MSG_INFO, "`pgm' command unavailable for this programmer type\n"); + terminal_message(MSG_INFO, "%s: pgm command unavailable for this programmer type\n", + progname); return -1; } @@ -936,26 +1234,62 @@ static int cmd_verbose(PROGRAMMER * pgm, struct avrpart * p, char *endp; if (argc != 1 && argc != 2) { - avrdude_message(MSG_INFO, "Usage: verbose []\n"); + terminal_message(MSG_INFO, "Usage: verbose []\n"); return -1; } if (argc == 1) { - avrdude_message(MSG_INFO, "Verbosity level: %d\n", verbose); + terminal_message(MSG_INFO, "Verbosity level: %d\n", verbose); return 0; } nverb = strtol(argv[1], &endp, 0); - if (endp == argv[2]) { - avrdude_message(MSG_INFO, "%s: can't parse verbosity level \"%s\"\n", - progname, argv[2]); + if (endp == argv[1] || *endp) { + terminal_message(MSG_INFO, "%s (verbose): can't parse verbosity level %s\n", + progname, argv[1]); return -1; } if (nverb < 0) { - avrdude_message(MSG_INFO, "%s: verbosity level must be positive: %d\n", - progname, nverb); + terminal_message(MSG_INFO, "%s: verbosity level must not be negative: %d\n", + progname, nverb); return -1; } verbose = nverb; - avrdude_message(MSG_INFO, "New verbosity level: %d\n", verbose); + terminal_message(MSG_INFO, "New verbosity level: %d\n", verbose); + + return 0; +} + +static int cmd_quell(PROGRAMMER * pgm, struct avrpart * p, + int argc, char * argv[]) +{ + int nquell; + char *endp; + + if (argc != 1 && argc != 2) { + terminal_message(MSG_INFO, "Usage: quell []\n"); + return -1; + } + if (argc == 1) { + terminal_message(MSG_INFO, "Quell level: %d\n", quell_progress); + return 0; + } + nquell = strtol(argv[1], &endp, 0); + if (endp == argv[1] || *endp) { + terminal_message(MSG_INFO, "%s (quell): can't parse quell level %s\n", + progname, argv[1]); + return -1; + } + if (nquell < 0) { + terminal_message(MSG_INFO, "%s: quell level must not be negative: %d\n", + progname, nquell); + return -1; + } + quell_progress = nquell; + terminal_message(MSG_INFO, "New quell level: %d\n", quell_progress); + + if(quell_progress > 0) + update_progress = NULL; + else + terminal_setup_update_progress(); return 0; } @@ -974,9 +1308,9 @@ static int tokenize(char * s, char *** argv) slen = strlen(s); - /* + /* * initialize allow for 20 arguments, use realloc to grow this if - * necessary + * necessary */ nargs = 20; bufsize = slen + 20; @@ -1032,7 +1366,7 @@ static int tokenize(char * s, char *** argv) } } - /* + /* * We have parsed all the args, n == argc, bufv contains an array of * pointers to each arg, and buf points to one memory block that * contains all the args, back to back, seperated by a nul @@ -1073,8 +1407,8 @@ static int do_cmd(PROGRAMMER * pgm, struct avrpart * p, } else if (strncasecmp(argv[0], cmd[i].name, len)==0) { if (hold != -1) { - avrdude_message(MSG_INFO, "%s: command \"%s\" is ambiguous\n", - progname, argv[0]); + terminal_message(MSG_INFO, "%s (cmd): command %s is ambiguous\n", + progname, argv[0]); return -1; } hold = i; @@ -1084,8 +1418,8 @@ static int do_cmd(PROGRAMMER * pgm, struct avrpart * p, if (hold != -1) return cmd[hold].func(pgm, p, argc, argv); - avrdude_message(MSG_INFO, "%s: invalid command \"%s\"\n", - progname, argv[0]); + terminal_message(MSG_INFO, "%s (cmd): invalid command %s\n", + progname, argv[0]); return -1; } @@ -1117,7 +1451,6 @@ char * terminal_get_input(const char *prompt) int terminal_mode(PROGRAMMER * pgm, struct avrpart * p) { char * cmdbuf; - int i; char * q; int rc; int argc; @@ -1125,11 +1458,11 @@ int terminal_mode(PROGRAMMER * pgm, struct avrpart * p) rc = 0; while ((cmdbuf = terminal_get_input("avrdude> ")) != NULL) { - /* + /* * find the start of the command, skipping any white space */ q = cmdbuf; - while (*q && isspace((int)*q)) + while (*q && isspace((unsigned char) *q)) q++; /* skip blank lines and comments */ @@ -1143,10 +1476,12 @@ int terminal_mode(PROGRAMMER * pgm, struct avrpart * p) return argc; } +#if !defined(HAVE_LIBREADLINE) || defined(WIN32) || defined(__APPLE__) fprintf(stdout, ">>> "); - for (i=0; i= msglvl) { + va_start(ap, format); + rc = vfprintf(stderr, format, ap); + va_end(ap); + } + fflush(stderr); + + return rc; +} + + +static void update_progress_tty (int percent, double etime, char *hdr) +{ + static char hashes[51]; + static char *header; + static int last = 0; + int i; + + setvbuf(stderr, (char*)NULL, _IONBF, 0); + + hashes[50] = 0; + + memset (hashes, ' ', 50); + for (i=0; i>1)*2; + + setvbuf(stderr, (char*)NULL, _IONBF, 0); + + if (hdr) { + avrdude_message(MSG_INFO, "\n%s | ", hdr); + last = 0; + done = 0; + } + else { + while ((cnt > last) && (done == 0)) { + avrdude_message(MSG_INFO, "#"); + cnt -= 2; + } + } + + if ((percent == 100) && (done == 0)) { + avrdude_message(MSG_INFO, " | 100%% %0.2fs\n\n", etime); + last = 0; + done = 1; + } + else + last = (percent>>1)*2; /* Make last a multiple of 2. */ + + setvbuf(stderr, (char*)NULL, _IOLBF, 0); +} + +void terminal_setup_update_progress() { + if (isatty (STDERR_FILENO)) + update_progress = update_progress_tty; + else { + update_progress = update_progress_no_tty; + /* disable all buffering of stderr for compatibility with + software that captures and redirects output to a GUI + i.e. Programmers Notepad */ + setvbuf( stderr, NULL, _IONBF, 0 ); + setvbuf( stdout, NULL, _IONBF, 0 ); + } +} diff --git a/src/term.h b/src/term.h index f114d4b0..a89927ea 100644 --- a/src/term.h +++ b/src/term.h @@ -34,6 +34,8 @@ typedef enum { int terminal_mode(PROGRAMMER * pgm, struct avrpart * p); char * terminal_get_input(const char *prompt); +void terminal_setup_update_progress(); +int terminal_message(const int msglvl, const char *format, ...); #ifdef __cplusplus }