Index: head/usr.sbin/spi/spi.c =================================================================== --- head/usr.sbin/spi/spi.c (revision 335528) +++ head/usr.sbin/spi/spi.c (revision 335529) @@ -1,958 +1,958 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 S.F.T. Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_DEVICE_NAME "/dev/spigen0.0" #define DEFAULT_BUFFER_SIZE 8192 #define DIR_READ 0 #define DIR_WRITE 1 #define DIR_READWRITE 2 #define DIR_NONE -1 struct spi_options { int mode; /* mode (0,1,2,3, -1 == use default) */ int speed; /* speed (in Hz, -1 == use default) */ int count; /* count (0 through 'n' bytes, negative for * stdin length) */ int binary; /* non-zero for binary output or zero for * ASCII output when ASCII != 0 */ int ASCII; /* zero for binary input and output. * non-zero for ASCII input, 'binary' * determines output */ int lsb; /* non-zero for LSB order (default order is * MSB) */ int verbose; /* non-zero for verbosity */ int ncmd; /* bytes to skip for incoming data */ uint8_t *pcmd; /* command data (NULL if none) */ }; static void usage(void); static int interpret_command_bytes(const char *parg, struct spi_options *popt); static void * prep_write_buffer(struct spi_options *popt); static int _read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb); static int _do_data_output(void *pr, struct spi_options *popt); static int get_info(int hdev, const char *dev_name); static int set_mode(int hdev, struct spi_options *popt); static int set_speed(int hdev, struct spi_options *popt); static int hexval(char c); static int perform_read(int hdev, struct spi_options *popt); static int perform_write(int hdev, struct spi_options *popt); static int perform_readwrite(int hdev, struct spi_options *popt); static void verbose_dump_buffer(void *pbuf, int icount, int lsb); /* * LSB array - reversebits[n] is the LSB value of n as an MSB. Use this array * to obtain a reversed bit pattern of the index value when bits must * be sent/received in an LSB order vs the default MSB */ static uint8_t reversebits[256] = { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff }; static void usage(void) { fputs(getprogname(), stderr); fputs(" - communicate on SPI bus with slave devices\n" "Usage:\n" " spi [-f device] [-d r|w|rw] [-m mode] [-s max-speed] [-c count]\n" " [-C \"command bytes\"] [-A] [-b] [-L] [-v]\n" " spi -i [-f device] [-v]\n" " spi -h\n" " where\n" " -f specifies the device (default is spigen0.0)\n" " -d specifies the operation (r, w, or rw; default is rw)\n" " -m specifies the mode (0, 1, 2, or 3)\n" " -s specifies the maximum speed (default is 0, device default)\n" " -c specifies the number of data bytes to transfer (default 0, i.e. none)\n" " A negative value uses the length of the input data\n" " -C specifies 'command bytes' to be sent, as 2 byte hexadecimal values\n" " (these should be quoted, separated by optional white space)\n" " -L specifies 'LSB' order on the SPI bus (default is MSB)\n" " -i query information about the device\n" " -A uses ASCII for input/output as 2-digit hex values\n" " -b Override output format as binary (only valid with '-A')\n" " -v verbose output\n" " -h prints this message\n" "\n" "NOTE: setting the mode and/or speed is 'sticky'. Subsequent transactions\n" " on that device will, by default, use the previously set values.\n" "\n", stderr); } int main(int argc, char *argv[], char *envp[] __unused) { struct spi_options opt; int err, ch, hdev, finfo, fdir; char *pstr; char dev_name[PATH_MAX * 2 + 5]; finfo = 0; fdir = DIR_NONE; hdev = -1; err = 0; dev_name[0] = 0; opt.mode = -1; opt.speed = -1; opt.count = 0; opt.ASCII = 0; opt.binary = 0; opt.lsb = 0; opt.verbose = 0; opt.ncmd = 0; opt.pcmd = NULL; while (!err && (ch = getopt(argc, argv, "f:d:m:s:c:C:AbLvih")) != -1) { switch (ch) { case 'd': if (optarg[0] == 'r') { if (optarg[1] == 'w' && optarg[2] == 0) { fdir = DIR_READWRITE; } else if (optarg[1] == 0) { fdir = DIR_READ; } } else if (optarg[0] == 'w' && optarg[1] == 0) { fdir = DIR_WRITE; } else { err = 1; } break; case 'f': if (!optarg[0]) { /* unlikely */ fputs("error - missing device name\n", stderr); err = 1; } else { if (optarg[0] == '/') strlcpy(dev_name, optarg, sizeof(dev_name)); else snprintf(dev_name, sizeof(dev_name), "/dev/%s", optarg); } break; case 'm': opt.mode = (int)strtol(optarg, &pstr, 10); if (!pstr || *pstr || opt.mode < 0 || opt.mode > 3) { fprintf(stderr, "Invalid mode specified: %s\n", optarg); err = 1; } break; case 's': opt.speed = (int)strtol(optarg, &pstr, 10); if (!pstr || *pstr || opt.speed < 0) { fprintf(stderr, "Invalid speed specified: %s\n", optarg); err = 1; } break; case 'c': opt.count = (int)strtol(optarg, &pstr, 10); if (!pstr || *pstr) { fprintf(stderr, "Invalid count specified: %s\n", optarg); err = 1; } break; case 'C': if(opt.pcmd) /* specified more than once */ err = 1; else { /* get malloc'd buffer or error */ if (interpret_command_bytes(optarg, &opt)) err = 1; } break; case 'A': opt.ASCII = 1; break; case 'b': opt.binary = 1; break; case 'L': opt.lsb = 1; break; case 'v': opt.verbose++; break; case 'i': finfo = 1; break; default: err = 1; /* FALLTHROUGH */ case 'h': usage(); goto the_end; } } argc -= optind; argv += optind; if (err || (fdir == DIR_NONE && !finfo && opt.mode == -1 && opt.speed == -1 && opt.count == 0)) { /* * if any of the direction, mode, speed, or count not specified, * print usage */ usage(); goto the_end; } if ((opt.count != 0 || opt.ncmd != 0) && fdir == DIR_NONE) { /* * count was specified, but direction was not. default is * read/write */ /* * this includes a negative count, which implies write from * stdin */ if (opt.count == 0) fdir = DIR_WRITE; else fdir = DIR_READWRITE; } if (opt.count < 0 && fdir != DIR_READWRITE && fdir != DIR_WRITE) { fprintf(stderr, "Invalid length %d when not writing data\n", opt.count); err = 1; usage(); goto the_end; } if (!dev_name[0]) /* no device name specified */ strlcpy(dev_name, DEFAULT_DEVICE_NAME, sizeof(dev_name)); hdev = open(dev_name, O_RDWR); if (hdev == -1) { fprintf(stderr, "Error - unable to open '%s', errno=%d\n", dev_name, errno); err = 1; goto the_end; } if (finfo) { err = get_info(hdev, dev_name); goto the_end; } /* check and assign mode, speed */ if (opt.mode != -1) { err = set_mode(hdev, &opt); if (err) goto the_end; } if (opt.speed != -1) { err = set_speed(hdev, &opt); if (err) goto the_end; } /* do data transfer */ if (fdir == DIR_READ) { err = perform_read(hdev, &opt); } else if (fdir == DIR_WRITE) { err = perform_write(hdev, &opt); } else if (fdir == DIR_READWRITE) { err = perform_readwrite(hdev, &opt); } the_end: if (hdev != -1) close(hdev); free(opt.pcmd); return (err); } static int interpret_command_bytes(const char *parg, struct spi_options *popt) { int ch, ch2, ctr, cbcmd, err; const char *ppos; void *ptemp; uint8_t *pcur; err = 0; cbcmd = DEFAULT_BUFFER_SIZE; /* initial cmd buffer size */ popt->pcmd = (uint8_t *)malloc(cbcmd); if (!popt->pcmd) return 1; pcur = popt->pcmd; ctr = 0; ppos = parg; while (*ppos) { while (*ppos && *ppos <= ' ') { ppos++; /* skip (optional) leading white space */ } if (!*ppos) break; /* I am done */ ch = hexval(*(ppos++)); if (ch < 0 || !*ppos) { /* must be valid pair of hex characters */ err = 1; goto the_end; } ch2 = hexval(*(ppos++)); if (ch2 < 0) { err = 1; goto the_end; } ch = (ch * 16 + ch2) & 0xff; /* convert to byte */ if (ctr >= cbcmd) { /* need re-alloc buffer? (unlikely) */ cbcmd += 8192; /* increase by additional 8k */ ptemp = realloc(popt->pcmd, cbcmd); if (!ptemp) { err = 1; fprintf(stderr, "Not enough memory to interpret command bytes, errno=%d\n", errno); goto the_end; } popt->pcmd = (uint8_t *)ptemp; pcur = popt->pcmd + ctr; } if (popt->lsb) *pcur = reversebits[ch]; else *pcur = (uint8_t)ch; pcur++; ctr++; } popt->ncmd = ctr; /* record num bytes in '-C' argument */ the_end: /* at this point popt->pcmd is NULL or a valid pointer */ return err; } static int get_info(int hdev, const char *dev_name) { uint32_t fmode, fspeed; int err; char temp_buf[PATH_MAX], cpath[PATH_MAX]; if (!realpath(dev_name, cpath)) /* get canonical name for info purposes */ strlcpy(cpath, temp_buf, sizeof(cpath)); /* this shouldn't happen */ err = ioctl(hdev, SPIGENIOC_GET_SPI_MODE, &fmode); if (err == 0) err = ioctl(hdev, SPIGENIOC_GET_CLOCK_SPEED, &fspeed); if (err == 0) { fprintf(stderr, "Device name: %s\n" "Device mode: %d\n" "Device speed: %d\n", cpath, fmode, fspeed);//, max_cmd, max_data, temp_buf); } else fprintf(stderr, "Unable to query info (err=%d), errno=%d\n", err, errno); return err; } static int set_mode(int hdev, struct spi_options *popt) { uint32_t fmode = popt->mode; if (popt->mode < 0) /* use default? */ return 0; return ioctl(hdev, SPIGENIOC_SET_SPI_MODE, &fmode); } static int set_speed(int hdev, struct spi_options *popt) { uint32_t clock_speed = popt->speed; if (popt->speed < 0) return 0; return ioctl(hdev, SPIGENIOC_SET_CLOCK_SPEED, &clock_speed); } static int hexval(char c) { if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } else if (c >= 'a' && c <= 'f') { return c - 'a' + 10; } return -1; } static void * prep_write_buffer(struct spi_options *popt) { int ch, ch2, ch3, ncmd, lsb, err; uint8_t *pdata, *pdat2; size_t cbdata, cbread; const char *szbytes; ncmd = popt->ncmd; /* num command bytes (can be zero) */ if (ncmd == 0 && popt->count == 0) return NULL; /* always since it's an error if it happens * now */ if (popt->count < 0) { cbdata = DEFAULT_BUFFER_SIZE; } else { cbdata = popt->count; } lsb = popt->lsb; /* non-zero if LSB order; else MSB */ pdata = malloc(cbdata + ncmd + 1); cbread = 0; err = 0; if (!pdata) return NULL; if (popt->pcmd && ncmd > 0) { memcpy(pdata, popt->pcmd, ncmd); /* copy command bytes */ pdat2 = pdata + ncmd; } else pdat2 = pdata; /* no prepended command data */ /* * read up to 'cbdata' bytes. If I get an EOF, do one of two things: * a) change the data count to match how many bytes I read in b) fill * the rest of the input buffer with zeros * * If the specified length is negative, I do 'a', else 'b' */ while (!err && cbread < cbdata && (ch = fgetc(stdin)) != EOF) { if (popt->ASCII) { /* skip consecutive white space */ while (ch <= ' ') { if ((ch = fgetc(stdin)) == EOF) break; } if (ch != EOF) { ch2 = hexval(ch); if (ch2 < 0) { invalid_character: fprintf(stderr, "Invalid input character '%c'\n", ch); err = 1; break; } ch = fgetc(stdin); if (ch != EOF) { ch3 = hexval(ch); if (ch3 < 0) goto invalid_character; ch = ch2 * 16 + ch3; } } if (err || ch == EOF) break; } /* for LSB, flip the bits - otherwise, just copy the value */ if (lsb) pdat2[cbread] = reversebits[ch]; else pdat2[cbread] = (uint8_t) ch; cbread++; /* increment num bytes read so far */ } /* if it was an error, not an EOF, that ended the I/O, return NULL */ if (err || ferror(stdin)) { free(pdata); return NULL; } if (popt->verbose > 0) { const char *sz_bytes; if (cbread != 1) sz_bytes = "bytes"; /* correct plurality of 'byte|bytes' */ else sz_bytes = "byte"; if (popt->ASCII) fprintf(stderr, "ASCII input of %zd %s\n", cbread, sz_bytes); else fprintf(stderr, "Binary input of %zd %s\n", cbread, sz_bytes); } /* * if opt.count is negative, copy actual byte count to opt.count which does * not include any of the 'command' bytes that are being sent. Can be zero. */ if (popt->count < 0) { popt->count = cbread; } /* * for everything else, fill the rest of the read buffer with '0' * bytes, as per the standard practice for SPI */ else { while (cbread < cbdata) pdat2[cbread++] = 0; } /* * popt->count bytes will be sent and read from the SPI, preceded by the * 'popt->ncmd' command bytes (if any). * So we must use 'popt->count' and 'popt->ncmd' from this point on in * the code. */ if (popt->verbose > 0 && popt->count + popt->ncmd) { if ((popt->count + popt->ncmd) == 1) szbytes = "byte"; else szbytes = "bytes"; fprintf(stderr, "Writing %d %s to SPI device\n", popt->count + popt->ncmd, szbytes); verbose_dump_buffer(pdata, popt->count + popt->ncmd, lsb); } return pdata; } static int _read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb) { int err, ctr; struct spigen_transfer spi; if (!cbrw) return 0; if (!bufr) bufr = bufw; else memcpy(bufr, bufw, cbrw); /* transaction uses bufr for * both R and W */ bzero(&spi, sizeof(spi)); /* zero structure first */ /* spigen code seems to suggest there must be at least 1 command byte */ spi.st_command.iov_base = bufr; spi.st_command.iov_len = cbrw; /* * The remaining members for spi.st_data are zero - all bytes are * 'command' for this. The driver doesn't really do anything different * for 'command' vs 'data' and at least one command byte must be sent in * the transaction. */ err = ioctl(hdev, SPIGENIOC_TRANSFER, &spi) < 0 ? -1 : 0; if (!err && lsb) { /* flip the bits for 'lsb' mode */ for (ctr = 0; ctr < cbrw; ctr++) { ((uint8_t *) bufr)[ctr] = reversebits[((uint8_t *)bufr)[ctr]]; } } if (err) fprintf(stderr, "Error performing SPI transaction, errno=%d\n", errno); return err; } static int _do_data_output(void *pr, struct spi_options *popt) { - int err, index, icount; + int err, idx, icount; const char *sz_bytes, *sz_byte2; const uint8_t *pbuf; pbuf = (uint8_t *)pr + popt->ncmd; /* only the data we want */ icount = popt->count; err = 0; if (icount <= 0) { return -1; /* should not but could happen */ } if (icount != 1) sz_bytes = "bytes"; /* correct plurality of 'byte|bytes' */ else sz_bytes = "byte"; if (popt->ncmd != 1) sz_byte2 = "bytes"; else sz_byte2 = "byte"; /* binary on stdout */ if (popt->binary || !popt->ASCII) { if (popt->verbose > 0) fprintf(stderr, "Binary output of %d %s\n", icount, sz_bytes); err = (int)fwrite(pbuf, 1, icount, stdout) != icount; } else if (icount > 0) { if (popt->verbose > 0) fprintf(stderr, "ASCII output of %d %s\n", icount, sz_bytes); /* ASCII output */ - for (index = 0; !err && index < icount; index++) { - if (index) { + for (idx = 0; !err && idx < icount; idx++) { + if (idx) { /* * not the first time, insert separating space */ err = fputc(' ', stdout) == EOF; } if (!err) - err = fprintf(stdout, "%02hhx", pbuf[index]) < 0; + err = fprintf(stdout, "%02hhx", pbuf[idx]) < 0; } if (!err) err = fputc('\n', stdout) == EOF; } /* verbose text out on stderr */ if (err) fprintf(stderr, "Error writing to stdout, errno=%d\n", errno); else if (popt->verbose > 0 && icount) { fprintf(stderr, "%d command %s and %d data %s read from SPI device\n", popt->ncmd, sz_byte2, icount, sz_bytes); /* verbose output will show the command bytes as well */ verbose_dump_buffer(pr, icount + popt->ncmd, popt->lsb); } return err; } static int perform_read(int hdev, struct spi_options *popt) { int icount, err; void *pr, *pw; pr = NULL; icount = popt->count + popt->ncmd; /* prep write buffer filled with 0 bytes */ pw = malloc(icount); if (!pw) { err = -1; goto the_end; } bzero(pw, icount); /* if I included a command sequence, copy bytes to the write buf */ if (popt->pcmd && popt->ncmd > 0) memcpy(pw, popt->pcmd, popt->ncmd); pr = malloc(icount + 1); if (!pr) { err = -2; goto the_end; } bzero(pr, icount); err = _read_write(hdev, pw, pr, icount, popt->lsb); if (!err && popt->count > 0) err = _do_data_output(pr, popt); the_end: free(pr); free(pw); return err; } static int perform_write(int hdev, struct spi_options *popt) { int err; void *pw; /* read data from cmd buf and stdin and write to 'write' buffer */ pw = prep_write_buffer(popt); if (!pw) { err = -1; goto the_end; } err = _read_write(hdev, pw, NULL, popt->count + popt->ncmd, popt->lsb); the_end: free(pw); return err; } static int perform_readwrite(int hdev, struct spi_options *popt) { int icount, err; void *pr, *pw; pr = NULL; pw = prep_write_buffer(popt); icount = popt->count + popt->ncmd; /* assign after fn call */ if (!pw) { err = -1; goto the_end; } pr = malloc(icount + 1); if (!pr) { err = -2; goto the_end; } bzero(pr, icount); err = _read_write(hdev, pw, pr, icount, popt->lsb); if (!err) err = _do_data_output(pr, popt); the_end: free(pr); free(pw); return err; } static void verbose_dump_buffer(void *pbuf, int icount, int lsb) { uint8_t ch; - int ictr, ictr2, index; + int ictr, ictr2, idx; fputs(" | 0 1 2 3 4 5 6 7 8 9 A B C D E F " "| |\n", stderr); for (ictr = 0; ictr < icount; ictr += 16) { fprintf(stderr, " %6x | ", ictr & 0xfffff0); for (ictr2 = 0; ictr2 < 16; ictr2++) { - index = ictr + ictr2; + idx = ictr + ictr2; - if (index < icount) { - ch = ((uint8_t *) pbuf)[index]; + if (idx < icount) { + ch = ((uint8_t *) pbuf)[idx]; if (lsb) ch = reversebits[ch]; fprintf(stderr, "%02hhx ", ch); } else { fputs(" ", stderr); } } fputs("| ", stderr); for (ictr2 = 0; ictr2 < 16; ictr2++) { - index = ictr + ictr2; + idx = ictr + ictr2; - if (index < icount) { - ch = ((uint8_t *) pbuf)[index]; + if (idx < icount) { + ch = ((uint8_t *) pbuf)[idx]; if (lsb) ch = reversebits[ch]; if (ch < ' ' || ch > 127) goto out_of_range; fprintf(stderr, "%c", ch); } - else if (index < icount) { + else if (idx < icount) { out_of_range: fputc('.', stderr); } else { fputc(' ', stderr); } } fputs(" |\n", stderr); } fflush(stderr); }