Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

tc_load.c

Go to the documentation of this file.
00001 /*
00002 ** Copyright (C) 2005 by Kevin L. Mitchell <klmitch@mit.edu>
00003 **
00004 ** This program is free software; you can redistribute it and/or modify
00005 ** it under the terms of the GNU General Public License as published by
00006 ** the Free Software Foundation; either version 2 of the License, or
00007 ** (at your option) any later version.
00008 **
00009 ** This program is distributed in the hope that it will be useful,
00010 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 ** GNU General Public License for more details.
00013 **
00014 ** You should have received a copy of the GNU General Public License
00015 ** along with this program; if not, write to the Free Software
00016 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 **
00018 ** @(#)$Id: tc_load.c,v 1.4 2005/06/07 04:53:50 klmitch Exp $
00019 */
00027 #include <ctype.h>
00028 #include <errno.h>
00029 #include <fcntl.h>
00030 #include <sys/stat.h>
00031 #include <sys/types.h>
00032 #include <unistd.h>
00033 
00034 #include "treeconf_int.h"
00035 
00036 RCSTAG("@(#)$Id: tc_load.c,v 1.4 2005/06/07 04:53:50 klmitch Exp $");
00037 
00043 #define Q_O -3
00044 
00050 #define Q_X -2
00051 
00057 #define Q_S -1
00058 
00066 static int xform[] = {
00067   '\a',   '\b',    Q_S,    Q_S, '\033',   '\f',    Q_S,    Q_S,    Q_S,
00068    Q_S,    Q_S,    Q_S,    Q_S,   '\n',    Q_S,    Q_S,    Q_S,    Q_S,
00069    Q_S,   '\t',    Q_S,   '\v',    Q_S,    Q_X,    Q_S,    Q_S
00070 };
00071 
00082 #define xlate(c)  (islower((c)) ? xform[(int)(c - 'a')] :               \
00083                    ((c >= '0' && c <= '7') ? Q_O : Q_S))
00084 
00095 #define accum(buf, c)   do {                                      \
00096   if (buf ## _pos >= sizeof(buf) - 1) /* but we overflow it... */       \
00097     _tc_doerror(ENOSPC); /* no more space... */                         \
00098   else /* otherwise, add the character to the buffer */                       \
00099     buf[buf ## _pos++] = (c);                                     \
00100 } while (0)
00101 
00109 #define clear(buf)      (buf ## _pos = 0) /* clear buffer length... */
00110 
00119 #define finish(buf)     (buf[buf ## _pos] = '\0') /* terminate the buffer */
00120 
00129 #define cunget(chr)     do {                                      \
00130   if (buf_pos <= 0) /* backing up would cause problems... */                  \
00131     _tc_doerror(EINVAL); /* really shouldn't happen, but just in case... */   \
00132   count++; /* putting the character back... */                          \
00133   buf_pos--; /* back up in the buffer */                          \
00134   c = (chr); /* make current char this char */                          \
00135 } while (0)
00136 
00143 #define FL_DQUOTE 0x0001
00144 
00151 #define FL_SQUOTE 0x0002
00152 
00158 #define FL_CQUOTE 0x0004
00159 
00166 #define FL_OCTCHR 0x0008
00167 
00174 #define FL_HEXCHR 0x0010
00175 
00182 #define FL_QUOTEMASK    (FL_DQUOTE | FL_SQUOTE | FL_CQUOTE |                  \
00183                    FL_OCTCHR | FL_HEXCHR)
00184 
00190 #define FL_SPACE  0x0020
00191 
00200 #define quoted()  (flags & FL_QUOTEMASK)
00201 
00202 unsigned int
00203 tc_load(const char *file, void *v_ctx)
00204 {
00205   treeconf_ctx_t *ctx = (treeconf_ctx_t *)v_ctx;
00206   treeconf_node_t *parent = 0;
00207   char buf[TC_BUFSIZE], nambuf[TC_NAMSIZE], valbuf[TC_BUFSIZE];
00208   enum {
00209     comm_none, comm_nl, comm_slash, comm_star, comm_c
00210   } comm_state = comm_none;
00211   enum {
00212     st_init, st_parent, st_close, st_varname, st_equals, st_value
00213   } state = st_init;
00214   unsigned int err = 0, flags = FL_SPACE;
00215   int count = 0, buf_pos = 0, nambuf_pos = 0, valbuf_pos = 0, fd;
00216   int c, q = 0, siz = 0;
00217 
00218   initialize_trcf_error_table();
00219 
00220   /* Verify arguments... */
00221   if (!file || !*file || !tx_verify(ctx))
00222     return EINVAL;
00223 
00224   /* try to open the file... */
00225   if ((fd = open(file, O_RDONLY)) < 0)
00226     return errno;
00227 
00228   /* Now, let's loop through the characters of the file... */
00229   while (1) { /* bail out if an error occurs... */
00230     if (!count) { /* need more characters...hmmm... */
00231       if (!(count = read(fd, buf, sizeof(buf)))) /* Got EOF? */
00232       break; /* all done; let's get out of here--break out of while loop */
00233       else if (count < 0) /* some error occurred... */
00234       _tc_doerror(errno);
00235       buf_pos = 0; /* reset the buffer position */
00236     }
00237     c = buf[buf_pos++]; /* get a character from the buffer... */
00238     count--; /* and remember how many characters are left */
00239 
00240     if (!quoted()) { /* no comments inside of quoted strings */
00241       if (comm_state == comm_none) { /* beginning of comment? */
00242       if (c == '#') /* shell comment... */
00243         comm_state = comm_nl; /* comment until newline */
00244       else if (c == '/') /* C- or C++-style comment... */
00245         comm_state = comm_slash; /* looking for one more chraacter... */
00246       } else if (comm_state == comm_slash) {
00247       if (c == '/') /* C++-style comment! */
00248         comm_state = comm_nl;
00249       else if (c == '*') /* C-style comment! */
00250         comm_state = comm_c;
00251       else { /* not a comment introducer; process the previous '/' */
00252         comm_state = comm_none; /* not in a comment anymore */
00253         cunget('/');
00254       }
00255       } else if (comm_state == comm_c && c == '*')
00256       comm_state = comm_star; /* possible end of a C-style comment */
00257       else if (comm_state == comm_star) {
00258       if (c == '/') { /* end of a C-style comment... */
00259         comm_state = comm_none; /* end of comment reached */
00260         c = ' '; /* C-style comments turn into spaces */
00261       } else if (c != '*') /* not another star... */
00262         comm_state = comm_c; /* so go back to C comment state! */
00263       } else if (comm_state == comm_nl && c == '\n')
00264       comm_state = comm_none; /* end of shell or C++-style comment */
00265       /* NL-style comments look like newlines... */
00266 
00267       if (comm_state != comm_none) /* if we're in a comment state... */
00268       continue; /* go to next character! */
00269     }
00270 
00271     /* Now, check for string quoting... */
00272     if (c == '"' && !((flags & FL_QUOTEMASK) & ~FL_DQUOTE)) {
00273       flags ^= FL_DQUOTE; /* toggle double-quoting state */
00274       continue; /* go to next character */
00275     } else if (c == '\'' && !((flags & FL_QUOTEMASK) & ~FL_SQUOTE)) {
00276       flags ^= FL_SQUOTE; /* toggle single-quoting state */
00277       continue; /* go to next character */
00278     } else if (c == '\\' && !((flags & FL_QUOTEMASK) & ~FL_DQUOTE)) {
00279       flags |= FL_CQUOTE; /* mark next character as quoted... */
00280       continue; /* go to next character */
00281     } else if (flags & FL_OCTCHR) { /* build up octal character */
00282       if (c >= '0' && c <= '7') {
00283       q = (q << 3) | (c - '0');
00284 
00285       if (--siz) /* can still accumulate some characters... */
00286         continue; /* so go get another */
00287 
00288       c = q; /* definitely hit end, but don't need to unget char */
00289       } else
00290       cunget(q); /* feed in the escaped character */
00291 
00292       flags &= ~FL_OCTCHR; /* done building escape */
00293     } else if (flags & FL_HEXCHR) { /* build up hex character */
00294       if (isxdigit(c)) { /* if it is a hex digit... */
00295       if (isdigit(c)) /* it's a numeral... */
00296         q = (q << 4) | (c - '0'); /* so convert it as such! */
00297       else if (c >= 'a' && c <= 'f') /* lowercase hex letter... */
00298         q = (q << 4) | (c - 'a' + 0x0a);
00299       else if (c >= 'A' && c <= 'F') /* uppercase hex letter... */
00300         q = (q << 4) | (c - 'A' + 0x0a);
00301 
00302       if (--siz) /* can still accumulate some characters... */
00303         continue; /* so get another */
00304 
00305       c = q; /* definitely hit end, but don't need to unget char */
00306       } else
00307       cunget(q); /* feed in the escaped character */
00308 
00309       flags &= ~FL_HEXCHR; /* done building escape */
00310     } else if (flags & FL_CQUOTE)
00311       switch ((q = xlate(c))) { /* translate quoted character... */
00312       case Q_S: /* quote self; leave c alone... */
00313       break;
00314       case Q_X: /* quote hexadecimal... */
00315       flags |= FL_HEXCHR;
00316       q = 0; /* zero quoted character */
00317       siz = 2; /* can take up to 2 digits... */
00318       continue; /* process next digit */
00319       break;
00320       case Q_O: /* quote octal... */
00321       flags |= FL_OCTCHR;
00322       q = (c - '0'); /* initialize quoted character */
00323       siz = (q <= 3) ? 1 : 2; /* \377, but \40 */
00324       continue; /* process next digit */
00325       break;
00326       default:
00327       c = q; /* use substituted character from xlate() */
00328       break;
00329       }
00330 
00331     if (flags & FL_SPACE) { /* accepting spaces... */
00332       if (quoted() || !isspace(c)) /* is it a space? */
00333       flags &= ~FL_SPACE; /* ate up all the spaces */
00334       else
00335       continue; /* ok, eat this space and get the next one */
00336     }
00337 
00338     switch (state) { /* process char */
00339     case st_init: /* initial state... */
00340       if (!quoted() && c == '[') { /* introducing a parent... */
00341       clear(nambuf); /* clear name buffer... */
00342       state = st_parent; /* parent state... */
00343       flags |= FL_SPACE; /* but allow leading spaces... */
00344       } else if (quoted() || isalpha(c) || c == '_') { /* start of var name */
00345       clear(nambuf); /* clear name buffer... */
00346       accum(nambuf, c); /* and add first character to it. */
00347       state = st_varname; /* getting variable name... */
00348       } else if (!isspace(c))
00349       err = EINVAL; /* weird character in name... */
00350       break;
00351 
00352     case st_parent: /* building up a variable context */
00353       if (!quoted() && (c == ']' || isspace(c))) { /* hit end of name */
00354       finish(nambuf); /* finish the buffer... */
00355 
00356       if (!nambuf[0]) /* "[ ]"--no parent */
00357         parent = 0;
00358       else if ((err = tc_find(ctx, nambuf, &parent, 0))) /* look it up */
00359         goto error; /* jump to error handling... */
00360 
00361       state = (c == ']') ? st_init : st_close; /* look for closing ']' */
00362       flags |= FL_SPACE; /* allow empty spaces... */
00363       } else
00364       accum(nambuf, c); /* add character to buffer */
00365       break;
00366 
00367     case st_varname: /* building up a variable name */
00368       if (!quoted() && c == '=') { /* found equals...hmmm... */
00369       finish(nambuf); /* finish the buffer... */
00370       clear(valbuf);
00371       state = st_value; /* look for value... */
00372       flags |= FL_SPACE; /* allow empty spaces before value */
00373       } else if (!quoted() && c == ';') { /* resetting variable... */
00374       finish(nambuf); /* finish the buffer... */
00375 
00376       if ((err = tc_set(ctx, nambuf, 0, parent)))
00377         goto error; /* jump to error handling if set fails... */
00378 
00379       state = st_init; /* go back to initial state */
00380       flags |= FL_SPACE; /* and accept leading spaces */
00381       } else if (!quoted() && isspace(c)) { /* hit end of name */
00382       finish(nambuf); /* finish the buffer... */
00383 
00384       state = st_equals; /* look for an equals... */
00385       flags |= FL_SPACE; /* allow empty spaces before equals */
00386       } else
00387       accum(nambuf, c); /* add character to buffer */
00388       break;
00389 
00390     case st_close: /* looking for closing ']' */
00391       if (quoted() || c != ']') /* um... */
00392       _tc_doerror(EINVAL); /* invalid character for that context */
00393       else {
00394       state = st_init; /* go back to initial state */
00395       flags |= FL_SPACE; /* and skip any whitespace */
00396       }
00397       break;
00398 
00399     case st_equals: /* non-space character MUST be '=' */
00400       if (quoted() || (c != '=' && c != ';')) /* um... */
00401       _tc_doerror(EINVAL); /* invalid character for that context */
00402       else if (c == ';') { /* reset variable to default value */
00403       if ((err = tc_set(ctx, nambuf, 0, parent)))
00404         goto error; /* jump to error handling if set fails... */
00405 
00406       state = st_init; /* go back to initial state */
00407       flags |= FL_SPACE; /* and accept leading spaces */
00408       } else {
00409       clear(valbuf); /* initialize value buffer */
00410       state = st_value; /* switch to value state... */
00411       flags |= FL_SPACE; /* but accept leading spaces */
00412       }
00413       break;
00414 
00415     case st_value:
00416       if (!quoted() && c == ';') { /* finished reading value... */
00417       finish(valbuf); /* finish the value buffer... */
00418 
00419       if ((err = tc_set(ctx, nambuf, valbuf, parent))) /* set the value */
00420         goto error; /* jump to error handling if set fails... */
00421       state = st_init; /* switch back to initial state */
00422       flags |= FL_SPACE; /* and accept spaces */
00423       } else if (!quoted() && isspace(c)) { /* turn unquoted space into ' ' */
00424       accum(valbuf, ' '); /* accumulate a single space... */
00425       flags |= FL_SPACE; /* and collapse any remaining spaces */
00426       } else
00427       accum(valbuf, c); /* add character to value buffer */
00428       break;
00429     }
00430 
00431     flags &= ~FL_CQUOTE; /* turn off single character quote */
00432   }
00433 
00434   if (comm_state != comm_none || quoted()) /* run-away string or comment? */
00435     err = EINVAL; /* file invalid */
00436 
00437  error:
00438   close(fd); /* ignore errors on close... */
00439 
00440   return err; /* all done; return any errors */
00441 }

Generated on Wed Jun 8 09:18:27 2005 for treeconf by  doxygen 1.3.9.1