Author: ablack
Date: Mon Mar 31 13:03:58 2008
New Revision: 643120
URL: http://svn.apache.org/viewvc?rev=643120&view=rev
Log:
2008-03-31 Andrew Black <[EMAIL PROTECTED]>
STDCXX-426
* util/output.cpp (struct readback): Add convenience data structure
for backwards file reading.
(rbinit): Add method to initialize structure.
(rbbof): Add method to check if structure points to the beginning
of a file.
(rbsync): Add method to synchronize the file handle the structure
is built on to the structure.
(rbgetc): Add method to retrieve a character from the file and
move the structure one position closer to the start
(rbscanf): Add methods to search for a string using the
structure, possibly capturing an unsigned integer in the process.
(check_test, check_compat_test): Alter to use above methods.
Modified:
stdcxx/trunk/util/output.cpp
Modified: stdcxx/trunk/util/output.cpp
URL:
http://svn.apache.org/viewvc/stdcxx/trunk/util/output.cpp?rev=643120&r1=643119&r2=643120&view=diff
==============================================================================
--- stdcxx/trunk/util/output.cpp (original)
+++ stdcxx/trunk/util/output.cpp Mon Mar 31 13:03:58 2008
@@ -44,6 +44,239 @@
# define ENOENT 2
#endif /* ENOENT */
+/**
+ Arbitrary constant controling static read buffer size.
+
+ @see check_example ()
+ @see rbread ()
+*/
+#define DELTA_BUF_LEN 64
+
+/**
+ This structure is used to encapsulate the data involved in a backwards
+ file read.
+*/
+struct readback {
+ FILE* src;
+ char buf[DELTA_BUF_LEN];
+ long bpos;
+};
+
+/**
+ Initializes the provided readback structure, with the provided file
+ handle.
+
+ @param rb pointer to readback structure to initialize
+ @param src file handle to initialize rb with.
+ @returns true if successfully initialized, false otherwise.
+*/
+static bool
+rbinit (struct readback* rb, FILE* const src)
+{
+ const size_t buflen = sizeof rb->buf;
+ long fpos;
+ assert (0 != rb);
+ assert (0 != src);
+
+ rb->src = src;
+ if (-1 == fseek (rb->src, 0, SEEK_END))
+ return false;
+ fpos = ftell (rb->src);
+ if (-1 == fpos)
+ return false;
+
+ if (fpos <= buflen)
+ rb->bpos = fpos;
+ else
+ rb->bpos = buflen;
+
+ if (-1 == fseek (rb->src, fpos - rb->bpos, SEEK_SET))
+ return false;
+
+ return rb->bpos == fread (rb->buf, 1, rb->bpos, rb->src);
+}
+
+/**
+ This method is semi-analagous to feof.
+
+ @param rb pointer to readback structure to check begin-of-file state for
+ @returns true if structure points to the begining of the file, false
+ otherwise
+*/
+static bool
+rbbof (const struct readback* const rb)
+{
+ assert (0 != rb);
+ assert (0 != rb->src);
+ return (0 == rb->bpos) && (0 == ftell (rb->src));
+}
+
+/**
+ Syncronizes the file handle underlying a readback structure to the
+ pointer in the structure. This method is called if you wish to start
+ reading forward from the current point in the readback structure.
+
+ @param rb pointer to the readback structure to syncronize.
+ @return true if the structure was successfully syncronized, false
+ otherwise.
+*/
+static bool
+rbsync (struct readback* const rb)
+{
+ assert (0 != rb);
+ assert (0 != rb->src);
+ const size_t buflen = sizeof rb->buf;
+ long fpos = ftell (rb->src) - buflen;
+ if (0 > fpos)
+ fpos=0;
+ fpos += rb->bpos;
+ rb->bpos=0;
+
+ return -1 != fseek (rb->src, fpos, SEEK_SET);
+}
+
+/**
+ Updates the provided readback structure, returning the last unread
+ character in the file. This method is semi-analagous to fgetc.
+
+ @param rb pointer to readback structure to read from.
+ @return EOF if beginning of file or I/O error, read character
+ otherwise.
+*/
+static char
+rbgetc (struct readback* rb)
+{
+ const size_t buflen = sizeof rb->buf;
+ assert (0 != rb);
+ assert (0 != rb->src);
+ if (!rb->bpos) {
+ const size_t bufdelta = buflen << 1;
+ long fpos = ftell (rb->src);
+ long seek;
+
+ if (-1 == fpos)
+ return EOF;
+
+ if (bufdelta <= fpos) {
+ seek = fpos - bufdelta;
+ rb->bpos = buflen;
+ }
+ else if (buflen < fpos) {
+ seek = 0;
+ rb->bpos = fpos - buflen;
+ }
+ else {
+ fseek (rb->src, 0, SEEK_SET);
+ return EOF;
+ }
+
+ if (-1 == fseek (rb->src, seek, SEEK_SET))
+ return EOF;
+
+ if (rb->bpos != fread (rb->buf, 1, rb->bpos, rb->src))
+ return EOF;
+ }
+ assert (0 < rb->bpos && rb->bpos <= buflen);
+
+ return rb->buf [--rb->bpos];
+}
+
+/**
+ This method is semi-analagous to fscanf, with some key differences.
+ First, it opperates on a readback structure. Second, it scans until
+ either the search pattern is matched, or until the begining of the file
+ is reached. Third, it reads backwards from the end of the file.
+
+ Limitations: string to match can't end in a repeated set of characters
+ (ie: 'singing' has the repeated characters 'ing')
+
+ @param rb pointer to readback structure to operate on
+ @param match string to search for
+ @return true if match is found, false if begining of file is reached
+*/
+static bool
+rbscanf (struct readback* rb, const char* const match)
+{
+ char tok;
+ size_t matched;
+ size_t count;
+ assert (0 != rb);
+ assert (0 != match);
+
+ matched = count = strlen (match) - 1;
+
+ for(tok = rbgetc(rb); matched && !rbbof (rb); tok = rbgetc (rb)) {
+ if (tok == match [matched])
+ --matched;
+ else
+ matched = count;
+ }
+ return !matched;
+}
+
+/**
+ This method is semi-analagous to fscanf, reading backwards on a readback
+ structure, starting from the end of the file.
+
+ This method searches backwards for the first (last) number and captures
+ it. It then checks if the match string exists directly after (before)
+ the captured value. If this is the case, a match is considered to have
+ been found, and the captured value is copied to val. Otherwise, the
+ search process is restarted. If the search hits the beginning of the
+ file, matching is considered to have failed, and val is unaltered.
+
+ Limitations: string to match can't end in a repeated set of characters
+ (ie: 'singing' has the repeated characters 'ing'.) or a repeated set
+ of characters, bordering on either side of one or more numbers (ie:
+ 'xyzzy9zzy' has the repeated characters 'zzy')
+
+ @param rb pointer to readback structure to operate on.
+ @param match string to search for.
+ @param val reference to variable to store captured value in.
+ @return true if match is found, false if begining of file is reached.
+*/
+static bool
+rbscanf (struct readback* rb, const char* const match, unsigned& val)
+{
+ size_t count;
+ unsigned tval =0;
+ unsigned radix;
+ assert (0 != rb);
+ assert (0 != match);
+
+ count = strlen (match) - 1;
+
+ while (!rbbof(rb)) {
+ char tok;
+ size_t matched = count;
+
+ tval=0;
+ radix=1;
+
+ /* Search for a numeric digit */
+ for (tok = rbgetc (rb);
+ (tok < '0' || tok > '9') && !rbbof (rb);
+ tok = rbgetc (rb)) /* Do nothing */;
+
+ /* Read in the number. */
+ for ( ; tok >= '0' && tok <= '9' && !rbbof (rb);
+ tok = rbgetc (rb)) {
+ tval += (tok-'0') * radix;
+ radix *= 10;
+ }
+
+ /* Make certain the content prior to the number in the file
+ matches the search pattern. */
+ for( ; matched && tok == match [matched] && !rbbof (rb);
+ tok = rbgetc (rb), --matched) /* Do nothing */;
+
+ if (tok == match [matched]) {
+ val = tval;
+ return true;
+ }
+ }
+ return false;
+}
/**
Parses contents of the open file handle data for test target_name.
@@ -58,6 +291,7 @@
static void
check_test (FILE* data, struct target_status* status)
{
+ struct readback buf;
unsigned r_lvl = 0; /* diagnostic severity level */
unsigned r_active = 0; /* number of active diagnostics */
unsigned r_total = 0; /* total number of diagnostics */
@@ -73,15 +307,28 @@
assert (0 != data);
assert (0 != status);
- tok = fgetc (data);
+ if (!rbinit (&buf, data)) {
+ status->status = ST_SYSTEM_ERROR;
+ return;
+ }
- if (feof (data)) {
+ if (rbbof (&buf)) {
/* target produced no output (regression test?) */
status->status = ST_NO_OUTPUT;
return;
}
- for ( ; fsm < 6 && !feof (data); tok = fgetc (data)) {
+ if (!rbscanf (&buf, "| INACTIVE |\n# +")) {
+ status->status = ST_FORMAT;
+ return;
+ }
+
+ rbsync(&buf);
+
+ /* While it'd probably be (slightly) faster to seek to a fixed position
+ after the file pointer has been synced, this should be more reliable
+ */
+ for (tok = fgetc (data); fsm < 6 && !feof (data); tok = fgetc (data)) {
switch (tok) {
case '\n':
fsm = 1;
@@ -150,58 +397,25 @@
static void
check_compat_test (FILE* data, struct target_status* status)
{
- int read = 0;
- unsigned fsm = 0;
- char tok;
+ struct readback buf;
assert (0 != data);
assert (0 != status);
- tok = fgetc (data);
-
- if (feof (data)) {
+ if (!rbinit (&buf, data)) {
+ status->status = ST_SYSTEM_ERROR;
+ }
+ else if (rbbof (&buf)) {
/* target produced no output (regression test?) */
status->status = ST_NO_OUTPUT;
- return;
- }
-
- for ( ; !feof (data); tok = fgetc (data)) {
- switch (tok) {
- case '\n':
- fsm = 1;
- break;
- case '#':
- if (1 == fsm || 2 == fsm)
- ++fsm;
- else
- fsm = 0;
- break;
- case ' ':
- if (3 == fsm)
- ++fsm;
- else
- fsm = 0;
- break;
- case 'W':
- if (4 == fsm && !feof (data)) /* leading "## W" eaten */
- read = fscanf (data, "arnings = %u\n## Assertions = %u\n"
- "## FailedAssertions = %u",
- &status->t_warn, &status->assert, &status->failed);
- default:
- fsm = 0;
- }
}
- if (3 != read) {
+ else if ( !rbscanf (&buf, "## FailedAssertions = ", status->failed)
+ || !rbscanf (&buf, "## Assertions = ", status->assert)
+ || !rbscanf (&buf, "## Warnings = ", status->t_warn)) {
status->status = ST_FORMAT;
}
}
-/**
- Arbitrary constant controling static read buffer size.
-
- @see check_example ()
-*/
-#define DELTA_BUF_LEN 64
/**
Parses output file out_name for the example target_name.