diff options
| author | John Bowler <jbowler@nslu2-linux.org> | 2005-06-07 22:32:06 +0000 |
|---|---|---|
| committer | John Bowler <jbowler@nslu2-linux.org> | 2005-06-07 22:32:06 +0000 |
| commit | 689086642964939e81cabbfd3aa18e1777f6cf19 (patch) | |
| tree | d1b9f7cee8f00c7cb382039221176b50899ce4d1 /packages | |
| parent | 6df171ed659aa21947821f2a5b8c5af96b69e8d6 (diff) | |
Move devio to a separate package
BKrev: 42a62066p_4MA9b5mKFPNSB_JVH1YA
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/devio/devio-native_cvs.bb | 0 | ||||
| -rw-r--r-- | packages/devio/devio_cvs.bb | 0 | ||||
| -rw-r--r-- | packages/openslug-init/openslug-init-0.10/devio.c | 1767 | ||||
| -rw-r--r-- | packages/openslug-init/openslug-init_0.10.bb | 9 |
4 files changed, 4 insertions, 1772 deletions
diff --git a/packages/devio/devio-native_cvs.bb b/packages/devio/devio-native_cvs.bb new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/packages/devio/devio-native_cvs.bb diff --git a/packages/devio/devio_cvs.bb b/packages/devio/devio_cvs.bb new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/packages/devio/devio_cvs.bb diff --git a/packages/openslug-init/openslug-init-0.10/devio.c b/packages/openslug-init/openslug-init-0.10/devio.c deleted file mode 100644 index bfaace2da1..0000000000 --- a/packages/openslug-init/openslug-init-0.10/devio.c +++ /dev/null @@ -1,1767 +0,0 @@ -/* vi: set sw=4 ts=4: */ -/* - * devio: correctly read a region of a device - * - * A dd like program designed to read correctly from mtd character - * (and maybe block) devices. Allows access to specific regions - * of the device and allows output of numbers from specific locations. - * - * Copyright (C) 2005 John Bowler <jbowler@acm.org> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <limits.h> -#include <errno.h> - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <ctype.h> - -#ifndef S_ISSOCK -#define S_ISSOCK(fd) 0 -#endif - -/* Define to 0 to remove the detailed help. */ -#ifndef HELP -#define HELP 1 -#endif - -#ifndef STR_MAX -#define STR_MAX 4096 -#endif - -/* common error-and-die functions - reduces code size slightly. */ -static int error_level = 1; /* Increased by write operations. */ - -/* NDEBUG will only save about 600 bytes! */ -/* The noreturn attribute helps reduce size by 300 bytes, and removes - * warning messages - */ -#if NDEBUG -static void do_die(const unsigned char *why) __attribute__((noreturn)); -static void do_die(const unsigned char *why) { - fprintf(stderr, "devio: %s\n", why); - exit(error_level); -} -#define die(a,b) do_die(a) -#else -static void do_die(const unsigned char *why, const unsigned char *infile) - __attribute__((noreturn)); -static void do_die(const unsigned char *why, const unsigned char *infile) { - fprintf(stderr, "devio: %s: %s\n", infile, why); - exit(error_level); -} -#define die(a,b) do_die((a),(b)) -#endif - -#if NDEBUG -static void do_pdie(const unsigned char *why) __attribute__((noreturn)); -static void do_pdie(const unsigned char *why) { - fprintf(stderr, "devio: %s: %s\n", why, strerror(errno)); - exit(error_level); -} -#define pdie(a,b) do_pdie(a) -#else -static void do_pdie(const unsigned char *why, const unsigned char *infile) - __attribute__((noreturn)); -static void do_pdie(const unsigned char *why, const unsigned char *infile) { - fprintf(stderr, "devio: %s: %s: %s\n", infile, why, strerror(errno)); - exit(error_level); -} -#define pdie(a,b) do_pdie((a),(b)) -#endif - -/* This is a non-standard assert but it saves quite a lot of - * space (1kbyte) over the OS version. - */ -#if NDEBUG -#define assert(condition) do;while(0) -#elif 0 /* Expensive string asserts (lots of space in strings). */ -static void do_assert(const unsigned char *why) - __attribute__((noreturn)); -static void do_assert(const unsigned char *why) { - fprintf(stderr, "devio: internal error: %s\n", why); - exit(error_level); -} -#define assert(condition) do if (!(condition)) do_assert(#condition); while (0) -#else -static void do_assert(int line) __attribute__((noreturn)); -static void do_assert(int line) { - fprintf(stderr, "devio: internal error: %d\n", line); - exit(error_level); -} -#define assert(condition) do if (!(condition)) do_assert(__LINE__); while (0) -#endif - -/* This is a non-ANSI extension. */ -unsigned char *my_strdup(const unsigned char *from) { - size_t cb = strlen(from)+1; - unsigned char *to = malloc(cb); - if (to == 0) - die("out of memory", from); - memcpy(to, from, cb); - return to; -} - - -/*********************************************************************** - * mtd_file - * - * Basic device safe IO. - * Set mtd_seek to set the desired read or write point. - * Use mtd_getb and mtd_putb to read/write a single byte. - * Use mtd_readbytes and mtd_writebytes to move multiple bytes. - *********************************************************************/ -/* File structure, used for read and write operations. stdio() should do - * everything this does pretty much except that this allows for no-write - * buffering and it is 'weird' in that it won't overwrite beyond the end - * of the data. */ -typedef struct mtd_file { - unsigned char* pname; - int fwrite; - int fverify; /* do not write, just verify */ - int fwritten; /* something to do! */ - int fchanged; /* something was done! */ - int fd; - struct stat stat; - size_t cbbuf; - /* The user pointer is at 'useroffset', the buffer contains data from - * 'bufferoffset' to 'deviceoffset' (exclusive - the buffer may be empty), so - * the file descriptor is pointing to 'deviceoffset', which may be just beyond - * the end of the file. - */ - off_t useroffset; /* Current user position */ - off_t bufferoffset; /* Base of current buffer */ - off_t deviceoffset; /* End of current buffer */ - off_t endoffset; /* Length of char or block device. */ - unsigned char* pbuf; - unsigned char* pwritebuf; -} mtd_file; - - -/* Initialise an mtd structure. */ -static void init_mtd(mtd_file *pfile) { - pfile->pname = 0; - pfile->fwrite = 0; - pfile->fverify = 0; - pfile->fwritten = 0; - pfile->fchanged = 0; - pfile->fd = (-1); - memset(&pfile->stat, 0, sizeof pfile->stat); - pfile->cbbuf = 0; - pfile->useroffset = 0; - pfile->bufferoffset = 0; - pfile->deviceoffset = 0; - pfile->endoffset = (off_t)-1; - pfile->pbuf = 0; - pfile->pwritebuf = 0; -} - - -/* Return the size, in bytes. */ -static size_t size_mtd(mtd_file *pfile) { - if (S_ISCHR(pfile->stat.st_mode) || S_ISBLK(pfile->stat.st_mode)) { - if (pfile->endoffset == (off_t)-1) { - off_t len; - assert(pfile->stat.st_size == 0); - assert(pfile->stat.st_blocks == 0); - assert(pfile->stat.st_blksize > 0); - /* So seek to the end then come back here. */ - len = lseek(pfile->fd, -pfile->stat.st_blksize, SEEK_END); - if (len == (off_t)-1) - pdie("lseek(length)", pfile->pname); - if (lseek(pfile->fd, pfile->deviceoffset, SEEK_SET) != pfile->deviceoffset) - pdie("lseek(length reset)", pfile->pname); - len += pfile->stat.st_blksize; - pfile->endoffset = len; - } - return pfile->endoffset; - } else if (S_ISDIR(pfile->stat.st_mode) || S_ISFIFO(pfile->stat.st_mode) || - S_ISSOCK(pfile->stat.st_mode)) - die("cannot find size of this device", pfile->pname); - else - return pfile->stat.st_size; -} - - -/* Open the named file for read or write, the structure is initialised - * appropriately. The name is copied. */ -static void new_mtd(mtd_file *pfile, const char *pname, int fwrite, int fverify, int fd) { - pfile->pname = my_strdup(pname); - pfile->fwrite = fwrite; - pfile->fverify = fverify; - pfile->fwritten = 0; - pfile->fchanged = 0; - pfile->fd = fd; - - if (fstat(fd, &pfile->stat) != 0) - pdie("fstat", pname); - /* This can be made to work with fifos on read because it is possible - * to seek by reading so long as we only seek forward, but it really - * isn't worth spending time on this. - */ - if (S_ISDIR(pfile->stat.st_mode) || S_ISFIFO(pfile->stat.st_mode) || - S_ISSOCK(pfile->stat.st_mode)) - die("invalid device", pname); - /* Allow writing to a file for testing - i.e. S_ISREG is fine above. */ - pfile->cbbuf = pfile->stat.st_blksize; - if (pfile->cbbuf == 0) - pfile->cbbuf = 4096; - pfile->useroffset = 0; - pfile->bufferoffset = 0; - pfile->deviceoffset = 0; - pfile->pbuf = 0; - pfile->pwritebuf = 0; -} - -static void open_mtd(mtd_file *pfile, const char *pname, int fwrite, int fverify) { - int fd = open(pname, (fwrite && !fverify) ? O_RDWR : O_RDONLY); - if (fd < 0) - pdie("open", pname); - else if (fd < 3) - die("no standard streams", "-"); - new_mtd(pfile, pname, fwrite, fverify, fd); -} - - -/* Do the actual write. Any pending write buffers are checked and output - * to the device. Happens on close and can be called before. Does not - * do an fsync. The fwritten flag indicates that write_mtd needs to be - * called, the fchanegd flag indicates that something has been written and - * an fdatasync needs to happen before the close. - */ -static void write_mtd(mtd_file *pfile) { - if (pfile->fwritten) { - size_t count = pfile->deviceoffset - pfile->bufferoffset; - unsigned char *pbuf = pfile->pwritebuf; - - assert(pfile->pbuf != 0); - assert(pbuf != 0); - assert(pfile->deviceoffset > pfile->bufferoffset); - assert(pfile->deviceoffset <= pfile->bufferoffset + pfile->cbbuf); - /* If it changed write it. */ - if (memcmp(pfile->pbuf, pbuf, count) != 0) { - /* If verifying the verify just failed... */ - if (pfile->fverify) - die("verification failed", pfile->pname); - - /* So write the whole of this buffer back. Do not do a sync here - * because that would force a complete write of the flash erase - * block - not good. - */ - if (lseek(pfile->fd, pfile->bufferoffset, SEEK_SET) != pfile->bufferoffset) - pdie("lseek(write)", pfile->pname); - /* write, well, you have to keep doing it until it works, you - * also have to RTFM several times to get this write, so if - * this looks wrong please fix it. No, not that, that was - * deliberate. - */ - do { - ssize_t cb = write(pfile->fd, pbuf, count); - assert(cb != 0); - assert(cb <= count); - if (cb < 0) switch (errno) { - case EINTR: /* shall we try that again then? */ - /* This is the common case - this does happen, it is - * necessary to deal with it and it is sufficient to - * try again. - */ - break; - case EAGAIN: /* what, oh well, if at once you don't succeed. */ - /* We didn't say O_NONBLOCK above so this should never - * happen, however it has. The code will therefore go into - * a tight loop in the manner of a certain Scottish nobleman. - */ - break; - case EPIPE: /* you don't love me any more. */ - /* This is a little difficult, it means we were squirting - * data down a pipe, so somehow someone has managed to work - * out both how to create a named pipe and how much fun to - * have by passing it to this program on the command line, - * then they have worked out how to make the shell ignore - * SIGPIPE in a spawned program (possible with some shells) - * then they want to see the really dumb message that comes - * out as a result. We just say no. - */ - exit(1); - default: - pdie("write", pfile->pname); - } else { - count -= cb; - /* It is now necessary to fdatasync this file descriptor - * to ensure that this data really does get to its final - * destination. (Note that even this is probably not certain - * if the destination is a disk with a RAM buffer - which - * means *any* disk these days.) - */ - pfile->fchanged = 1; - /* Something has been written to flash, but not everything - * has (necessarily) been written yet, so if something goes - * wrong after this point we are in deep, deep, trouble. - */ - error_level = 3; - } - } while (count > 0); - - /* So now the device matches the write buffer and the device - * pointer is back where it was before. - */ - memcpy(pfile->pbuf, pfile->pwritebuf, pfile->deviceoffset-pfile->bufferoffset); - } - /* Nothing remains to write from this buffer (hence nothing at all - * for this device.) - */ - pfile->fwritten = 0; - } -} - - -/* Close the file, if anything was written out does an fsync. - */ -static void close_mtd(mtd_file *pfile) { - write_mtd(pfile); - assert(!pfile->fwritten); - if (pfile->pbuf != 0) { - free(pfile->pbuf); - pfile->pbuf = 0; - } - if (pfile->pwritebuf != 0) { - free(pfile->pwritebuf); - pfile->pwritebuf = 0; - } - if (pfile->fd >= 0) { - /* For a write file be very very careful. For read ignore errors: - * it is more important to successfully write than to whine about - * strange close errors from a file we don't care about. For a - * write file with nothing written we don't care either. - */ - if (pfile->fchanged) { - /* This is the all important bit. Doing the fdatasync is what - * flushes the data to the flash. If this isn't done there is - * no guarantee that close will detect a write error, 'cause the - * flash may not have completed the write before the close - * returns. - */ - if (fdatasync(pfile->fd) != 0) { - /* Trying an fdatasync on a pipe, etc, is silly, but we do - * it anyway. EROFS means we just tried to write to a - * read only file system, safe but still an error. - */ - if (errno != EINVAL) - pdie("sync", pfile->pname); - } - if (close(pfile->fd) != 0) - pdie("close", pfile->pname); - } else - (void)close(pfile->fd); - pfile->fd = (-1); - } - if (pfile->pname != 0) { - free(pfile->pname); - pfile->pname = 0; - } - init_mtd(pfile); -} - - -/* Obtain an input and, if necessary, an output buffer. */ -static void buffer_mtd(mtd_file *pfile) { - if (pfile->pbuf == 0) { - size_t blksize = pfile->cbbuf; - assert(blksize > 0); - assert(pfile->pwritebuf == 0); - - /* Get blksize bytes (note: things could be speeded up by aligning - * the buffer but this really doesn't matter, all the time goes in - * read/write of the flash!) - */ - pfile->pbuf = malloc(blksize); - if (pfile->fwrite) - pfile->pwritebuf = malloc(blksize); - if (pfile->pbuf == 0 || (pfile->fwrite && pfile->pwritebuf == 0)) - die("out of memory", pfile->pname); - } -} - - -/* Read some data including the current user position. This will also *write* data - * if something is waiting to be written. - * - * NOTE: in the original design I conceived of some scheme whereby all the writes - * would be buffered up for the end, but I can't see how this would actually help - * anything because even if data has to be read from the device to determine read - * locations it tends to happen before the relevant writes. In the access patterns - * I know (they are very simple - and that is important in itself) there is never - * a need to read from a write device. - */ -static void read_mtd(mtd_file *pfile) { - size_t cbread; - int ioffset; - - /* 'useroffset' is where we need to read from, 'deviceoffset' is where we are - * at (sic) and 'bufferoffset'..'deviceoffset' is what we have already. - */ - if (pfile->useroffset >= pfile->bufferoffset && - pfile->useroffset < pfile->deviceoffset) - return; - - if (pfile->useroffset < 0 || pfile->useroffset >= size_mtd(pfile)) - die("read outside file", pfile->pname); - - /* Make sure there is a buffer. */ - buffer_mtd(pfile); - - /* This is the maximum amount which can be read. */ - cbread = pfile->cbbuf; - if (pfile->useroffset >= pfile->bufferoffset && - pfile->useroffset < pfile->bufferoffset + cbread) { - /* Just fill the rest of the buffer. */ - ioffset = pfile->deviceoffset - pfile->bufferoffset; - - assert(pfile->deviceoffset < pfile->bufferoffset + cbread); - cbread -= ioffset; - } else { - off_t base; - - /* We to move the buffer therefore any pending write needs to be flushed. */ - write_mtd(pfile); - assert(!pfile->fwritten); - - /* Seek to the aligned buffer boundary if necessary. */ - base = (pfile->useroffset / cbread) * cbread; - if (base != pfile->deviceoffset) { - if (lseek(pfile->fd, base, SEEK_SET) != base) - pdie("lseek(read)", pfile->pname); - pfile->deviceoffset = base; - } - pfile->bufferoffset = base; - ioffset = 0; - } - - /* Reading is like writing, EINTR can stop it succeeding but is a - * continuable error. - */ - assert(pfile->bufferoffset <= pfile->useroffset); - assert(pfile->useroffset < pfile->bufferoffset + cbread); - assert(pfile->deviceoffset <= pfile->useroffset); - do { - ssize_t cb = read(pfile->fd, pfile->pbuf+ioffset, cbread); - if (cb < 0) switch (errno) { - case EINTR: /* simple restart */ - /* POSIX allows this to happen when something has been - * read. Reset the file pointer just in case. - */ - if (lseek(pfile->fd, pfile->deviceoffset, SEEK_SET) != pfile->deviceoffset) - pdie("lseek(read reset)", pfile->pname); - break; - case EAGAIN: /* O_NONBLOCK on the input? */ - break; - default: - pdie("read", pfile->pname); - } else if (cb == 0) { - die("unexpected end of file", pfile->pname); - } else { - /* Save a copy of the data so that it can be written out again - * by a write file. - */ - if (pfile->pwritebuf != 0) - memcpy(pfile->pwritebuf+ioffset, pfile->pbuf+ioffset, cb); - cbread -= cb; - ioffset += cb; - pfile->deviceoffset += cb; - } - } while (cbread > 0 && pfile->useroffset >= pfile->deviceoffset); - - assert(pfile->useroffset < pfile->deviceoffset); -} - - -/* Basic IO - these are the functions to use, not the internal read/write - * functions above. - */ -/* Set the current read/write pointer on this file. */ -#if 0 /*UNUSED*/ -static void mtd_seek(mtd_file *pfile, off_t offset) { - pfile->useroffset = offset; -} -#endif - - -/* Get a single byte (returned) and advance the read pointer by one. */ -static unsigned char mtd_getb(mtd_file *pfile) { - read_mtd(pfile); - return (pfile->fwrite ? pfile->pwritebuf : pfile->pbuf)[ - pfile->useroffset++ - pfile->bufferoffset]; -} - - -/* Store a single byte in a write file and advance the pointer by one. */ -static void mtd_putb(mtd_file *pfile, unsigned long b) { - if (!pfile->fwrite) - die("file is not writeable", pfile->pname); - read_mtd(pfile); - if (b != pfile->pwritebuf[pfile->useroffset-pfile->bufferoffset]) { - pfile->pwritebuf[pfile->useroffset-pfile->bufferoffset] = b; - pfile->fwritten = 1; - } - ++(pfile->useroffset); -} - - -/* Read a given number of bytes, which must exist in the file, and - * advance the pointer by that amount. - */ -static void mtd_readbytes(mtd_file *pfile, unsigned char *pbuf, size_t cb) { - if (pfile->useroffset+cb > size_mtd(pfile)) - die("read beyond end of file", pfile->pname); - - while (cb > 0) { - int cbavail; - - read_mtd(pfile); - cbavail = pfile->deviceoffset - pfile->useroffset; - assert(cbavail > 0 && cbavail <= pfile->cbbuf); - if (cbavail > cb) - cbavail = cb; - - assert(pfile->useroffset >= pfile->bufferoffset); - assert(pfile->useroffset < pfile->deviceoffset); - assert(pfile->deviceoffset <= pfile->bufferoffset + pfile->cbbuf); - - memcpy(pbuf, (pfile->fwrite ? pfile->pwritebuf : pfile->pbuf) + - (pfile->useroffset-pfile->bufferoffset), cbavail); - pfile->useroffset += cbavail; - pbuf += cbavail; - cb -= cbavail; - } -} - - -/* Write a given number of bytes and advance the pointer. As with readbytes - * the bytes must already exist in the file - mtd_file will never extend the - * file only change existing bytes. - */ -static void mtd_writebytes(mtd_file *pfile, const unsigned char *pbuf, size_t cb) { - if (!pfile->fwrite) - die("file is not writeable", pfile->pname); - if (pfile->useroffset+cb > size_mtd(pfile)) - die("write beyond end of file", pfile->pname); - while (cb > 0) { - int cbavail; - - /* This may look strange but it is correct - this code always reads - * before it writes to avoid unnecessary writes. - */ - read_mtd(pfile); - cbavail = pfile->deviceoffset - pfile->useroffset; - if (cbavail > cb) - cbavail = cb; - memcpy(pfile->pwritebuf + (pfile->useroffset-pfile->bufferoffset), pbuf, cbavail); - pfile->fwritten = 1; - pfile->useroffset += cbavail; - pbuf += cbavail; - cb -= cbavail; - } -} - - -#if 0 /* Commented out because I don't think this is worth while. */ -/* Copy bytes from the pointer in one file to the pointer in another - * file (avoids an intermediate buffer compared to readbytes/writebytes.) - */ -static void mtd_copy(mtd_file *pto, mtd_file *pfrom, size_t cb) { - int cbfrom, cbto; - - if (!pto->fwrite) - die("file is not writeable", pto->pname); - if (pto->useroffset+cb > size_mtd(pto)) - die("write beyond end of file", pto->pname); - if (pfrom->useroffset+cb > size_mtd(pfrom)) - die("read beyond end of file", pfrom->pname); - /* Copying from and to the same place has no effect. */ - if (pfrom == pto) - return; - - cbfrom = cbto = 0; - while (cb > 0) { - int cbavail; - - if (cbfrom <= 0) { - read_mtd(pfrom); - cbfrom = pfrom->deviceoffset - pfrom->useroffset; - compared to readbytes/writebytes} - if (cbto <= 0) { - read_mtd(pto); - cbto = pto->deviceoffset - pto->useroffset; - } - - /* Take the smallest byte count and copy it. */ - cbavail = cbfrom; - if (cbavail > cbto) - cbavail = cbto; - if (cbavail > cb) - cbavail = cb; - - memcpy(pto->pwritebuf + (pto->useroffset-pto->bufferoffset), - pfrom->pbuf + (pfrom->useroffset-pfrom->bufferoffset), - cbavail); - pto->fwritten = 1; - - pto->useroffset += cbavail; - cbto -= cbavail; - pfrom->useroffset += cbavail; - cbfrom -= cbavail; - - cb -= cbavail; - } -} -#endif - - -/*********************************************************************** - * parse - * - * Parse a command line option or a single line. See the help below - * for details... - ***********************************************************************/ -#define STACK_BASE 8 -#define STACK_SIZE 256 -#define NUM_FILES 16 -typedef struct parse_buf { - int fverify; /* Just verifying, do no write. */ - int cstack; - int fbreak; /* Break in an expression. */ - mtd_file* pfrom; - mtd_file* pto; - - /* The buffers. */ - unsigned long variables[256]; - unsigned long stack[STACK_SIZE]; - mtd_file files[NUM_FILES]; -} parse_buf; - - -/* Initialiser. */ -static void init_parse(parse_buf *pp, int fverify) { - int i; - memset(pp, 0, sizeof *pp); - pp->fverify = fverify; - pp->cstack = STACK_BASE; - pp->fbreak = 0; - pp->pfrom = 0; - pp->pto = 0; - for (i=0; i<NUM_FILES; ++i) - init_mtd(pp->files+i); -} - - -/* Terminator. */ -static void quit(parse_buf *pp, int exit_code) __attribute__((noreturn)); -static void quit(parse_buf *pp, int exit_code) { - int i; - /* Close all the files. */ - for (i=0; i<NUM_FILES; ++i) - if (pp->files[i].pname != 0) - close_mtd(pp->files+i); - - /* And make sure the output worked too. */ - if (fflush(stdout) == EOF || ferror(stdout) || fclose(stdout) == EOF) - pdie("output failed", "stdout"); - - exit(exit_code); -} - - -/* Input a single byte. */ -static unsigned char inb(parse_buf *pp) { - int b; - if (pp->pfrom == 0) { - b = getchar(); - if (b == EOF) - pdie("read error", "stdin"); - } else { - b = mtd_getb(pp->pfrom); - } - return b; -} - - -/* Output a single byte. */ -static void outb(parse_buf *pp, unsigned long b) { - if (pp->pto == 0) { - if (putchar(b) == EOF) - pdie("write error", "stdout"); - } else { - mtd_putb(pp->pto, b); - } -} - - -/* Output these bytes. */ -static void outputbytes(parse_buf *pp, const char *pbuf, size_t cb) { - if (pp->pto == 0) { - if (fwrite(pbuf, cb, 1, stdout) != 1) - pdie("write error", "stdout"); - } else - mtd_writebytes(pp->pto, pbuf, cb); -} - -/* Copy a stream of bytes. */ -static void copybytes(parse_buf *pp, size_t cb) { - while (cb > 0) { - size_t cbavail = cb; - unsigned char buf[1024]; - if (cbavail > sizeof buf) - cbavail = sizeof buf; - if (pp->pfrom == 0) { - if (fread(buf, cbavail, 1, stdin) != 1) - pdie("read error", "stdin"); - } else - mtd_readbytes(pp->pfrom, buf, cbavail); - - outputbytes(pp, buf, cbavail); - - cb -= cbavail; - } -} - - -/* Fill the output with a count of bytes of a given value. */ -static void fillbytes(parse_buf *pp, unsigned long val, size_t cb) { - unsigned char buf[1024]; - memset(buf, val, sizeof buf); - - while (cb > 0) { - size_t cbavail = cb; - if (cbavail > sizeof buf) - cbavail = sizeof buf; - - outputbytes(pp, buf, cbavail); - - cb -= cbavail; - } -} - - -/* Push a single numeric value onto the stack. */ -static void push(parse_buf *pp, unsigned long num, const unsigned char *str) { - if (pp->cstack >= STACK_SIZE) - die("stack overflow", str); - pp->stack[pp->cstack++] = num; -} - - -/* Pop one or move variables. */ -static void pop(parse_buf *pp, int num, const unsigned char *str) { - if (pp->cstack < STACK_BASE+num) - die("stack underflow", str); - pp->cstack -= num; -} - -/* Return (and pop) the top of stack. */ -static unsigned long top(parse_buf *pp, const unsigned char *str) { - if (pp->cstack <= STACK_BASE) - die("stack underflow", str); - return pp->stack[--(pp->cstack)]; -} - - -/* Store the result of an operator. */ -static void op(parse_buf *pp, int numpop, unsigned long num, - const unsigned char *str) { - pop(pp, numpop, str); - push(pp, num, str); -} - - -/* Parse a single expression, which may be empty. The conditional execution - * stuff is identical to that for a command except that (:?) are used instead - * of $($:$?$) - */ -static int parse_expression(parse_buf *pp, const unsigned char *line, int Ac, int AcEnd) { - int SP = 0, fnoexec = 0, test = 0; - int stack[16]; - - for (;Ac<AcEnd;++Ac) { - const unsigned char *lp = line+Ac; - unsigned char ch = *lp; - switch (ch) { - /* Control flow. These operators have to explicitly check - * the fnoexec state because they manipulate it. - */ - case '(': /* if */ - if (SP >= 16) - die("() stack overflow", lp); - stack[SP++] = Ac; - if (fnoexec) { - fnoexec += 3; - } else { - fnoexec = top(pp, lp) == 0; - } - break; - - case '[': /* test start */ - /* If fnoexec >= 3 the whole block is disabled. */ - if (fnoexec <= 2) { - /* Valid only inside an () block and there should only - * be one active at once. - */ - if (test != 0 || SP <= 0) - goto badnest; - /* Record the start of the test. */ - test = Ac+1; - /* If the previous block executed record this. */ - if (fnoexec == 0) - fnoexec = 2; - } - break; - - case ':': /* elif */ - /* fnoexec is 1 if nothing has executed in this block yet, - * and if the block itself is executing, it is 2 if something - * did execute, it is >2 for a non-executed block, including - * one with a break. - */ - if (fnoexec <= 2) { - if (test == 0 || SP <= 0) - goto badnest; - - assert(fnoexec > 0); - assert(!pp->fbreak); - /* 1: nothing has executed yet. - * 2: an if or elif has executed. - */ - if (fnoexec == 1) { - /* Parse the test. If this results in a break no - * condition is popped from the stack, otherwise - * the condition which the expression should push - * is popped. - */ - (void)parse_expression(pp, line, test, Ac); - if (pp->fbreak) { - fnoexec = 3; - pp->fbreak = 0; - } else - fnoexec = top(pp, lp) == 0; - } - - /* And the test has been consumed. */ - test = 0; - } - break; - - case ')': /* end+loop if */ - /* The stack must always be popped. */ - if (SP <= 0) - goto badnest; - --SP; - /* If fnoexec>2 then this is a nested disabled block or, in - * the case of 3, a break. In neither case is the expression - * evaluated and the test setting is for an enclosing block. - */ - if (fnoexec > 2) { - fnoexec -= 3; - } else { - /* In this case there must be a test. */ - if (test == 0) - goto badnest; - - /* Execution resumes regardless. */ - fnoexec = 0; - assert(!pp->fbreak); - - /* So make the loop test now - this may cause a branch back - * to the ( and that will push the stack again. Evaluate - * the test. - */ - (void)parse_expression(pp, line, test, Ac); - if (pp->fbreak) - pp->fbreak = 0; - else if (top(pp, lp) != 0) { - Ac = stack[SP]-1; /* Ac is incremented below */ - } - - /* And the test has been consumed. */ - test = 0; - } - break; - - badnest: - die("bad [: or [) nesting", lp); - break; - - case ';': - case '\n': - /* end of line terminates the loop, but Ac is stepped beyond the - * terminator. - */ - ++Ac; - goto end; - - case ' ': - case '\f': - case '\r': - case '\t': - case '\v': - case ',': /* Treat as a space */ - /* Skip other white space. */ - break; - - /* Everything else is glommed together because the fnoexec case - * can be simply handled by skipping character-by-character (because - * (:?); do not occur inside numbers!) - */ - default: - if (fnoexec) - break; - - if (isupper(ch)) - push(pp, pp->variables[ch], lp); - else if (isdigit(ch)) { - char *end = (char*)lp; - unsigned long num; - errno = 0; - num = strtoul(lp, &end, 0); - if (num == ULONG_MAX && (errno == EINVAL || errno == ERANGE)) - pdie("invalid number", lp); - push(pp, num, lp); - /* strotul returns a pointer to the first invalid character in - * end, so Ac becomes end-line-1, because it is incremented below. - */ - Ac = (const unsigned char*)end-line-1; - } else { - /* The operators are handled here. An unrecognised character is an - * error at this point. The left, right are always valid because - * the stack has 8 unused slots at the top... - */ - unsigned long left = pp->stack[pp->cstack - 2]; - unsigned long right = pp->stack[pp->cstack - 1]; - - switch (ch) { - case '?': /* break */ - /* break inside a condition is actually allowed, so this may - * happen with SP==0 while evaluating a condition. For the - * moment ? is also allowed outside a loop, it terminates the - * processing of the whole expression. - */ - if (top(pp, lp) != 0) { - /* break: skip to the ) and do not do the test on - * that either. - */ - fnoexec = 3; - } - break; - - #define DIOP(operator) op(pp, 2, left operator right, lp); break - #define MONOP(operator) op(pp, 1, operator right, lp); break - /* The C operators */ - case '*': DIOP(*); - case '+': DIOP(+); - case '-': DIOP(-); - case '/': DIOP(/); - case '%': DIOP(%); - case '<': DIOP(<); - case '>': DIOP(>); - case '|': DIOP(|); - case '&': DIOP(&); - case '^': DIOP(^); - case '~': MONOP(~); - case '!': MONOP(!); - case '=': /* equality */ - op(pp, 2, left == right, lp); - break; - case '{': /* shift left */ - op(pp, 2, left << right, lp); - break; - case '}': /* shift right */ - op(pp, 2, left >> right, lp); - break; - case 'r': /* rotate right */ - op(pp, 2, (left >> right) + (left << (32-right)), lp); - break; - case 'e': /* sign extend (right is number of valid bits). */ - op(pp, 2, (long)(left << (32-right)) >> (32-right), lp); - break; - case 'm': /* mask (right is number of valid bits). */ - op(pp, 2, (left << (32-right)) >> (32-right), lp); - break; - case '$': /* Size of input. */ - if (pp->pfrom == 0) - die("size of input unknown", lp); - push(pp, size_mtd(pp->pfrom), lp); - break; - case 'f': /* position of input ('from' pointer). */ - if (pp->pfrom == 0) - die("position of input unknown", lp); - push(pp, pp->pfrom->useroffset, lp); - break; - case '#': /* Size of output. */ - if (pp->pto == 0) - die("size of output unknown", lp); - push(pp, size_mtd(pp->pto), lp); - break; - case 't': /* position of output ('to' pointer). */ - if (pp->pto == 0) - die("position of output unknown", lp); - push(pp, pp->pto->useroffset, lp); - break; - case 'd': /* device number of the input device */ - if (pp->pfrom == 0) - die("input device number unknown", lp); - push(pp, pp->pfrom->stat.st_rdev == 0 ? pp->pfrom->stat.st_dev : - pp->pfrom->stat.st_rdev, lp); - break; - case '@': /* one byte read. */ - push(pp, inb(pp), lp); - break; - case 'b': /* big endian 4 byte read. */ - #define P( |
