/* * Copyright © 2001 Novell, Inc. All Rights Reserved. * * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file. * */ /* * FILENAME : NWUtil.c * DESCRIPTION : Utility functions for NetWare implementation of Perl. * Author : HYAK * Date : January 2001. * */ #include "stdio.h" #include "string.h" #include // For "MAX_DN_BYTES" #include // For "malloc" and "free" #include // For "getenv" #include // For "isspace" #include #include #include #include #include #include #include "nwutil.h" #define TRUE 1 #define FALSE 0 /** Global variables used for better token parsing. When these were absent, token parsing was not correct when there were more number of arguments passed. These are used in fnCommandLineParser, fnSkipToken and fnScanToken to get/return the correct and updated pointer to the command line string. **/ char *s1 = NULL; // Used in fnScanToken. char *s2 = NULL; // Used in fnSkipToken. /*============================================================================================ Function : fnSkipWhite Description : This function skips the white space characters in the given string and returns the resultant value. Parameters : s (IN) - Input string. Returns : String. ==============================================================================================*/ char *fnSkipWhite(char *s) { while (isspace(*s)) s++; return s; } /*============================================================================================ Function : fnNwGetEnvironmentStr Description : This function returns the NetWare environment string if available, otherwise returns the supplied default value Parameters : name (IN) - To hold the NetWare environment value. defaultvalue (IN) - Default value. Returns : String. ==============================================================================================*/ char *fnNwGetEnvironmentStr(char *name, char *defaultvalue) { char* ret = getenv(name); if (ret == NULL) ret = defaultvalue; return ret; } /*============================================================================================ Function : fnCommandLineParser Description : This function parses the command line into argc/argv style of Number of params and array of params. Parameters : pclp (IN) - CommandLine structure. commandLine (IN) - CommandLine String. preserverQuotes (IN) - Indicates whether to preserve/copy the quotes or not. Returns : Nothing. ==============================================================================================*/ void fnCommandLineParser(PCOMMANDLINEPARSER pclp, char * commandLine, BOOL preserveQuotes) { char *buffer = NULL; int index = 0; int do_delete = 1; int i=0, j=0, k=0; // +1 makes room for the terminating NULL buffer = (char *) malloc((strlen(commandLine) + 1) * sizeof(char)); if (buffer == NULL) { pclp->m_isValid = FALSE; return; } if (preserveQuotes) { // No I/O redirection nor quote processing if preserveQuotes char *s = NULL; char *sSkippedToken = NULL; strcpy(buffer, commandLine); s = buffer; s = fnSkipWhite(s); // Skip white spaces. s2 = s; // Update the global pointer. pclp->sSkippedToken = (char *) malloc(MAX_DN_BYTES * sizeof(char)); if(pclp->sSkippedToken == NULL) { pclp->m_isValid = FALSE; return; } while (*s && pclp->m_isValid) { /**** // Commented since only one time malloc and free is enough as is done outside this while loop. // It is not required to do them everytime the execution comes into this while loop. // Still retained here. Remove this once things are proved to be working fine to a good confident level, if(pclp->sSkippedToken) { free(pclp->sSkippedToken); pclp->sSkippedToken = NULL; } if(pclp->sSkippedToken == NULL) { pclp->sSkippedToken = (char *) malloc(MAX_DN_BYTES * sizeof(char)); if(pclp->sSkippedToken == NULL) { pclp->m_isValid = FALSE; return; } } ****/ // Empty the string. strncpy(pclp->sSkippedToken, "", (MAX_DN_BYTES * sizeof(char))); // s is advanced by fnSkipToken pclp->sSkippedToken = fnSkipToken(s, pclp->sSkippedToken); // Collect the next command-line argument. s2 = fnSkipWhite(s2); // s2 is already updated by fnSkipToken. s = s2; // Update the local pointer too. fnAppendArgument(pclp, pclp->sSkippedToken); // Append the argument into an array. } if(pclp->sSkippedToken) { free(pclp->sSkippedToken); pclp->sSkippedToken = NULL; } } else { char *s = NULL; strcpy(buffer, commandLine); s = buffer; s = fnSkipWhite(s); s1 = s; // Update the global pointer. while (*s && pclp->m_isValid) { // s is advanced by fnScanToken // Check for I/O redirection here, *outside* of // fnScanToken(), so that quote-protected angle // brackets do NOT cause redirection. if (*s == '<') { s = fnSkipWhite(s+1); // get stdin redirection if(pclp->m_redirInName) { free(pclp->m_redirInName); pclp->m_redirInName = NULL; } if(pclp->m_redirInName == NULL) { pclp->m_redirInName = (char *) malloc(MAX_DN_BYTES * sizeof(char)); if(pclp->m_redirInName == NULL) { pclp->m_isValid = FALSE; return; } } // Collect the next command-line argument. pclp->m_redirInName = fnScanToken(s, pclp->m_redirInName); s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken. s = s1; // Update the local pointer too. } else if (*s == '>') { s = fnSkipWhite(s+1); //get stdout redirection if(pclp->m_redirOutName) { free(pclp->m_redirOutName); pclp->m_redirOutName = NULL; } if(pclp->m_redirOutName == NULL) { pclp->m_redirOutName = (char *) malloc(MAX_DN_BYTES * sizeof(char)); if(pclp->m_redirOutName == NULL) { pclp->m_isValid = FALSE; return; } } // Collect the next command-line argument. pclp->m_redirOutName = fnScanToken(s, pclp->m_redirOutName); s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken. s = s1; // Update the local pointer too. } else if (*s == '2' && s[1] == '>') { s = fnSkipWhite(s+2); // get stderr redirection if(pclp->m_redirErrName) { free(pclp->m_redirErrName); pclp->m_redirErrName = NULL; } if(pclp->m_redirErrName == NULL) { pclp->m_redirErrName = (char *) malloc(MAX_DN_BYTES * sizeof(char)); if(pclp->m_redirErrName == NULL) { pclp->m_isValid = FALSE; return; } } // Collect the next command-line argument. pclp->m_redirErrName = fnScanToken(s, pclp->m_redirErrName); s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken. s = s1; // Update the local pointer too. } else if (*s == '&' && s[1] == '>') { s = fnSkipWhite(s+2); // get stdout+stderr redirection if(pclp->m_redirBothName) { free(pclp->m_redirBothName); pclp->m_redirBothName = NULL; } if(pclp->m_redirBothName == NULL) { pclp->m_redirBothName = (char *) malloc(MAX_DN_BYTES * sizeof(char)); if(pclp->m_redirBothName == NULL) { pclp->m_isValid = FALSE; return; } } // Collect the next command-line argument. pclp->m_redirBothName = fnScanToken(s, pclp->m_redirBothName); s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken. s = s1; // Update the local pointer too. } else { if(pclp->nextarg) { free(pclp->nextarg); pclp->nextarg = NULL; } if(pclp->nextarg == NULL) { pclp->nextarg = (char *) malloc(MAX_DN_BYTES * sizeof(char)); if(pclp->nextarg == NULL) { pclp->m_isValid = FALSE; return; } } // Collect the next command-line argument. pclp->nextarg = fnScanToken(s, pclp->nextarg); s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken. s = s1; // Update the local pointer too. // Append the next command-line argument into an array. fnAppendArgument(pclp, pclp->nextarg); } } } // The -{ option, the --noscreen option, the --autodestroy option, if present, // are processed now and removed from the argument vector. for(index=0; index < pclp->m_argc; ) { // "-q" is replaced by "-{", because of clash with GetOpt - sgp - 7th Nov 2000 // Copied from NDK build - Jan 5th 2001 if (strncmp(pclp->m_argv[index], (char *)"-{", 2) == 0) { // found a -q option; grab the semaphore number sscanf(pclp->m_argv[index], (char *)"-{%x", &pclp->m_qSemaphore); fnDeleteArgument(pclp, index); // Delete the argument from the list. } else if (strcmp(pclp->m_argv[index], (char *)"--noscreen") == 0) { // found a --noscreen option pclp->m_noScreen = 1; fnDeleteArgument(pclp, index); } else if (strcmp(pclp->m_argv[index], (char *)"--autodestroy") == 0) { // found a --autodestroy option - create a screen but close automatically pclp->m_AutoDestroy = 1; fnDeleteArgument(pclp, index); } else index++; } // pclp->m_isValid is TRUE if there are more than 2 command line parameters OR // if there is only one command and if it is the comman PERL. pclp->m_isValid = ((pclp->m_argc >= 2) || ((pclp->m_argc > 0) && (stricmp(pclp->m_argv[0], LOAD_COMMAND) != 0))); if(buffer) { free(buffer); buffer = NULL; } return; } /*============================================================================================ Function : fnAppendArgument Description : This function appends the arguments into a list. Parameters : pclp (IN) - CommandLine structure. new_arg (IN) - The new argument to be appended. Returns : Nothing. ==============================================================================================*/ void fnAppendArgument(PCOMMANDLINEPARSER pclp, char *new_arg) { char **new_argv = pclp->new_argv; int new_argv_len = pclp->m_argv_len*2; int i = 0, j = 0; // Lengthen the argument vector if there's not room for another. // Testing for 'm_argc+2' rather than 'm_argc+1' in the test guarantees // that there'll always be a NULL terminator at the end of argv. if ((pclp->m_argc + 2) > pclp->m_argv_len) { new_argv = (char **) malloc(new_argv_len * sizeof(char*)); // get a longer arg-vector if (new_argv == NULL) { pclp->m_isValid = FALSE; return; } for(i=0; im_isValid = FALSE; return; } } for (i=0; im_argc; i++) strcpy(new_argv[i], pclp->m_argv[i]); // copy old arg strings for(i=0; i<(pclp->m_argv_len); i++) { if(pclp->m_argv[i]) { free(pclp->m_argv[i]); pclp->m_argv[i] = NULL; } } if (pclp->m_argv != NULL) { free(pclp->m_argv); pclp->m_argv = NULL; } pclp->m_argv = new_argv; pclp->m_argv_len = new_argv_len; } // Once m_argv is guaranteed long enough, appending the argument is a direct job. strcpy(pclp->m_argv[pclp->m_argc], new_arg); // Appended the new argument. pclp->m_argc++; // Increment the number of parameters appended. // The char array is emptied for all elements upto the end so that there are no // junk characters. If this is not done, then the issue is like this: // - Simple perl command like "perl" on the system console works fine for the first time. // - When "perl" is executed the second time, a new blank screen should come up // which allows for editing also. This was not consistently working well. // More so when the command was like, "perl ", that is the name "perl" followed // by a few blank spaces, it used to give error in opening file: // "unable to open the file" since the filename would have some junk characters. // // These issues are fixed through the code below. for(i=pclp->m_argc; im_argv_len; i++) strncpy(pclp->m_argv[i], "", (MAX_DN_BYTES * sizeof(char))); // MAX_DN_BYTES is the size of pclp->m_argv[]. // Fix for empty command line double quote abend - perl <.pl> "" if ((new_arg==NULL) || ((strlen(new_arg))<=0)) { pclp->m_argc--; // Decrement the number of parameters appended. pclp->m_isValid = FALSE; return; } return; } /*============================================================================================ Function : fnSkipToken Description : This function collects the next command-line argument, breaking on unquoted white space. The quote symbols are copied into the output. White space has already been skipped. Parameters : s (IN) - Input string in which the token is skipped. r (IN) - The resultant return string. Returns : String. ==============================================================================================*/ char *fnSkipToken(char *s, char *r) { char *t=NULL; char quote = '\0'; // NULL, single quote, or double quote char ch = '\0'; for (t=s; t[0]; t++) { ch = t[0]; if (!quote) { if (isspace(ch)) // if unquoted whitespace... { break; // ...end of token found } else if (ch=='"' || ch=='\'') // if opening quote... { quote = ch; // ...enter quote mode } } else { if (ch=='\\' && t[1]==quote) // if escaped quote... { t++; // ...skip backslash } else if (ch==quote) // if close quote... { quote = 0; // ...leave quote mode } } } r = fnStashString(s, r, t-s); // get heap-allocated token string t = fnSkipWhite(t); // skip any trailing white space s = t; // return updated source pointer s2 = t; // return updated global source pointer return r; // return heap-allocated token string } /*============================================================================================ Function : fnScanToken Description : This function collects the next command-line argument, breaking on unquoted white space or I/O redirection symbols. Quote symbols are not copied into the output. When called, any leading white space has already been skipped. Parameters : x (IN) - Input string in which the token is scanned. r (IN) - The resultant return string. Returns : String. ==============================================================================================*/ char *fnScanToken(char *x, char *r) { char *s = x; // input string position char *t = x; // output string position char quote = '\0'; // either NULL, or single quote, or double quote char ch = '\0'; char c = '\0'; while (*s) { ch = *s; // invariant: ch != 0 // look to see if we've reached the end of the token if (!quote) // but don't look for token break if we're inside quotes { if (isspace(ch)) break; // break on whitespace if (ch=='>') break; // break on ">" (redirect stdout) if (ch=='<') break; // break on "<" (redirect stdin) if (ch=='&' && x[1]=='>') break; // break on "&>" (redirect both stdout & stderr) } // process the next source character if (ch=='\\' && (c=s[1]) && (c=='\\'||c=='>'||c=='<'||c==quote)) { //-----------------if an escaped '\\', '>', '<', or quote... s++; // ...skip over the backslash... *t++ = *s++; // ...and copy the escaped character } else if (ch==quote) // (won't match unless inside quotes because invariant ch!=0) { //-----------------if close quote... s++; // ...skip over the quote... quote=0; // ...and leave quote mode } else if (!quote && (ch=='"' || ch=='\'')) { //-----------------if opening quote... quote = *s++; // ...enter quote mode (remembering quote char, and skipping the quote) } else { //----------if normal character... *t++ = *s++; // ...copy the character } } // clean up return values r = fnStashString(x, r, t-x); // get heap-allocated token string s = fnSkipWhite(s); // skip any trailing white space x = s; // return updated source pointer s1 = s; // return updated global source pointer return r; } /*============================================================================================ Function : fnStashString Description : This function return the heap-allocated token string. Parameters : s (IN) - Input string from which the token is extracted. buffer (IN) - Return string. length (IN) - Length of the token to be extracted. Returns : String. ==============================================================================================*/ char *fnStashString(char *s, char *buffer, int length) { if (length <= 0) { // Copy "" instead of NULL since "" indicates that there is memory allocated having no/null value. // NULL indicates that there is no memory allocated to it! strcpy(buffer, ""); } else { strncpy(buffer, s, length); buffer[length] = '\0'; } return buffer; } /*============================================================================================ Function : fnDeleteArgument Description : This function deletes an argument (that was originally appended) from the list. Parameters : pclp (IN) - CommandLine structure. index (IN) - Index of the argument to be deleted. Returns : Nothing. ==============================================================================================*/ void fnDeleteArgument(PCOMMANDLINEPARSER pclp, int index) { int i = index; // If index is greater than the no. of arguments, just return. if (index >= pclp->m_argc) return; // Move all the arguments after the index one up. while(i < (pclp->m_argv_len-1)) { strcpy(pclp->m_argv[i], pclp->m_argv[i+1]); i++; } // Delete the last one and free memory. if ( pclp->m_argv[i] ) { free(pclp->m_argv[i]); pclp->m_argv[i] = NULL; } pclp->m_argc--; // Decrement the number of arguments. pclp->m_argv_len--; return; } /*============================================================================================ Function : fnMy_MkTemp Description : This is a standard ANSI C mktemp for NetWare Parameters : templatestr (IN) - Input temp filename. Returns : String. ==============================================================================================*/ char* fnMy_MkTemp(char* templatestr) { char* pXs=NULL; char numbuf[50]={'\0'}; int count=0; char* pPid=NULL; char termchar = '\0'; char letter = 'a'; char letter1 = 'a'; if (templatestr && (pXs = strstr(templatestr, (char *)"XXXXXX"))) { // generate temp name termchar = pXs[6]; ltoa(GetThreadID(), numbuf, 16); // numbuf[sizeof(numbuf)-1] = '\0'; numbuf[strlen(numbuf)-1] = '\0'; // beware! thread IDs are 8 hex digits on NW 4.11 and only the // lower digits seem to change, whereas on NW 5 they are in the // range of < 1000 hex or 3 hex digits in length. So the following // logic ensures we use the least significant portion of the number. if (strlen(numbuf) > 5) pPid = &numbuf[strlen(numbuf)-5]; else pPid = numbuf; /** Backtick operation uses temp files that are stored under NWDEFPERLTEMP directory. They are temporarily used and then cleaned up after usage. In cases where multiple backtick operations are used that call some complex scripts, new temp files will be created before the old ones are deleted. So, we need to have a provision to create many temp files. Hence the below logic. It is found that provision for 26 files may not be enough in some cases. This below logic allows 26 files (like, pla00015.tmp through plz00015.tmp) plus 6x26=676 (like, plaa0015.tmp through plzz0015.tmp) **/ letter = 'a'; do { sprintf(pXs, (char *)"%c%05.5s", letter, pPid); pXs[6] = termchar; if (access(templatestr, 0) != 0) // File does not exist { return templatestr; } letter++; } while (letter <= 'z'); letter1 = 'a'; do { letter = 'a'; do { sprintf(pXs, (char *)"%c%c%04.5s", letter1, letter, pPid); pXs[6] = termchar; if (access(templatestr, 0) != 0) // File does not exist { return templatestr; } letter++; } while (letter <= 'z'); letter1++; } while (letter1 <= 'z'); errno = ENOENT; return NULL; } else { errno = EINVAL; return NULL; } } /*============================================================================================ Function : fnSystemCommand Description : This function constructs a system command from the given null-terminated argv array and runs the command on the system console. Parameters : argv (IN) - Array of input commands. argc (IN) - Number of input parameters. Returns : Nothing. ==============================================================================================*/ void fnSystemCommand (char** argv, int argc) { // calculate the size of a temp buffer needed int k = 0; int totalSize = 0; int bytes = 0; char* tempCmd = NULL; char* tptr = NULL; for(k=0; k