To get a nice, graphical display of some update logs for my SCM solution, I wrote the following quick and dirty parser.
The code is specific to parsing my update (.wud) files, but I'm including it here because it should be simple enough to modify for other basic parsing needs.
parse-wud.c
#include <stdio.h>
#include <stdlib.h>
/**
* Some typedefs and enums for neater look.
*/
enum eStopGo {
STOP = 0,
CONTINUE
};
typedef enum eStopGo eStopGo;
typedef char *StrPtr;
typedef void *VPtr;
/**
* These are the various status that the parser
* returns.
*/
enum eParseResult {
PR_PARSE_OK = 0,
PR_MODULE_PATH,
PR_ADDED_FILE,
PR_UPDATED_FILE,
PR_DELETED_FILE,
PR_MERGED_FILE,
PR_CONFLICTED_FILE,
PR_AT_REVISION_NUMBER,
PR_UPDT_REVISION_NUMBER,
PR_BAD_HANDLE,
PR_READ_FAILED,
PR_LINE_TOO_LONG,
PR_USER_STOPPED,
};
typedef enum eParseResult eParseResult;
/**
* This callback is passed each line of the input
* file + a parsed version (if recognized).
*/
typedef
eStopGo (*parse_wup_callback) (StrPtr pNextLine,
eParseResult pToken,
StrPtr pParsedLine,
VPtr pObj);
/**
* Parses a single line of input, recognizing certain patterns.
*/
eStopGo parse_wup_line (StrPtr pRaw, parse_wup_callback pCbk, VPtr pObj)
{
char pathtoken[] = "Update Path:\t";
char atrev[] = "At revision ";
char updrev[] = "Updated to revision ";
char added[] = "A ";
char updated[] = "U ";
char conflicted[] = "C ";
char merged[] = "G ";
char deleted[] = "D ";
/* helper macro for recognizing lines */
#define wup_startswith(x,y) if (!strncmp(pRaw,x, sizeof (x)-1)) \
return pCbk (pRaw,y,pRaw+(sizeof(x)-1),pObj)
wup_startswith (pathtoken, PR_MODULE_PATH);
wup_startswith (atrev, PR_AT_REVISION_NUMBER);
wup_startswith (updrev, PR_UPDT_REVISION_NUMBER);
wup_startswith (added, PR_ADDED_FILE);
wup_startswith (updated, PR_UPDATED_FILE);
wup_startswith (conflicted, PR_CONFLICTED_FILE);
wup_startswith (merged, PR_MERGED_FILE);
wup_startswith (deleted, PR_DELETED_FILE);
#undef wup_startswith
return pCbk (pRaw, 0, NULL, pObj);
}
/**
* This is the main function that parses a given WSCM Update Log (.wup) file.
*/
int parse_wup (StrPtr pFName, parse_wup_callback pCbk, VPtr pObj)
{
FILE *wup;
long sz, cz;
char buf [256];
char *p, *o;
/* Open the file for reading in text mode */
wup = fopen (pFName, "rt");
/* check that we have a valid file */
if (!wup) {
/* nope! return */
return PR_BAD_HANDLE;
}
/* helper macro for cleanup+return */
#define wup_err(x) fclose(wup);return (x);
/* read all file data in one go (it should be small enough, being
* some simple text */
o = NULL; /* Read into this */
sz = 0; /* Total size read */
while (cz = fread (buf, 1, sizeof (buf), wup)) {
/* allocate space to save read data */
p = malloc (sz + cz);
if (o) {
/* copy old data into new buffer */
memcpy (p, o, sz);
free (o);
}
/* copy data into new buffer */
memcpy (p + sz, buf, cz);
o = p;
sz += cz;
}
o [sz] = 0; /* Terminating null */
/* managed to read everthing correctly? */
if (!feof (wup) || !o) {
/* Nope! Fail */
wup_err (PR_READ_FAILED);
}
/* parse the entire file */
for (;1;) {
int i;
/* a single line at a time */
i = 0;
while ((*p) && (*p != '\r') && (*p != '\n')) {
buf [i++] = *p;
if (i == sizeof(buf)) {
wup_err (PR_LINE_TOO_LONG);
}
++p;
}
/* got some data on line */
if (i) {
buf[i] = 0;
/* parse it! */
if (parse_wup_line (buf, pCbk, pObj) != CONTINUE) {
/* user stopped us */
wup_err (PR_USER_STOPPED);
}
}
/* finished parsing entire input */
if (!*p) {
break;
}
/* continue with next line */
++p;
}
#undef wup_err
/* All ok! */
fclose (wup);
return PR_PARSE_OK;
}
/*****************************************************************************/
/* Sample code for testing */
eStopGo just_print (StrPtr pNextLine,
eParseResult pToken,
StrPtr pParsedLine,
VPtr pObj)
{
if (pParsedLine) {
switch (pToken) {
case PR_MODULE_PATH:
printf ("%s ==> %s [MODULE]\n", pNextLine, pParsedLine);
break;
case PR_ADDED_FILE:
printf ("%s ==> %s [ADDED]\n", pNextLine, pParsedLine);
break;
case PR_UPDATED_FILE:
printf ("%s ==> %s [UPDATED]\n", pNextLine, pParsedLine);
break;
case PR_DELETED_FILE:
printf ("%s ==> %s [DELETED]\n", pNextLine, pParsedLine);
break;
case PR_MERGED_FILE:
printf ("%s ==> %s [MERGED]\n", pNextLine, pParsedLine);
break;
case PR_CONFLICTED_FILE:
printf ("%s ==> %s [CONFLICTED]\n", pNextLine, pParsedLine);
break;
case PR_AT_REVISION_NUMBER:
printf ("%s ==> %s [AT REVISION NUMBER]\n", pNextLine, pParsedLine);
break;
case PR_UPDT_REVISION_NUMBER:
printf ("%s ==> %s [UPDATED REVISION NUMBER]\n", pNextLine, pParsedLine);
break;
default:
printf ("ERROR!!!!!!!!\n");
}
} else {
/* print starting with comments */
printf ("//%s\n", pNextLine);
}
return CONTINUE;
}
void
main (int argc, char *argv[])
{
int r;
if (argc != 2) {
printf ("Usage: %s <.wup file to parse>\n", argv [0]);
return;
}
r = parse_wup (argv [1], just_print, NULL);
if (r) {
printf ("Error: Failed parsing %s! [Reason: %d]\n", argv [1], r);
}
}