From 848118b1f743ca22c113700dab4e046f5fabf851 Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Tue, 31 Jan 2012 13:01:14 +0400 Subject: [PATCH] Qsub: fix -W regression This patch restores the ability of qsub to handle -W arguments like 'name=value1,value2,value3' that are effectively equal to '-W name=value1 -W name=value2 -W name=value3'. Signed-off-by: Eygene Ryabinkin --- src/cmds/qsub.c | 428 +++++++++++++++++++++---------------------------------- 1 files changed, 160 insertions(+), 268 deletions(-) diff --git a/src/cmds/qsub.c b/src/cmds/qsub.c index d6b0e02..77b3883 100644 --- a/src/cmds/qsub.c +++ b/src/cmds/qsub.c @@ -148,6 +148,17 @@ static char *DefaultXauthPath = XAUTH_PATH; #define MAX_PROCS_DIGITS 15 /* A 15 digit number is a lot of processors. 100 trillion will this be enough for the future? */ + +/* Session descriptor for get_name_value() */ +struct parse_name_value { + char *name; /* Last name that was processed */ + char *buffer; /* Buffer with input string */ + char *curpos; /* Current position inside buffer */ + unsigned char flags; /* Flags for the session */ +}; +#define PNV_BADSYNTAX 0x01 + + static char PBS_DPREFIX_DEFAULT[] = "#PBS"; char PBS_Filter[256]; @@ -335,311 +346,169 @@ static char *x11_get_proto( - -char *smart_strtok( - - char *line, /* I */ - char *delims, /* I */ - char **ptrPtr, /* O */ - int ign_backslash) /* I */ - +/* + * Strips trailing blank characters. + */ +static void chomp_blanks(s) +char *s; { - char *head = NULL; - char *start = NULL; + size_t len; - int dindex; - int ignchar; - int ignore; + for (len = strlen(s) - 1; len >= 0 && isspace(s[len]); len--); + s[len + 1] = '\0'; + } /* END chomp_blanks() */ - int sq_count = 0; - int dq_count = 0; - int sb_count = 0; - char *tmpLine = NULL; - int tmpLineSize; - int tindex; - char *ptr; +/* + * Parses argument lists like 'name=value[,value,value],name=value'. + * The first form, with many values for a single name will be + * permitted only if 'name' is found in the array multiarg_OK. + * + * When 'start' is non-NULL this means that a new parsing session + * with the buffer 'start' is pointing to will be created. + * + * Any subsequent calls to this function with 'start' being NULL + * means "continue the parsing from the last position". + * + * Return values: + * 1: new name/value pair was parsed; + * 0: end of string reached, nothing new was parsed; + * -1: memory allocation error (won't happen when start == NULL); + * -2: bad line contents. + */ +int get_name_value(session, start, name, value, multiarg_OK) +struct parse_name_value *session; +char *start; +char **name; +char **value; +const char *multiarg_OK[]; + { + char *p; - if (ptrPtr == NULL) - { - /* FAILURE */ + assert(session != NULL); + assert(name != NULL); + assert(value != NULL); - return(head); - } - else if (line != NULL) + if (start != NULL) { - *ptrPtr = line; + size_t len = strlen(start); + + bzero((void *)session, sizeof(*session)); + session->buffer = (char *)malloc(len + 1); + if (session->buffer == NULL) + return (-1); + strncpy(session->buffer, start, len); + session->buffer[len] = '\0'; + session->curpos = session->buffer; } - else if (*ptrPtr == NULL) + else { - /* FAILURE */ - - return(head); + assert(session->buffer != NULL); + assert(session->curpos >= session->curpos); + if (session->flags & PNV_BADSYNTAX) + return (-2); } - start = *ptrPtr; - - tmpLineSize = (line == NULL) ? strlen(*ptrPtr) + 1 : strlen(line) + 1; - tmpLine = (char *)malloc(tmpLineSize * sizeof(char)); - - tmpLine[0] = '\0'; - - tindex = 0; - - ignchar = FALSE; + if (session->curpos[0] == '\0') + return (0); - ptr = *ptrPtr; + while (isspace(session->curpos[0])) + session->curpos++; - while (*ptr != '\0') + /* Look for the name */ + for (p = session->curpos; *p != '='; p++) { - if (*ptr == '\'') + if (*p == '\0' || *p == ',') { - sq_count++; + unsigned char permit_multi = 0; + size_t i; - if ((head != NULL) && !(sq_count % 2) && !(dq_count % 2)) + if (session->name != NULL) { - ptr++; - - ignchar = TRUE; - } - else - { - ignore = TRUE; - - if (ign_backslash == TRUE) - { - /* check if backslash precedes delimiter */ - - if ((ptr > start) && (*(ptr-1) == '\\')) + /* + * Reuse the last seen name (for second and successive 'value' + * in 'name=value[,value,value,...]') if it is permitted + * by multiarg_OK. + */ + for (i = 0; multiarg_OK[i] != NULL; i++) + if (strcmp(session->name, multiarg_OK[i]) == 0) { - /* check if backslash is backslashed */ - - if ((ptr > start + 1) && (*(ptr-2) != '\\')) - { - /* delimiter is backslashed, ignore */ - - ignore = FALSE; - - sq_count--; - } + permit_multi = 1; + break; } - } - - if (ignore == TRUE) - { - ptr++; - - ignchar = TRUE; - } } - } - else if (*ptr == '\"') - { - dq_count++; - if ((head != NULL) && !(sq_count % 2) && !(dq_count % 2)) + if (permit_multi) { - ptr++; - - ignchar = TRUE; - } - else - { - ignore = TRUE; - - if (ign_backslash == TRUE) + *name = session->name; + *value = session->curpos; + if (*p != '\0') { - /* check if backslash precedes delimiter */ - - if ((ptr > start) && (*(ptr-1) == '\\')) - { - /* check if backslash is backslashed */ - - if ((ptr > start + 1) && (*(ptr-2) != '\\')) - { - /* delimiter is backslashed, ignore */ - - ignore = FALSE; - - dq_count--; - } - } + *p = '\0'; + session->curpos = p + 1; } + else + session->curpos = p; - if (ignore == TRUE) - { - ptr++; - - ignchar = TRUE; - } + goto _try_to_return_result; } - } - else if (*ptr == '[' ) - { - sb_count = 1; - } - else if (*ptr == ']') - { - sb_count = 0; - } - else if (*ptr == '{') - { - sb_count = 1; - } - else if (*ptr == '}') - { - sb_count = 0; - } - else if (!(sq_count % 2) && !(dq_count % 2) && (sb_count == 0)) - { - /* not in quotations, locate delimiter */ - - for (dindex = 0; delims[dindex] != '\0'; dindex++) - { - if (*ptr != delims[dindex]) - continue; - if ((ign_backslash == TRUE) && (head != NULL)) - { - /* check if backslash precedes delimiter */ - if ((ptr > head) && (*(ptr-1) == '\\')) - { - /* check if backslash is backslashed */ - - if ((ptr > head + 1) && (*(ptr-1) != '\\')) - { - /* delimiter is backslashed, ignore */ - - continue; - } - } - } - - /* delimiter found */ - - *ptr = '\0'; - - ptr++; - - if (head != NULL) - { - *ptrPtr = ptr; - - tmpLine[tindex] = '\0'; - - if (tindex > 0) - strcpy(head,tmpLine); - - free(tmpLine); - - return(head); - } - - ignchar = TRUE; - - break; - } /* END for (dindex) */ - } - - if ((ignchar != TRUE) && (*ptr != '\0')) - { - if (head == NULL) - head = ptr; - - tmpLine[tindex++] = ptr[0]; - - ptr++; + session->flags |= PNV_BADSYNTAX; + return (-2); } + } - ignchar = FALSE; - } /* END while (*ptr != '\0') */ - - tmpLine[tindex] = '\0'; - - if (tindex > 0) - strcpy(head,tmpLine); - - free(tmpLine); - - *ptrPtr = ptr; - - return(head); - } /* END smart_strtok */ - - - + /* Cache current name and skip blanks */ + *p = '\0'; + session->name = session->curpos; + p++; + while (isspace(*p)) + p++; + session->curpos = p; + /* Look for the value */ + for (; *p != '\0' && *p != ','; p++); -int get_name_value(start, name, value) -char *start; -char **name; -char **value; - { - static char *tok_ptr; - char *curr_ptr; - char *equals; - static char tmpLine[65536]; - char *s; - - /* we've reached the end */ - if ((start == NULL) && - (*tok_ptr == '\0')) - return(0); - - s = NULL; - /* XXX: may truncate input */ - if (start != NULL) + *name = session->name; + *value = session->curpos; + if (*p != '\0') { - strncpy(tmpLine, start, sizeof(tmpLine)); - tmpLine[sizeof(tmpLine) - 1] = '\0'; - s = tmpLine; + *p = '\0'; + session->curpos = p + 1; } + else + session->curpos = p; - curr_ptr = smart_strtok(s,",",&tok_ptr,FALSE); - - if ((curr_ptr == NULL)) - return(0); - - if ((*curr_ptr == '=') || - (*curr_ptr == '\0')) +_try_to_return_result: + /* + * Almost done, need to strip trailing blanks + * and check empty name/value. + */ + chomp_blanks(*name); + chomp_blanks(*value); + if (**name == '\0' || **value == '\0') { - /* no name, fail */ - return(-1); + session->flags |= PNV_BADSYNTAX; + return (-2); } - /* skip leading spaces */ - while (isspace((int)*curr_ptr) && (*curr_ptr)) - curr_ptr++; - - *name = curr_ptr; - - equals = *name; - - /* skip over name */ - while ((*equals) && (!isspace((int)*equals)) && (*equals != '=')) - equals++; - - /* strip blanks */ - while ((*equals) && (isspace((int)*equals))) - *equals++ = '\0'; - - if (*equals != '=') - return (-1); /* should have found a = as first non blank */ - - *equals++ = '\0'; - - /* skip leading white space */ - while (isspace((int)*equals) && *equals) - equals++; + return (1); + } /* END get_name_value() */ - if (*equals == '\0') - return(-1); - *value = equals; +/* + * Frees memory associated with name/value parsing session. + */ +void free_name_value_session(session) +struct parse_name_value *session; + { + assert(session != NULL); - return (1); - } + if (session->buffer != NULL) + free((void *)session->buffer); + bzero((void *)session, sizeof(*session)); + } /* END free_name_value_session */ @@ -2948,6 +2817,18 @@ int process_opts( int nitems; char search_string[256]; + struct parse_name_value gnv_session; + + /* + * Array of directives for -W for which we permit + * multivalued specifications like name=value1,value2,value3. + * Must be NULL-terminated. + */ + static const char *optW_multiarg[] = { + "stagein", "stageout", + NULL + }; + #if !defined(PBS_NO_POSIX_VIOLATION) #define GETOPT_ARGS "a:A:b:c:C:d:D:e:fF:hIJ:j:k:l:m:M:N:o:p:P:q:r:S:t:T:u:v:Vw:W:Xxz-:" #else @@ -3933,7 +3814,8 @@ int process_opts( break; } - i = get_name_value(optarg, &keyword, &valuewd); + i = get_name_value(&gnv_session, optarg, + &keyword, &valuewd, optW_multiarg); if (i != 1) { @@ -3944,7 +3826,8 @@ int process_opts( snprintf(tmpLine, sizeof(tmpLine), "x=%s", optarg); - i = get_name_value(tmpLine, &keyword, &valuewd); + i = get_name_value(&gnv_session, tmpLine, + &keyword, &valuewd, optW_multiarg); } while (i == 1) @@ -4043,10 +4043,11 @@ set_attr(&attrib, keyword, valuewd); } - i = get_name_value(NULL, &keyword, &valuewd); + i = get_name_value(&gnv_session, NULL, + &keyword, &valuewd, optW_multiarg); } /* END while (i == 1) */ - if (i == -1) + if (i == -2) { fprintf(stderr, "qsub: illegal -W value '%s' " "for keyword '%s'\n", valuewd, keyword); @@ -4054,6 +4055,13 @@ errflg++; } + if (i == -1) + { + fprintf(stderr, "qsub: not enough memory to parse -W value\n"); + + errflg++; + } + break; #if !defined(PBS_NO_POSIX_VIOLATION)