GridLab
Grid Application Toolkit

A simple API for Grid Applications
GAT

Menu



Main Page   Alphabetical List   Compound List   File List   Compound Members   File Members  

GATConfig.c

Go to the documentation of this file.
00001 /** @file GATConfig.c
00002  * Main file for the GATConfig class.
00003  * 
00004  * A GATConfig object contains a set of configuration tables
00005  * which define configuration parameters for the system and
00006  * for specific adaptors.
00007  * 
00008  * @date Tue Sep 23 2003
00009  * 
00010  * @version $Header: /export/cvs-gridlab/wp-1/Codes/GATEngine/C-reference/src/GATConfig.c,v 1.18 2004/04/28 10:50:10 hartmutkaiser Exp $
00011  *
00012  *  Copyright (C) Tom Goodale
00013  *  This file is part of the GAT Engine.
00014  *  Contributed by Tom Goodale <goodale@aei.mpg.de>.
00015  *
00016  *  Use, modification and distribution is subject to the Gridlab Software
00017  *  License. (See accompanying file GLlicense.txt or copy at
00018  *  http://www.gridlab.org/GLlicense.txt)
00019  */
00020 
00021 static const char *rcsid = "$Header: /export/cvs-gridlab/wp-1/Codes/GATEngine/C-reference/src/GATConfig.c,v 1.18 2004/04/28 10:50:10 hartmutkaiser Exp $";
00022 
00023 /* System Header Files */
00024 
00025 #include <errno.h>
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <ctype.h>
00030 #include <limits.h>
00031 #include <unistd.h>
00032 #include <sys/stat.h>
00033 
00034 /* GAT Header Files */
00035 
00036 #include "GATUtil.h"
00037 #include "GATConfig.h"
00038 #include "GATTable.h"
00039 #include "GATContext.h"
00040 #include "GATErrors.h"
00041 
00042 /* Macros */
00043 
00044 #define LINE_LENGTH 1024
00045 #define RC_FILENAME ".gatrc"
00046 
00047 #if !defined(MAX_PATH)
00048 #define MAX_PATH    _POSIX_PATH_MAX
00049 #endif
00050 
00051 /* Structures, unions and enums */
00052 
00053 enum linetype {line_blank, 
00054                line_comment, 
00055                line_section, 
00056                line_adaptor,
00057                line_string};
00058 
00059 enum parse_error 
00060 {
00061   parse_success,
00062   parse_empty_token,
00063   parse_memory_failure,
00064   parse_malformed_line
00065 };
00066 
00067 struct adaptor_list
00068 {
00069   struct adaptor_list *next;
00070   char *name;
00071   GATConfigTableList configs;
00072 };
00073 
00074 struct GATConfig_S
00075 {
00076   GATTable system_config;
00077   struct adaptor_list *adaptor_configs;
00078 };
00079 
00080 /* Static function prototypes */
00081 
00082 static GATResult
00083 AddToAdaptorConfig(GATContext error_context, struct adaptor_list *adaptor, 
00084   const char *nickname, GATTable config);
00085 
00086 static GATResult
00087 ParseConfigFile(GATConfig this, GATContext error_context, 
00088   const char *filename);
00089 
00090 static enum parse_error Tokenise(const char *line, 
00091                                  char **token1, 
00092                                  char **token2,
00093                                  enum linetype *type);
00094 
00095 static enum parse_error ParseKeyVal(const char *string,
00096                                     char **token1, 
00097                                     char **token2,
00098                                     enum linetype *type);
00099 
00100 static enum parse_error ParseString(const char *token_start,
00101                                     char **buffer);
00102 
00103 static enum parse_error ParseSection(const char *token_start,
00104                                      char **buffer);
00105 
00106 /* File scope variables */
00107 
00108 /* External functions */
00109 
00110 /** GATConfig_Create
00111  *  The  GATConfig constructor.
00112  *  This is the constructor for GATConfig objects.
00113  *
00114  * @return A new GATConfig
00115  */
00116 GATConfig GATConfig_Create(GATContext error_context)
00117 {
00118   GAT_USES_STATUS(error_context, "GATConfig_Create");
00119   
00120   GATConfig new_config = (GATConfig)malloc(sizeof(struct GATConfig_S));
00121 
00122   /* Create the structure */
00123   if (NULL != new_config)
00124   {
00125     char *config_path = NULL;
00126     char const *env_var = NULL;
00127 
00128     new_config->system_config = GATTable_Create();
00129     new_config->adaptor_configs = NULL;
00130 
00131     /* Now parse the configuration file */
00132     if (NULL != (env_var = getenv("GAT_CONFIG_FILE")))
00133     {
00134       config_path = GATUtil_strdup(env_var);
00135     }
00136     else if (NULL != (env_var = getenv("HOME")))
00137     {
00138       config_path = (char *)malloc(strlen(env_var)+sizeof(RC_FILENAME)+2);
00139       if (NULL != config_path)
00140       {
00141         struct stat buf;
00142         sprintf(config_path, "%s/%s", env_var, RC_FILENAME);
00143         if (stat(config_path, &buf))
00144         {
00145           /* Error stating the file, try to create it. */
00146           int fd = creat(config_path, S_IREAD | S_IWRITE);
00147           close(fd);
00148           
00149           /* free the allocated memory */
00150           free(config_path);
00151           config_path = NULL;
00152         }
00153       }
00154       else
00155       {
00156         GAT_CREATE_STATUS(GAT_MEMORYFAILURE);
00157       }
00158     }
00159 
00160     /* FIXME: should also try a system.gatrc file as a fallback.
00161      *        Should this be in <prefix>/etc ?
00162      */
00163     if (NULL != config_path)
00164     {
00165       GATResult retval = ParseConfigFile(new_config, error_context, config_path);
00166       if (GAT_FAILED(retval))
00167       {
00168         char buffer[MAX_PATH+100];
00169         sprintf(buffer, "Error opening config file '%s'", config_path);
00170         GAT_CREATE_STATUS_MSG(retval, buffer);
00171       }
00172       free(config_path);
00173     }
00174   }
00175   else
00176   {
00177     GAT_CREATE_STATUS(GAT_MEMORYFAILURE);
00178   }
00179 
00180   if (GAT_FAILED(GAT_CURRENT_STATUS()))
00181   {
00182     GATConfig_Destroy(&new_config);
00183   }
00184   
00185   GAT_STORE_STATUS();
00186   return new_config;
00187 }
00188 
00189 /** GATConfig_Destroy
00190  *  The GATConfig destructor.
00191  *  This is the destructor for GATConfig objects.
00192  *
00193  * @param this An old GATConfig
00194  */
00195 void GATConfig_Destroy(GATConfig *object)
00196 {
00197   if(NULL != object && NULL != *object)
00198   {
00199     struct adaptor_list *current = (*object)->adaptor_configs;
00200     while(NULL != current)
00201     {
00202       struct adaptor_list *next = current->next;
00203       GATConfigTableList current_config_table = current->configs;
00204 
00205       while (NULL != current_config_table)
00206       {
00207         GATConfigTableList next_config_table = current_config_table->next;
00208         
00209         GATTable_Destroy(&current_config_table->config);
00210         free(current_config_table->nickname);
00211         free(current_config_table);
00212         
00213         current_config_table = next_config_table;
00214       }
00215       free(current->name);
00216       free(current);
00217 
00218       current = next;
00219     }
00220     
00221     GATTable_Destroy(&((*object)->system_config));
00222     free (*object);
00223     *object = NULL;
00224   }
00225 }
00226 
00227 /** GATConfig_GetSystemConfig
00228  *  Gets the system configuration table.
00229  *  Gets the GATTable holding system configuration data.
00230  *
00231  * @param this The GATConfig structure
00232  * 
00233  * @return The system configuration table.
00234  */
00235 GATTable_const GATConfig_GetSystemConfig(GATConfig_const this)
00236 {
00237   return this->system_config;
00238 }
00239 
00240 /** GATConfig_GetAdaptorConfigs
00241  *  Gets the configuration tables for an adaptor.
00242  *  An adaptor may have more than one configuration table
00243  *  associated with it.  This function returns a list of
00244  *  them all.
00245  *
00246  * @param this The GATConfig structure
00247  * @param adaptor The adaptor to get the tables for.
00248  * 
00249  * @return A list of adaptor configuration tables.
00250  */
00251 GATConfigTableList_const 
00252 GATConfig_GetAdaptorConfigs(GATConfig_const this, const char *adaptor)
00253 {
00254   GATConfigTableList retval = NULL;
00255   struct adaptor_list *current = this->adaptor_configs;
00256 
00257   for(/**/; NULL != current; current = current->next)
00258   {
00259     if (!strcmp(current->name, adaptor))
00260     {
00261       retval = current->configs;
00262       break;
00263     }
00264   }
00265   return retval;
00266 }
00267 
00268 /** GATConfig_AddAdaptorConfig
00269  *  Adds a configuration table to an adaptor.
00270  *  An adaptor may have more than one configuration table
00271  *  associated with it.  This function add a new table.
00272  *
00273  * @param this The GATConfig structure
00274  * @param adaptor The adaptor to add the tables to.
00275  * @param nickname The name for thisinstance of the adaptor.
00276  * @param config The new configuration table.
00277  * 
00278  * @return An error code.
00279  */
00280 GATResult 
00281 GATConfig_AddAdaptorConfig(GATConfig this, GATContext error_context, 
00282   const char *adaptor, const char *nickname, GATTable config)
00283 {
00284   GAT_USES_STATUS(error_context, "GATConfig_AddAdaptorConfig");
00285 
00286   struct adaptor_list *current = NULL;
00287   struct adaptor_list *last = NULL;
00288 
00289   /* Look for the adaptor in the list */
00290   for(current = this->adaptor_configs; NULL != current;
00291       last = current, current = current->next)
00292   {
00293     if (!strcmp(current->name, adaptor))
00294     {
00295       break;    /* This already exists */
00296     }
00297   }
00298   
00299   if (NULL == current)
00300   {
00301     struct adaptor_list *new_adaptor = 
00302       (struct adaptor_list *)malloc(sizeof(*new_adaptor));
00303     if (NULL != new_adaptor)
00304     {
00305       new_adaptor->name = GATUtil_strdup(adaptor);
00306       if (new_adaptor->name)
00307       {
00308         /* Add to list */
00309         new_adaptor->next = NULL;
00310         if (NULL != last)
00311         {
00312           last->next = new_adaptor;
00313         }
00314         else
00315         {
00316           this->adaptor_configs = new_adaptor;
00317         }
00318         new_adaptor->configs = NULL;
00319       }
00320       else
00321       {
00322         free(new_adaptor);
00323         new_adaptor = NULL;
00324         GAT_CREATE_STATUS(GAT_MEMORYFAILURE);
00325       }
00326     }
00327     else
00328     {
00329       GAT_CREATE_STATUS(GAT_MEMORYFAILURE);
00330     }
00331     current = new_adaptor;
00332   }
00333 
00334   if (NULL != current)
00335   {
00336     GAT_CREATE_STATUS(AddToAdaptorConfig(error_context, current, nickname, 
00337       config));
00338   }
00339   
00340   return GAT_RETURN_STATUS();
00341 }
00342 
00343 /** AddAdaptorToConfig
00344  *  Adds a configuration table to an internal list for an adaptor.
00345  *  An adaptor may have more than one configuration table
00346  *  associated with it.  This function add a new table to the
00347  *  internal list maintained by a GATConfig structure.
00348  *
00349  * @param adaptor_list Structure to add the config to.
00350  * @param nickname The name for thisinstance of the adaptor.
00351  * @param config The new configuration table.
00352  * 
00353  * @return An error code.
00354  */
00355 static GATResult
00356 AddToAdaptorConfig(GATContext error_context, struct adaptor_list *adaptor, 
00357   const char *nickname, GATTable config)
00358 {
00359   GAT_USES_STATUS(error_context, "AddToAdaptorConfig");
00360 
00361   GATConfigTableList current;
00362   GATConfigTableList last = NULL;
00363 
00364   /* Look through the config list, it should be alphabetical */
00365   for (current = adaptor->configs; NULL != current; 
00366        last = current, current = current->next)
00367   {
00368     if (!strcmp(current->nickname, nickname))
00369     {
00370       /* This already exists */
00371       GAT_CREATE_STATUS(GAT_DUPLICATE_CONFIG);
00372       break;
00373     }
00374   }
00375 
00376   if (NULL == current)
00377   {
00378     GATConfigTableList new_config = 
00379       (GATConfigTableList)malloc(sizeof(*new_config));
00380     if (NULL != new_config)
00381     {
00382       new_config->nickname = GATUtil_strdup(nickname);
00383       if (NULL != new_config->nickname)
00384       {
00385         /* Add to list */
00386         new_config->next = NULL;
00387         if (NULL != last)
00388         {
00389           last->next = new_config;
00390         }
00391         else
00392         {
00393           adaptor->configs = new_config;
00394         }
00395         new_config->config = config;
00396       }
00397       else
00398       {
00399         free(new_config);
00400         new_config = NULL;
00401         GAT_CREATE_STATUS(GAT_MEMORYFAILURE);
00402       }
00403     }
00404     else
00405     {
00406       GAT_CREATE_STATUS(GAT_MEMORYFAILURE);
00407     }
00408   }
00409 
00410   return GAT_RETURN_STATUS();
00411 }
00412 
00413 /** ParseConfigFile
00414  *  Parse a GAT configuration file.
00415  *  Open and parse the contents of a GAT configuration file, 
00416  *  creating new configuration tables as necessary.
00417  *  
00418  * @param this The GATConfig structure
00419  * @param filename The configuration filename.
00420  * 
00421  * @return An error code.
00422  */
00423 static GATResult 
00424 ParseConfigFile(GATConfig this, GATContext error_context, const char *filename)
00425 {
00426   GAT_USES_STATUS(error_context, "ParseConfigFile");
00427   
00428   FILE *file = NULL;
00429   
00430   if (NULL != (file = fopen(filename, "r")))
00431   {
00432     enum parse_error error;
00433     enum linetype type;
00434     char line[LINE_LENGTH+1];
00435     int linenumber = 0;
00436     char *section = NULL;
00437     char *adaptor = NULL;
00438     GATTable current_config = this->system_config;
00439 
00440     /* FIXME:  This whole parsing sequence should be replaced
00441      * by something more robust against really long lines.
00442      * This is a stop-gap quick solution.
00443      */
00444     while (NULL != fgets(line, LINE_LENGTH+1, file))
00445     {
00446       char *token1 = NULL;
00447       char *token2 = NULL;
00448 
00449       linenumber++;
00450       error = Tokenise(line, &token1, &token2, &type);
00451       
00452       switch(error)
00453       {
00454         case parse_success:
00455           switch(type)
00456           {
00457             case line_blank: 
00458               break;
00459             
00460             case line_comment: 
00461               break;
00462               
00463             case line_section: 
00464               if (NULL != section)
00465               {
00466                 if (NULL != adaptor)
00467                 {
00468                   GAT_CREATE_STATUS(GATConfig_AddAdaptorConfig(this, 
00469                     error_context, adaptor, section, current_config));
00470                   free(adaptor);
00471                   adaptor = NULL;
00472                 }
00473                 else
00474                 {
00475                   char buffer[MAX_PATH+100];
00476                   
00477                   sprintf(buffer, "Error <%s:%d>: No adaptor defined in "
00478                     "section '%s'.", filename, linenumber, section);
00479                   GAT_CREATE_STATUS_MSG(GAT_INVALID_CONFIG_FORMAT, buffer);
00480                 }
00481                 free(section);
00482               }
00483               section = token1;
00484               current_config = GATTable_Create();
00485               break;
00486               
00487             case line_adaptor: 
00488               if (NULL != adaptor)
00489               {
00490                 char buffer[MAX_PATH+100];
00491                 sprintf(buffer, "Error <%s:%d>: Second adaptor defined in "
00492                   "section '%s'", filename, linenumber, section);
00493                 GAT_CREATE_STATUS_MSG(GAT_INVALID_CONFIG_FORMAT, buffer);
00494               } 
00495               else if (NULL == section)
00496               {
00497                 char buffer[MAX_PATH+100];
00498                 sprintf(buffer, "Error <%s:%d>: Can't define adaptor in "
00499                   "default section", filename, linenumber);
00500               }
00501               else
00502               {
00503                 adaptor = token1;
00504               }
00505               break;
00506               
00507             case line_string:
00508               /* FIXME: Should parse this and put numeric types in as
00509                * ints and doubles depending on what they look like.
00510                */
00511               GATTable_Add_String(current_config, token1, token2);
00512               free(token1);
00513               free(token2);
00514               token1 = NULL;
00515               token2 = NULL;
00516               break;
00517           }
00518           break;
00519           
00520         case parse_empty_token:
00521           {
00522             char buffer[MAX_PATH+100];
00523             sprintf(buffer, "Error <%s:%d>: Empty token when parsing line", 
00524               filename, linenumber);
00525             GAT_CREATE_STATUS_MSG(GAT_INVALID_CONFIG_FORMAT, buffer);
00526           }
00527           break;
00528           
00529         case parse_memory_failure:
00530           {
00531             char buffer[MAX_PATH+100];
00532             sprintf(buffer, "Error <%s:%d>: Out of memory when parsing line", 
00533               filename, linenumber);
00534             GAT_CREATE_STATUS_MSG(GAT_INVALID_CONFIG_FORMAT, buffer);
00535           }
00536           break;
00537         
00538         case parse_malformed_line:
00539           {
00540             char buffer[MAX_PATH+100];
00541             sprintf(buffer, "Error <%s:%d>: Malformed line", filename, 
00542               linenumber);
00543             GAT_CREATE_STATUS_MSG(GAT_INVALID_CONFIG_FORMAT, buffer);
00544           }
00545           break;
00546       }
00547     }
00548 
00549     /* Finish the last section */
00550 
00551     if (NULL != section)
00552     {
00553       if (NULL != adaptor)
00554       {
00555         GAT_CREATE_STATUS(GATConfig_AddAdaptorConfig(this, error_context, 
00556           adaptor, section, current_config));
00557         free(adaptor);
00558         adaptor = NULL;
00559       }
00560       else
00561       {
00562         char buffer[MAX_PATH+100];
00563         sprintf(buffer, "Error <%s:%d>: No adaptor defined in section '%s'.",
00564           filename, linenumber, section);
00565         GAT_CREATE_STATUS_MSG(GAT_INVALID_CONFIG_FORMAT, buffer);
00566       }
00567       free(section);
00568     }
00569   }
00570   else
00571   {
00572     GAT_CREATE_STATUS(GAT_FILEOPEN_ERROR);
00573   }
00574 
00575   return GAT_RETURN_STATUS();
00576 }
00577 
00578 
00579 /** Tokenise
00580  *  Determine what sort of string has been pased in and split into tokens.
00581  *  Configuration file lines are either blank, comments, section 
00582  *  definitions, adaptor definitions, or key/value pairs for the 
00583  *  table. This function determines which, and splits the line into the
00584  *  requisite number of tokens.
00585  *
00586  * @param line The line to tokenise
00587  * @param token1 Buffer for first token on line.
00588  * @param token2 Buffer for first token on line.
00589  * @param type What type the line is.
00590  * 
00591  * @return An error code.
00592  */
00593 static enum parse_error Tokenise(const char *line, 
00594                                  char **token1, 
00595                                  char **token2,
00596                                  enum linetype *type)
00597 {
00598   enum parse_error error;
00599   const char *token_start;
00600 
00601   error = parse_success;
00602 
00603   /* Remove leading whitespace */
00604   for(token_start = line;
00605       isspace(*token_start);
00606       token_start++)
00607   {
00608     /* Do nothing */
00609   }
00610 
00611   switch(*token_start)
00612   {
00613     case 0 :
00614       *type = line_blank;
00615       break;
00616     case '#' :
00617       *type = line_comment;
00618       break;
00619     case '[' :
00620       *type = line_section;
00621       error = ParseSection(token_start+1, token1);
00622       break;
00623     default :
00624       error = ParseKeyVal(token_start, token1, token2, type);
00625   }
00626 
00627   return error;
00628 }
00629 
00630 /** ParseKeyVal
00631  *  Parse a line defining a key/value pair.
00632  *  Key/Value lines are of the form token1 = token2
00633  *  Where token1 cannot have an "=" in it.
00634  *  Such lines are either destined for the configuration table
00635  *  or define an adaptor.
00636  *
00637  * @param string The string to tokenise
00638  * @param token1 Buffer for first token on line.
00639  * @param token2 Buffer for first token on line.
00640  * @param type What type the line is.
00641  * 
00642  * @return An error code.
00643  */
00644 static enum parse_error ParseKeyVal(const char *string,
00645                                     char **token1, 
00646                                     char **token2,
00647                                     enum linetype *type)
00648 {
00649   enum parse_error error;
00650   const char *equals;
00651   const char *token_end;
00652   int i;
00653 
00654   if((equals = strchr(string, '=')))
00655   {
00656     for(token_end = equals-1;
00657         token_end >= string && isspace(*token_end);
00658         token_end--)        
00659     {
00660       /* Do nothing */
00661     }
00662 
00663     if(!strncmp(string, "Adaptor", token_end - string))
00664     {
00665       *type = line_adaptor;
00666       error = ParseString(equals+1, token1);
00667     }
00668     else
00669     {
00670       *type = line_string;
00671 
00672       if(token_end - string > 0)
00673       {
00674         *token1 = (char *)malloc(token_end-string+2);
00675       
00676         if(*token1)
00677         {
00678           for(i=0; i <= token_end - string; i++)
00679           {
00680             (*token1)[i] = string[i];
00681           }
00682           (*token1)[i] = 0;
00683 
00684           error = ParseString(equals+1, token2);
00685         }
00686         else
00687         {
00688           error = parse_memory_failure;
00689         }
00690       }
00691       else
00692       {
00693         error = parse_empty_token;
00694       }
00695     }
00696   }
00697   else
00698   {
00699     error = parse_malformed_line;
00700   }
00701   
00702   return error;
00703 }
00704 
00705 /** ParseString
00706  *  Parse a string token
00707  *  Strip whitespace from either end of a string and 
00708  *  get rid of comments.
00709  *
00710  * @param token_start The beginning of the token.
00711  * @param buffer Buffer to put the stripped token into.
00712  * 
00713  * @return An error code.
00714  */
00715 static enum parse_error ParseString(const char *token_start,
00716                                     char **buffer)
00717 {
00718   enum parse_error error;
00719   const char *token_end;
00720   int i;
00721 
00722   error = parse_success;
00723 
00724   /* Remove leading whitespace */
00725   for(;
00726       isspace(*token_start);
00727       token_start++)
00728   {
00729     /* Do nothing */
00730   }
00731 
00732   /* Remove any comment */
00733   token_end = strchr(token_start, '#');
00734 
00735   if(! token_end)
00736   {
00737     token_end = token_start+strlen(token_start)-1;
00738   }
00739   else
00740   {
00741     token_end--;
00742   }
00743 
00744   /* Remove trailing whitespace */
00745   for(;
00746       token_end >= token_start && isspace(*token_end);
00747       token_end--)
00748   {
00749     /* Do nothing */
00750   }
00751 
00752   if(token_end - token_start > 0)
00753   {
00754     *buffer = (char *)malloc(token_end - token_start+2);
00755 
00756     if(*buffer)
00757     {
00758       for(i=0; i <= token_end - token_start; i++)
00759       {
00760         (*buffer)[i] = token_start[i];
00761       }
00762       (*buffer)[i] = 0;
00763     }
00764     else
00765     {
00766       error = parse_memory_failure;
00767     }
00768   }
00769   else
00770   {
00771     error = parse_empty_token;
00772   }
00773     
00774 
00775   return error;
00776 }
00777 
00778 /** ParseSection
00779  *  Parse a section definition
00780  *  Get a section name from a section definition.
00781  *
00782  * @param token_start The beginning of the token.
00783  * @param buffer Buffer to put the section name into.
00784  * 
00785  * @return An error code.
00786  */
00787 static enum parse_error ParseSection(const char *token_start,
00788                                      char **buffer)
00789 {
00790   enum parse_error error;
00791   const char *token_end;
00792   int i;
00793 
00794   error = parse_success;
00795 
00796   /* Remove leading whitespace */
00797   for(;
00798       isspace(*token_start);
00799       token_start++)
00800   {
00801     /* Do nothing */
00802   }
00803 
00804   for(token_end = token_start;
00805       *token_end && *token_end != ']';
00806       token_end++)
00807   {
00808     /* Do nothing */
00809   }
00810 
00811   if(*token_end != ']')
00812   {
00813     error = parse_malformed_line;
00814   }
00815   else
00816   {
00817     token_end--;
00818   }
00819 
00820   if(error == parse_success)
00821   {
00822     if(token_end - token_start > 0)
00823     {
00824       *buffer = (char *)malloc(token_end - token_start+2);
00825       
00826       if(*buffer)
00827       {
00828         for(i=0; i <= token_end - token_start; i++)
00829       {
00830         (*buffer)[i] = token_start[i];
00831       }
00832         (*buffer)[i] = 0;
00833       }
00834       else
00835       {
00836         error = parse_memory_failure;
00837       }
00838     }
00839     else
00840     {
00841       error = parse_empty_token;
00842     }    
00843   }
00844 
00845   /* FIXME: ignoring everything else on line.
00846    * Should really check for extraneous chars.
00847    */
00848 
00849   return error;
00850 }
00851