Logo Search packages:      
Sourcecode: condor version File versions  Download package

config.cpp

/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

 

#include "condor_common.h"
#include "condor_debug.h"
#include "condor_config.h"
#include "condor_string.h"
#include "extra_param_info.h"
#include "condor_random_num.h"
#include "condor_uid.h"
#include "my_popen.h"

#if defined(__cplusplus)
extern "C" {
#endif

static char *getline_implementation( FILE *, int );

int         ConfigLineNo;

/* WARNING: When we mean alphanumeric in this snippet of code, we really mean 
      characters that are legal in a C indentifier plus period and forward slash.
      It looks like what character set is allowable to be in the default value
      of the $$ expansion hasn't been thought about very well....

      XXX: If you've come here looking to add \ so windows paths may be
      substituted as the default value in a $$ expansion with a default
      value, be very careful. The $$() expansion algorithm is deep in
      the parsing of the RHS of the attr/value pair, and at the writing
      of this comment, it is unknown if \ substitution would happen
      before/during/after the $$ expansion would happen, in which case
      you'd be screwed and have to understand/alter much more code.
      If you perform these code alterings to support \ in this manner,
      then remove this XXX comment.
*/
int
condor_isidchar(int c)
{
      if( ('a' <= c && c <= 'z') ||
            ('A' <= c && c <= 'Z') ||
            ('0' <= c && c <= '9') ||
            /* See the above comment for this function and about the next line. */
            (strchr("_./", c) != NULL) )
      {
            return 1;
      } 

      return 0;
}

#define ISIDCHAR(c)           ( condor_isidchar(c) )

// $$ expressions may also contain a colon
#define ISDDCHAR(c) ( ISIDCHAR(c) || ((c)==':') )

#define ISOP(c)         (((c) == '=') || ((c) == ':'))

// Magic macro to represent a dollar sign, i.e. $(DOLLAR)="$"
#define DOLLAR_ID "DOLLAR"

bool 
is_piped_command(const char* filename)
{
      bool retVal = false;

      char const *pdest = strchr( filename, '|' );
      if ( pdest != NULL ) {
            // This is not a filename (still not sure it's a valid command though)
            retVal = true;
      }

      return retVal;
}


// Make sure the last character is the '|' char.  For now, that's our only criteria.
bool 
is_valid_command(const char* cmdToExecute)
{
      bool retVal = false;

      int cmdStrLength = strlen(cmdToExecute);
      if ( cmdToExecute[cmdStrLength - 1] == '|' ) {
            retVal = true;
      }

      return retVal;
}


int
Read_config( const char* config_source, BUCKET** table, 
                   int table_size, int expand_flag,
                   bool check_runtime_security,
                   ExtraParamTable *extra_info)
{
      FILE* conf_fp = NULL;
      char* name = NULL;
      char* value = NULL;
      char* rhs = NULL;
      char* ptr = NULL;
      char  op;
      int         retval = 0;
      bool  is_pipe_cmd = false;
      bool  firstRead = true;

      ConfigLineNo = 0;
   
      // Determine if the config file name specifies a file to open, or a
      // pipe to suck on. Process each accordingly
      if ( is_piped_command(config_source) ) {
            is_pipe_cmd = true;
            if ( is_valid_command(config_source) ) {
                  // try to run the cmd specified before the '|' symbol, and
                  // get the configuration from it's output.
                  char *cmdToExecute = strdup( config_source );
                  cmdToExecute[strlen(cmdToExecute)-1] = '\0';

                  ArgList argList;
                  MyString args_errors;
                  argList.AppendArgsV1RawOrV2Quoted(cmdToExecute, &args_errors);
                  conf_fp = my_popen(argList, "r", FALSE);
                  if( conf_fp == NULL ) {
                        printf("Can't open cmd %s\n", cmdToExecute);
                        free( cmdToExecute );
                        return -1;
                  }
                  free( cmdToExecute );
            } else {
                  printf("Specified cmd, %s, not a valid command to execute.  It must have a '|' "
                           "character at the end.\n", config_source);
                  return( -1 );
            }
      } else {
            is_pipe_cmd = false;
            conf_fp = safe_fopen_wrapper(config_source, "r");
            if( conf_fp == NULL ) {
                  printf("Can't open file %s\n", config_source);
                  return( -1 );
            }
      }

      if( check_runtime_security ) {
#ifndef WIN32
                  // unfortunately, none of this works on windoze... (yet)
            if ( is_pipe_cmd ) {
                  fprintf( stderr, "Configuration Error File <%s>: runtime config "
                               "not allowed to come from a pipe command\n",
                               config_source );
                  retval = -1;
                  goto cleanup;
            }
            int fd = fileno(conf_fp);
            struct stat statbuf;
            uid_t f_uid;
            int rval = fstat( fd, &statbuf );
            if( rval < 0 ) {
                  fprintf( stderr, "Configuration Error File <%s>, fstat() failed: %s (errno: %d)\n",
                               config_source, strerror(errno), errno );
                  retval = -1;
                  goto cleanup;
            }
            f_uid = statbuf.st_uid;
            if( can_switch_ids() ) {
                        // if we can switch, the file *must* be owned by root
                  if( f_uid != 0 ) {
                        fprintf( stderr, "Configuration Error File <%s>, "
                                     "running as root yet runtime config file owned "
                                     "by uid %d, not 0!\n", config_source, (int)f_uid );
                        retval = -1;
                        goto cleanup;
                  }
            } else {
                        // if we can't switch, at least ensure we own the file
                  if( f_uid != get_my_uid() ) {
                        fprintf( stderr, "Configuration Error File <%s>, "
                                     "running as uid %d yet runtime config file owned "
                                     "by uid %d!\n", config_source, (int)get_my_uid(),
                                     (int)f_uid );
                        retval = -1;
                        goto cleanup;
                  }
            }
#endif /* ! WIN32 */
      } // if( check_runtime_security )

      while(true) {
            name = getline_implementation(conf_fp, 128);
            // If the file is empty the first time through, warn the user.
            if (name == NULL) {
                  if (firstRead) {
                        dprintf(D_FULLDEBUG, "WARNING: Config source is empty: %s\n",
                                    config_source);
                  }
                  break;
            }
            firstRead = false;
            
            /* Skip over comments */
            if( *name == '#' || blankline(name) )
                  continue;

            ptr = name;

            // Assumption is that the line starts with a non whitespace
            // character
            // Example :
            // OP_SYS=SunOS
            while( *ptr ) {
                  if( isspace(*ptr) || ISOP(*ptr) ) {
                    /* *ptr is now whitespace or '=' or ':' */
                    break;
                  } else {
                    ptr++;
                  }
            }

            if( !*ptr ) {
                        // Here we have determined this line has no operator
                  if ( name && name[0] && name[0] == '[' ) {
                        // Treat a line w/o an operator that begins w/ a square bracket
                        // as a comment so a config file can look like
                        // a Win32 .ini file for MS Installer purposes.       
                        continue;
                  } else {
                        // No operator and no square bracket... bail.
                        retval = -1;
                        goto cleanup;
                  }
            }

            if( ISOP(*ptr) ) {
                  op = *ptr;
                  //op is now '=' in the above eg
                  *ptr++ = '\0';
                  // name is now 'OpSys' in the above eg
            } else {
                  *ptr++ = '\0';
                  while( *ptr && !ISOP(*ptr) ) {
                        ptr++;
                  }

                  if( !*ptr ) {
                        retval = -1;
                        goto cleanup;
                  }

                  op = *ptr++;
            }

            /* Skip to next non-space character */
            while( *ptr && isspace(*ptr) ) {
                  ptr++;
            }

            rhs = ptr;
            // rhs is now 'SunOS' in the above eg

            
            /* Expand references to other parameters */
            name = expand_macro( name, table, table_size );
            if( name == NULL ) {
                  retval = -1;
                  goto cleanup;
            }

            /* Check that "name" is a legal identifier : only
               alphanumeric characters and _ allowed*/
            ptr = name;
            while( *ptr ) {
                  char c = *ptr++;
                  if( !ISIDCHAR(c) ) {
                        fprintf( stderr,
            "Configuration Error File <%s>, Line %d: Illegal Identifier: <%s>\n",
                              config_source, ConfigLineNo, name );
                        retval = -1;
                        goto cleanup;
                  }
            }

            if( expand_flag == EXPAND_IMMEDIATE ) {
                  value = expand_macro( rhs, table, table_size );
                  if( value == NULL ) {
                        retval = -1;
                        goto cleanup;
                  }
            } else  {
                  /* expand self references only */
                  value = expand_macro( rhs, table, table_size, name );
                  if( value == NULL ) {
                        retval = -1;
                        goto cleanup;
                  }
            }  

            if( op == ':' || op == '=' ) {
                        /*
                          Yee haw!!! We can treat "expressions" and "macros"
                          the same way: just insert them into the config hash
                          table.  Everything now behaves like macros used to
                          Derek Wright <wright@cs.wisc.edu> 4/11/00
                        */
                  strlwr( name );

                  /* Put the value in the Configuration Table */
                  insert( name, value, table, table_size );
                  if (extra_info != NULL) {
                        extra_info->AddFileParam(name, config_source, ConfigLineNo);
                  }
            } else {
                  fprintf( stderr,
                        "Configuration Error File <%s>, Line %d: Syntax Error\n",
                        config_source, ConfigLineNo );
                  retval = -1;
                  goto cleanup;
            }

            FREE( name );
            name = NULL;
            FREE( value );
            value = NULL;
      }

 cleanup:
      if ( conf_fp ) {
            if ( is_pipe_cmd ) {
                  int exit_code = my_pclose( conf_fp );
                  if ( retval == 0 && exit_code != 0 ) {
                        fprintf( stderr, "Configuration Error File <%s>: "
                                     "command terminated with exit code %d\n",
                                     config_source, exit_code );
                        retval = -1;
                  }
            } else {
                  fclose( conf_fp );
            }
      }
      if(name) {
            FREE( name );
      }
      if(value) {
            FREE( value );
      }
      return retval;
}


/*
** Just compute a hash value for the given string such that
** 0 <= value < size 
*/
int
condor_hash( register char *string, register int size )
{
      register unsigned int         answer;

      answer = 1;

      for( ; *string; string++ ) {
            answer <<= 1;
            answer += (int)*string;
      }
      answer >>= 1;     /* Make sure not negative */
      answer %= size;
      return answer;
}

/*
** Insert the parameter name and value into the given hash table.
*/
void
insert( const char *name, const char *value, BUCKET **table, int table_size )
{
      register BUCKET   *ptr;
      int         loc;
      BUCKET      *bucket;
      char  tmp_name[ 1024 ];

            /* Make sure not already in hash table */
      strcpy( tmp_name, name );
      strlwr( tmp_name );
      loc = condor_hash( tmp_name, table_size );
      for( ptr=table[loc]; ptr; ptr=ptr->next ) {
            if( strcmp(tmp_name,ptr->name) == 0 ) {
                  FREE( ptr->value );
                  ptr->value = strdup( value );
                  return;
            }
      }

            /* Insert it */
      bucket = (BUCKET *)MALLOC( sizeof(BUCKET) );
      bucket->name = strdup( tmp_name );
      bucket->value = strdup( value );
      bucket->used = 0;
      bucket->next = table[ loc ];
      table[loc] = bucket;
}

/*
** Sets the given macro's state to used
*/
void 
set_macro_used ( const char *name, int used, BUCKET *table[], int table_size )
{
      register BUCKET   *ptr;
      int         loc;
      char  tmp_name[ 1024 ];
      
      /* find the macro in the hash table */
      strcpy( tmp_name, name );
      strlwr( tmp_name );
      loc = condor_hash( tmp_name, table_size );
      for( ptr=table[loc]; ptr; ptr=ptr->next ) {
            if( strcmp(tmp_name,ptr->name) == 0 ) {
                  ptr->used = used;
                  return;
            }
      }
}

/*
** Read one line and any continuation lines that go with it.  Lines ending
** with <white space><backslash> are continued onto the next line.
** Lines can be of any lengh.  We pass back a pointer to a buffer; do _not_
** free this memory.  It will get freed the next time getline() is called (this
** function used to contain a fixed-size static buffer).
*/
char *
getline( FILE *fp )
{
      return getline_implementation(fp,_POSIX_ARG_MAX);
}
static char *
getline_implementation( FILE *fp, int requested_bufsize )
{
      static char *buf = NULL;
      static unsigned int buflen = 0;
      char  *read_ptr;        // Pointer to read into next read
      char  *parse_ptr;       // Pointer to where to start parsing from

      if( feof(fp) ) {
                  // We're at the end of the file, clean up our buffers and
                  // return NULL.  
            if ( buf ) {
                  free(buf);
                  buf = NULL;
                  buflen = 0;
            }
            return NULL;
      }

      if ( buflen != (unsigned int)requested_bufsize ) {
            if ( buf ) free(buf);
            buf = (char *)malloc(requested_bufsize);
            buflen = requested_bufsize;
      }
      buf[0] = '\0';
      read_ptr = buf;
      parse_ptr = buf;

      // Loop 'til we're done reading a whole line, including continutations
      for(;;) {
            int         len = buflen - (read_ptr - buf);
            if( len <= 5 ) {
                  // we need a larger buffer -- grow buffer by 4kbytes
                  char *newbuf = (char *)realloc(buf, 4096 + buflen);
                  if ( newbuf ) {
                        read_ptr = (read_ptr - buf) + newbuf;
                        parse_ptr = (parse_ptr - buf) + newbuf;
                        buf = newbuf;     // note: realloc() freed our old buf if needed
                        buflen += 4096;
                        len += 4096;
                  } else {
                        // malloc returned NULL, we're out of memory
                        EXCEPT( "Out of memory - config file line too long" );
                  }
            }

            if( fgets(read_ptr,len,fp) == NULL ) {
                  if( buf[0] == '\0' ) {
                        return NULL;
                  } else {
                        return buf;
                  }
            }

            // See if fgets read an entire line, or simply ran out of buffer space
            if ( *read_ptr == '\0' ) {
                  continue;
            }
            if( strrchr(read_ptr, '\n') == NULL ) {
                  // if we made it here, fgets() ran out of buffer space.
                  // move our read_ptr pointer forward so we concatenate the
                  // rest on after we realloc our buffer above.
                  read_ptr += strlen(read_ptr);
                  continue;   // since we are not finished reading this line
            }

            ConfigLineNo++;

                  /* See if a continuation is indicated */
            char  *ptr;             // Temp pointer
            ptr = ltrunc( parse_ptr );
            if( ptr != parse_ptr ) {
                  (void)memmove( parse_ptr, ptr, strlen(ptr)+1 );
            }

            ptr = (char *) strrchr( parse_ptr, '\\' );
            if( ptr == NULL )
                  return buf;
            if( *(ptr+1) != '\0' )
                  return buf;

                  /* Ok read the continuation and concatenate it on */
            parse_ptr = read_ptr = ptr;
      }
}

/* 
** Utility function to get an integer from a string.
** Returns: -1 if input is NULL, -2 if input is invalid, 0 for success
*/
int
string_to_long( const char *s, long *valuep )
{
      // Verify that we have a valid pointer
      if ( !s ) {
            return -1;
      }

      // Call strtol(), verify that it liked the input
      char  *endp;
      long  value = strtol( s, &endp, 10 );
      if ( (const char *)endp == s ) {
            return -2;
      }

      // Done, get out of here
      *valuep = value;
      return 0;
}

/*
** Expand parameter references of the form "left$(middle)right".  This
** is deceptively simple, but does handle multiple and or nested references.
** If self is not NULL, then we only expand references to to the parameter
** specified by self.  This is used when we want to expand self-references
** only.
** Also expand references of the form "left$ENV(middle)right",
** replacing $ENV(middle) with getenv(middle).
** Also expand references of the form "left$RANDOM_CHOICE(middle)right".
*/
char *
expand_macro( const char *value, BUCKET **table, int table_size, char *self )
{
      char *tmp = strdup( value );
      char *left, *name, *tvalue, *right;
      char *rval;

      bool all_done = false;
      while( !all_done ) {          // loop until all done expanding
            all_done = true;

            if( !self && get_special_var("$ENV",true,tmp, &left, &name, &right) ) 
            {
                  all_done = false;
                  tvalue = getenv(name);
                  if( tvalue == NULL ) {
                        EXCEPT("Can't find %s in environment!",name);
                  }

                  rval = (char *)MALLOC( (unsigned)(strlen(left) + strlen(tvalue) +
                                                                    strlen(right) + 1));
                  (void)sprintf( rval, "%s%s%s", left, tvalue, right );
                  FREE( tmp );
                  tmp = rval;
            }

            if( !self && get_special_var("$RANDOM_CHOICE",false,tmp, &left, &name, 
                  &right) ) 
            {
                  all_done = false;
                  StringList entries(name,",");
                  int num_entries = entries.number();
                  tvalue = NULL;
                  if ( num_entries > 0 ) {
                        int rand_entry = (get_random_int() % num_entries) + 1;
                        int i = 0;
                        entries.rewind();
                        while ( (i < rand_entry) && (tvalue=entries.next()) ) {
                              i++;
                        }
                  }
                  if( tvalue == NULL ) {
                        EXCEPT("$RANDOM_CHOICE() macro in config file empty!" );
                  }

                  rval = (char *)MALLOC( (unsigned)(strlen(left) + strlen(tvalue) +
                                                                    strlen(right) + 1));
                  (void)sprintf( rval, "%s%s%s", left, tvalue, right );
                  FREE( tmp );
                  tmp = rval;
            }

            if( !self && get_special_var("$RANDOM_INTEGER",false,tmp, &left, &name, 
                  &right) ) 
            {
                  all_done = false;
                  StringList entries(name, ",");

                  entries.rewind();
                  const char *tmp2;

                  tmp2 = entries.next();
                  long  min_value;
                  if ( string_to_long( tmp2, &min_value ) < 0 ) {
                        EXCEPT( "$RANDOM_INTEGER() config macro: invalid min!" );
                  }

                  tmp2 = entries.next();
                  long  max_value;
                  if ( string_to_long( tmp2, &max_value ) < 0 ) {
                        EXCEPT( "$RANDOM_INTEGER() config macro: invalid max!" );
                  }

                  tmp2 = entries.next();
                  long  step = 1;
                  if ( string_to_long( tmp2, &step ) < -1 ) {
                        EXCEPT( "$RANDOM_INTEGER() config macro: invalid step!" );
                  }

                  if ( step < 1 ) {
                        EXCEPT( "$RANDOM_INTEGER() config macro: invalid step!" );
                  }
                  if ( min_value > max_value ) {
                        EXCEPT( "$RANDOM_INTEGER() config macro: min > max!" );
                  }

                  // Generate the random value
                  long  range = step + max_value - min_value;
                  long  num = range / step;
                  long  random_value =
                        min_value + (get_random_int() % num) * step;

                  // And, convert it to a string
                  char  buf[128];
                  snprintf( buf, sizeof(buf)-1, "%ld", random_value );
                  buf[sizeof(buf)-1] = '\0';
                  rval = (char *)MALLOC( (unsigned)(strlen(left) + strlen(buf) +
                                                                    strlen(right) + 1));
                  (void)sprintf( rval, "%s%s%s", left, buf, right );
                  FREE( tmp );
                  tmp = rval;
            }

            if( get_var(tmp, &left, &name, &right, self) ) {
                  all_done = false;
                  tvalue = lookup_macro( name, table, table_size );
                  if( tvalue == NULL ) {
                        tvalue = "";
                  }

                  rval = (char *)MALLOC( (unsigned)(strlen(left) + strlen(tvalue) +
                                                                    strlen(right) + 1));
                  (void)sprintf( rval, "%s%s%s", left, tvalue, right );
                  FREE( tmp );
                  tmp = rval;
            }
      }

      // Now, deal with the special $(DOLLAR) macro.
      if (!self)
      while( get_var(tmp, &left, &name, &right, DOLLAR_ID) ) {
            rval = (char *)MALLOC( (unsigned)(strlen(left) + 1 +
                                                              strlen(right) + 1));
            (void)sprintf( rval, "%s$%s", left, right );
            FREE( tmp );
            tmp = rval;
      }

      return( tmp );
}

// Local helper only.  If iter's current pointer is
// null, move along to the beginning of the next non-empty 
// bucket.  If there are no more non-empty buckets, leave
// it set to null and advance iter's index past the end (to
// make it clear we're done)
static void
hash_iter_scoot_to_next_bucket(HASHITER p)
{
      while(p->current == NULL) {
            (p->index)++;
            if(p->index >= p->table_size) {
                  // The hash table is empty
                  return;
            }
            p->current = p->table[p->index];
      }
}

HASHITER 
hash_iter_begin(BUCKET ** table, int table_size)
{
      ASSERT(table != NULL);
      ASSERT(table_size > 0);
      hash_iter * p = (hash_iter *)MALLOC(sizeof(hash_iter));
      p->table = table;
      p->table_size = table_size;
      p->index = 0;
      p->current = p->table[p->index];
      hash_iter_scoot_to_next_bucket(p);
      return p;
}

int
hash_iter_done(HASHITER iter)
{
      ASSERT(iter);
      ASSERT(iter->table); // Probably trying to use a iter already hash_iter_delete()d
      return iter->current == NULL;
}

int 
hash_iter_next(HASHITER iter)
{
      ASSERT(iter);
      ASSERT(iter->table); // Probably trying to use a iter already hash_iter_delete()d
      if(hash_iter_done(iter)) {
            return 0;
      }
      iter->current = iter->current->next;
      hash_iter_scoot_to_next_bucket(iter);
      return (iter->current) ? 1 : 0;
}

char * 
hash_iter_key(HASHITER iter)
{
      ASSERT(iter);
      ASSERT(iter->table); // Probably trying to use a iter already hash_iter_delete()d
      ASSERT( ! hash_iter_done(iter) );
      return iter->current->name;
}

char * 
hash_iter_value(HASHITER iter)
{
      ASSERT(iter);
      ASSERT(iter->table); // Probably trying to use a iter already hash_iter_delete()d
      ASSERT( ! hash_iter_done(iter) );
      return iter->current->value;
}

int
hash_iter_used_value(HASHITER iter)
{
      ASSERT(iter);
      ASSERT(iter->table); // Probably trying to use a iter already hash_iter_delete()d
      ASSERT( ! hash_iter_done(iter) );
      return iter->current->used;
}

void 
hash_iter_delete(HASHITER * iter)
{
      ASSERT(iter);
      ASSERT(iter[0]);
      ASSERT(iter[0]->table); // Probably trying to use a iter already hash_iter_delete()d
      iter[0]->table = NULL;
      free(*iter);
      *iter = 0;
}


/*
** Same as get_var() below, but finds special references like $ENV().
*/
int
get_special_var( const char *prefix, bool only_id_chars, register char *value, 
            register char **leftp, register char **namep, register char **rightp )
{
      char *left, *left_end, *name, *right;
      char *tvalue;
      int prefix_len;

      if ( prefix == NULL ) {
            return( 0 );
      }

      prefix_len = strlen(prefix);
      tvalue = value;
      left = value;

      for(;;) {
tryagain:
            if (tvalue) {
                  value = (char *)strstr( (const char *)tvalue, prefix );
            }
            
            if( value == NULL ) {
                  return( 0 );
            }

            value += prefix_len;
            if( *value == '(' ) {
                  left_end = value - prefix_len;
                  name = ++value;
                  while( *value && *value != ')' ) {
                        char c = *value++;
                        if( !ISIDCHAR(c) && only_id_chars ) {
                              tvalue = name;
                              goto tryagain;
                        }
                  }

                  if( *value == ')' ) {
                        right = value;
                        break;
                  } else {
                        tvalue = name;
                        goto tryagain;
                  }
            } else {
                  tvalue = value;
                  goto tryagain;
            }
      }

      *left_end = '\0';
      *right++ = '\0';

      *leftp = left;
      *namep = name;
      *rightp = right;

      return( 1 );
}

/*
** If self is not NULL, then only look for the parameter specified by self.
*/
int
get_var( register char *value, register char **leftp, 
             register char **namep, register char **rightp, char *self,
             bool getdollardollar, int search_pos)
{
      char *left, *left_end, *name, *right;
      char *tvalue;
      int selflen = (self) ? strlen(self) : 0;

      tvalue = value + search_pos;
      left = value;

      for(;;) {
tryagain:
            if (tvalue) {
                  value = (char *)strchr( (const char *)tvalue, '$' );
            }
            
            if( value == NULL ) {
                  return( 0 );
            }

                  // Do not treat $$(foo) as a macro unless
                  // getdollardollar = true.  This is for
                  // condor_submit, so it does not try to expand
                  // macros when we do "executable = foo.$$(arch)"
                  // If getdollardollar is true, than only get
                  // macros with two dollar signs, i.e. $$(foo).
            if ( getdollardollar ) {
                  if ( *++value != '$' ) {
                        // this is not a $$ macro
                        tvalue = value;
                        goto tryagain;
                  }
            } else {
                  // here getdollardollar is false, so ignore
                  // any $$(foo) macro.
                  if ( (*(value + sizeof(char))) == '$' ) {
                        value++; // skip current $
                        value++; // skip following $
                        tvalue = value;
                        goto tryagain;
                  }
            }

            if( *++value == '(' ) {
                  if( getdollardollar && *value != '\0' && value[1] == '[' ) {

                        // This is a classad expression to be evaled.  This layer
                        // doesn't need any intelligence, just scan for "])" which
                        // terminates it.  If we get to end of string without finding
                        // the terminating pattern, this $$ match fails, try again.

                        char * end_marker = strstr(value, "])");
                        if( end_marker == NULL ) { goto tryagain; }

                        left_end = value - 2;
                        name = ++value;
                        right = end_marker + 1;
                        break;

                  } else { 

                        // This might be a "normal" values $(FOO), $$(FOO) or
                        // $$(FOO:Bar) Skim until ")".  We only allow valid characters
                        // inside (basically alpha-numeric. $$ adds ":"); a non-valid
                        // character means this $$ match fails.  End of string before
                        // ")" means this match fails, try again.

                        if ( getdollardollar ) {
                              left_end = value - 2;
                        } else {
                              left_end = value - 1;
                        }
                        name = ++value;
                        while( *value && *value != ')' ) {
                              char c = *value++;
                              if( getdollardollar ) {
                                    if( !ISDDCHAR(c) ) {
                                          tvalue = name;
                                          goto tryagain;
                                    }
                              } else {
                                    if( !ISIDCHAR(c) ) {
                                          tvalue = name;
                                          goto tryagain;
                                    }
                              }
                        }

                        if( *value == ')' ) {
                              // We pass (value-name) to strncmp since name contains
                              // the entire line starting from the identifier and value
                              // points to the end of the identifier.  Thus, the difference
                              // between these two pointers gives us the length of the
                              // identifier.
                              int namelen = value-name;
                              if( !self || ( namelen == selflen &&
                                                   strincmp( name, self, namelen ) == MATCH ) ) {
                                          // $(DOLLAR) has special meaning; it is
                                          // set to "$" and is _not_ recursively
                                          // expanded.  To implement this, we have
                                          // get_var() ignore $(DOLLAR) and we then
                                          // handle it in expand_macro().
                                          // Note that $$(DOLLARDOLLAR) is handled a little
                                          // differently.  Instead of skipping over it,
                                          // we treat it like anything else, and it is up
                                          // to the caller to advance search_pos, so we
                                          // don't run into the literal $$ again.
                                    if ( !self && strincmp(name,DOLLAR_ID,namelen) == MATCH ) {
                                          tvalue = name;
                                          goto tryagain;
                                    }
                                    right = value;
                                    break;
                              } else {
                                    tvalue = name;
                                    goto tryagain;
                              }
                        } else {
                              tvalue = name;
                              goto tryagain;
                        }
                  }
            } else {
                  tvalue = value;
                  goto tryagain;
            }
      }

      *left_end = '\0';
      *right++ = '\0';

      *leftp = left;
      *namep = name;
      *rightp = right;

      return( 1 );
}

/*
** Return the value associated with the named parameter.  Return NULL
** if the given parameter is not defined.
*/
char *
lookup_macro( const char *name, BUCKET **table, int table_size )
{
      int                     loc;
      register BUCKET   *ptr;
      char              tmp_name[ 1024 ];

      tmp_name[1023] = '\0';
      strncpy( tmp_name, name, 1023 );
      strlwr( tmp_name );
      loc = condor_hash( tmp_name, table_size );
      for( ptr=table[loc]; ptr; ptr=ptr->next ) {
            if( !strcmp(tmp_name,ptr->name) ) {
                  ptr->used = 1;
                  return ptr->value;
            }
      }
      return NULL;
}

#if defined(__cplusplus)
}
#endif

Generated by  Doxygen 1.6.0   Back to index