3 * Copyright © 2001 Novell, Inc. All Rights Reserved.
5 * You may distribute under the terms of either the GNU General Public
6 * License or the Artistic License, as specified in the README file.
12 * DESCRIPTION : Utility functions for NetWare implementation of Perl.
14 * Date : Januray 2001.
23 #include <nwdsdefs.h> // For "MAX_DN_BYTES"
24 #include <malloc.h> // For "malloc" and "free"
25 #include <stdlib.h> // For "getenv"
26 #include <ctype.h> // For "isspace"
44 Global variables used for better token parsing. When these were absent,
45 token parsing was not correct when there were more number of arguments passed.
46 These are used in fnCommandLineParser, fnSkipToken and fnScanToken to get/return
47 the correct and updated pointer to the command line string.
49 char *s1 = NULL; // Used in fnScanToken.
50 char *s2 = NULL; // Used in fnSkipToken.
55 /*============================================================================================
57 Function : fnSkipWhite
59 Description : This function skips the white space characters in the given string and
60 returns the resultant value.
62 Parameters : s (IN) - Input string.
66 ==============================================================================================*/
68 char *fnSkipWhite(char *s)
77 /*============================================================================================
79 Function : fnNwGetEnvironmentStr
81 Description : This function returns the NetWare environment string if available,
82 otherwise returns the supplied default value
84 Parameters : name (IN) - To hold the NetWare environment value.
85 defaultvalue (IN) - Default value.
90 ==============================================================================================*/
92 char *fnNwGetEnvironmentStr(char *name, char *defaultvalue)
94 char* ret = getenv(name);
102 /*============================================================================================
104 Function : fnCommandLineParser
106 Description : This function parses the command line into argc/argv style of
107 Number of params and array of params.
109 Parameters : pclp (IN) - CommandLine structure.
110 commandLine (IN) - CommandLine String.
111 preserverQuotes (IN) - Indicates whether to preserve/copy the quotes or not.
115 ==============================================================================================*/
117 void fnCommandLineParser(PCOMMANDLINEPARSER pclp, char * commandLine, BOOL preserveQuotes)
126 // +1 makes room for the terminating NULL
127 buffer = (char *) malloc((strlen(commandLine) + 1) * sizeof(char));
130 pclp->m_isValid = FALSE;
136 // No I/O redirection nor quote processing if preserveQuotes
139 char *sSkippedToken = NULL;
142 strcpy(buffer, commandLine);
144 s = fnSkipWhite(s); // Skip white spaces.
146 s2 = s; // Update the global pointer.
149 pclp->sSkippedToken = (char *) malloc(MAX_DN_BYTES * sizeof(char));
150 if(pclp->sSkippedToken == NULL)
152 pclp->m_isValid = FALSE;
156 while (*s && pclp->m_isValid)
159 // Commented since only one time malloc and free is enough as is done outside this while loop.
160 // It is not required to do them everytime the execution comes into this while loop.
161 // Still retained here. Remove this once things are proved to be working fine to a good confident level,
163 if(pclp->sSkippedToken)
165 free(pclp->sSkippedToken);
166 pclp->sSkippedToken = NULL;
169 if(pclp->sSkippedToken == NULL)
171 pclp->sSkippedToken = (char *) malloc(MAX_DN_BYTES * sizeof(char));
172 if(pclp->sSkippedToken == NULL)
174 pclp->m_isValid = FALSE;
181 strncpy(pclp->sSkippedToken, "", (MAX_DN_BYTES * sizeof(char)));
183 // s is advanced by fnSkipToken
184 pclp->sSkippedToken = fnSkipToken(s, pclp->sSkippedToken); // Collect the next command-line argument.
186 s2 = fnSkipWhite(s2); // s2 is already updated by fnSkipToken.
187 s = s2; // Update the local pointer too.
189 fnAppendArgument(pclp, pclp->sSkippedToken); // Append the argument into an array.
192 if(pclp->sSkippedToken)
194 free(pclp->sSkippedToken);
195 pclp->sSkippedToken = NULL;
202 strcpy(buffer, commandLine);
206 s1 = s; // Update the global pointer.
208 while (*s && pclp->m_isValid)
210 // s is advanced by fnScanToken
211 // Check for I/O redirection here, *outside* of
212 // fnScanToken(), so that quote-protected angle
213 // brackets do NOT cause redirection.
216 s = fnSkipWhite(s+1); // get stdin redirection
218 if(pclp->m_redirInName)
220 free(pclp->m_redirInName);
221 pclp->m_redirInName = NULL;
224 if(pclp->m_redirInName == NULL)
226 pclp->m_redirInName = (char *) malloc(MAX_DN_BYTES * sizeof(char));
227 if(pclp->m_redirInName == NULL)
229 pclp->m_isValid = FALSE;
234 // Collect the next command-line argument.
235 pclp->m_redirInName = fnScanToken(s, pclp->m_redirInName);
237 s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken.
238 s = s1; // Update the local pointer too.
242 s = fnSkipWhite(s+1); //get stdout redirection
244 if(pclp->m_redirOutName)
246 free(pclp->m_redirOutName);
247 pclp->m_redirOutName = NULL;
250 if(pclp->m_redirOutName == NULL)
252 pclp->m_redirOutName = (char *) malloc(MAX_DN_BYTES * sizeof(char));
253 if(pclp->m_redirOutName == NULL)
255 pclp->m_isValid = FALSE;
260 // Collect the next command-line argument.
261 pclp->m_redirOutName = fnScanToken(s, pclp->m_redirOutName);
263 s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken.
264 s = s1; // Update the local pointer too.
266 else if (*s == '2' && s[1] == '>')
268 s = fnSkipWhite(s+2); // get stderr redirection
270 if(pclp->m_redirErrName)
272 free(pclp->m_redirErrName);
273 pclp->m_redirErrName = NULL;
276 if(pclp->m_redirErrName == NULL)
278 pclp->m_redirErrName = (char *) malloc(MAX_DN_BYTES * sizeof(char));
279 if(pclp->m_redirErrName == NULL)
281 pclp->m_isValid = FALSE;
286 // Collect the next command-line argument.
287 pclp->m_redirErrName = fnScanToken(s, pclp->m_redirErrName);
289 s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken.
290 s = s1; // Update the local pointer too.
292 else if (*s == '&' && s[1] == '>')
294 s = fnSkipWhite(s+2); // get stdout+stderr redirection
296 if(pclp->m_redirBothName)
298 free(pclp->m_redirBothName);
299 pclp->m_redirBothName = NULL;
302 if(pclp->m_redirBothName == NULL)
304 pclp->m_redirBothName = (char *) malloc(MAX_DN_BYTES * sizeof(char));
305 if(pclp->m_redirBothName == NULL)
307 pclp->m_isValid = FALSE;
312 // Collect the next command-line argument.
313 pclp->m_redirBothName = fnScanToken(s, pclp->m_redirBothName);
315 s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken.
316 s = s1; // Update the local pointer too.
323 pclp->nextarg = NULL;
326 if(pclp->nextarg == NULL)
328 pclp->nextarg = (char *) malloc(MAX_DN_BYTES * sizeof(char));
329 if(pclp->nextarg == NULL)
331 pclp->m_isValid = FALSE;
336 // Collect the next command-line argument.
337 pclp->nextarg = fnScanToken(s, pclp->nextarg);
339 s1 = fnSkipWhite(s1); // s1 is already updated by fnScanToken.
340 s = s1; // Update the local pointer too.
342 // Append the next command-line argument into an array.
343 fnAppendArgument(pclp, pclp->nextarg);
349 // The -{ option, the --noscreen option, the --autodestroy option, if present,
350 // are processed now and removed from the argument vector.
351 for(index=0; index < pclp->m_argc; )
353 // "-q" is replaced by "-{", because of clash with GetOpt - sgp - 7th Nov 2000
354 // Copied from NDK build - Jan 5th 2001
355 if (strncmp(pclp->m_argv[index], (char *)"-{", 2) == 0)
357 // found a -q option; grab the semaphore number
358 sscanf(pclp->m_argv[index], (char *)"-{%x", &pclp->m_qSemaphore);
359 fnDeleteArgument(pclp, index); // Delete the argument from the list.
361 else if (strcmp(pclp->m_argv[index], (char *)"--noscreen") == 0)
363 // found a --noscreen option
364 pclp->m_noScreen = 1;
365 fnDeleteArgument(pclp, index);
367 else if (strcmp(pclp->m_argv[index], (char *)"--autodestroy") == 0)
369 // found a --autodestroy option - create a screen but close automatically
370 pclp->m_AutoDestroy = 1;
371 fnDeleteArgument(pclp, index);
377 // pclp->m_isValid is TRUE if there are more than 2 command line parameters OR
378 // if there is only one command and if it is the comman PERL.
379 pclp->m_isValid = ((pclp->m_argc >= 2) || ((pclp->m_argc > 0) && (stricmp(pclp->m_argv[0], LOAD_COMMAND) != 0)));
392 /*============================================================================================
394 Function : fnAppendArgument
396 Description : This function appends the arguments into a list.
398 Parameters : pclp (IN) - CommandLine structure.
399 new_arg (IN) - The new argument to be appended.
403 ==============================================================================================*/
405 void fnAppendArgument(PCOMMANDLINEPARSER pclp, char *new_arg)
407 char **new_argv = pclp->new_argv;
409 int new_argv_len = pclp->m_argv_len*2;
413 // Lengthen the argument vector if there's not room for another.
414 // Testing for 'm_argc+2' rather than 'm_argc+1' in the test guarantees
415 // that there'll always be a NULL terminator at the end of argv.
416 if ((pclp->m_argc + 2) > pclp->m_argv_len)
418 new_argv = (char **) malloc(new_argv_len * sizeof(char*)); // get a longer arg-vector
419 if (new_argv == NULL)
421 pclp->m_isValid = FALSE;
424 for(i=0; i<new_argv_len; i++)
426 new_argv[i] = (char *) malloc(MAX_DN_BYTES * sizeof(char));
427 if (new_argv[i] == NULL)
443 pclp->m_isValid = FALSE;
448 for (i=0; i<pclp->m_argc; i++)
449 strcpy(new_argv[i], pclp->m_argv[i]); // copy old arg strings
451 for(i=0; i<(pclp->m_argv_len); i++)
455 free(pclp->m_argv[i]);
456 pclp->m_argv[i] = NULL;
459 if (pclp->m_argv != NULL)
466 pclp->m_argv = new_argv;
467 pclp->m_argv_len = new_argv_len;
471 // Once m_argv is guaranteed long enough, appending the argument is a direct job.
472 strcpy(pclp->m_argv[pclp->m_argc], new_arg); // Appended the new argument.
473 pclp->m_argc++; // Increment the number of parameters appended.
475 // The char array is emptied for all elements upto the end so that there are no junk characters.
476 // If this is not done, then the issue is like this:
477 // - Simple perl command like "perl" on the system console works fine for the first time.
478 // - When it is given the second time, a new blank screen should come up which also
479 // allows for editing. This was not consistently working well.
480 // More so when the command was like, "perl ", that is the name "perl"
481 // followed by a few blank spaces. It used to give error in opening file and
482 // would give some junk as the filename unable to open.
483 // Once the below fix was done, it is working fine.
484 for(i=pclp->m_argc; i<pclp->m_argv_len; i++)
485 strncpy(pclp->m_argv[i], "", (MAX_DN_BYTES * sizeof(char))); // MAX_DN_BYTES is the size of pclp->m_argv[].
488 // Fix for empty command line double quote abend - perl <.pl> ""
489 if ((new_arg==NULL) || ((strlen(new_arg))<=0))
491 pclp->m_argc--; // Decrement the number of parameters appended.
492 pclp->m_isValid = FALSE;
502 /*============================================================================================
504 Function : fnSkipToken
506 Description : This function collects the next command-line argument, breaking on
507 unquoted white space. The quote symbols are copied into the output.
508 White space has already been skipped.
510 Parameters : s (IN) - Input string in which the token is skipped.
511 r (IN) - The resultant return string.
515 ==============================================================================================*/
517 char *fnSkipToken(char *s, char *r)
519 register char *t=NULL;
520 register char quote = '\0'; // NULL, single quote, or double quote
528 if (isspace(ch)) // if unquoted whitespace...
530 break; // ...end of token found
532 else if (ch=='"' || ch=='\'') // if opening quote...
534 quote = ch; // ...enter quote mode
539 if (ch=='\\' && t[1]==quote) // if escaped quote...
541 t++; // ...skip backslash
543 else if (ch==quote) // if close quote...
545 quote = 0; // ...leave quote mode
550 r = fnStashString(s, r, t-s); // get heap-allocated token string
551 t = fnSkipWhite(t); // skip any trailing white space
552 s = t; // return updated source pointer
554 s2 = t; // return updated global source pointer
556 return r; // return heap-allocated token string
561 /*============================================================================================
563 Function : fnScanToken
565 Description : This function collects the next command-line argument, breaking on
566 unquoted white space or I/O redirection symbols. Quote symbols are not
567 copied into the output.
568 When called, any leading white space has already been skipped.
570 Parameters : x (IN) - Input string in which the token is scanned.
571 r (IN) - The resultant return string.
575 ==============================================================================================*/
577 char *fnScanToken(char *x, char *r)
579 register char *s = x; // input string position
580 register char *t = x; // output string position
581 register char quote = '\0'; // either NULL, or single quote, or double quote
582 register char ch = '\0';
583 register char c = '\0';
587 ch = *s; // invariant: ch != 0
589 // look to see if we've reached the end of the token
590 if (!quote) // but don't look for token break if we're inside quotes
593 break; // break on whitespace
595 break; // break on ">" (redirect stdout)
597 break; // break on "<" (redirect stdin)
598 if (ch=='&' && x[1]=='>')
599 break; // break on "&>" (redirect both stdout & stderr)
602 // process the next source character
603 if (ch=='\\' && (c=s[1]) && (c=='\\'||c=='>'||c=='<'||c==quote))
605 //-----------------if an escaped '\\', '>', '<', or quote...
606 s++; // ...skip over the backslash...
607 *t++ = *s++; // ...and copy the escaped character
609 else if (ch==quote) // (won't match unless inside quotes because invariant ch!=0)
611 //-----------------if close quote...
612 s++; // ...skip over the quote...
613 quote=0; // ...and leave quote mode
615 else if (!quote && (ch=='"' || ch=='\''))
617 //-----------------if opening quote...
618 quote = *s++; // ...enter quote mode (remembering quote char, and skipping the quote)
621 { //----------if normal character...
622 *t++ = *s++; // ...copy the character
626 // clean up return values
627 r = fnStashString(x, r, t-x); // get heap-allocated token string
628 s = fnSkipWhite(s); // skip any trailing white space
629 x = s; // return updated source pointer
631 s1 = s; // return updated global source pointer
638 /*============================================================================================
640 Function : fnStashString
642 Description : This function return the heap-allocated token string.
644 Parameters : s (IN) - Input string from which the token is extracted.
645 buffer (IN) - Return string.
646 length (IN) - Length of the token to be extracted.
650 ==============================================================================================*/
652 char *fnStashString(char *s, char *buffer, int length)
656 // Copy "" instead of NULL since "" indicates that there is memory allocated having no/null value.
657 // NULL indicates that there is no memory allocated to it!
662 strncpy(buffer, s, length);
663 buffer[length] = '\0';
671 /*============================================================================================
673 Function : fnDeleteArgument
675 Description : This function deletes an argument (that was originally appended) from the list.
677 Parameters : pclp (IN) - CommandLine structure.
678 index (IN) - Index of the argument to be deleted.
682 ==============================================================================================*/
684 void fnDeleteArgument(PCOMMANDLINEPARSER pclp, int index)
689 // If index is greater than the no. of arguments, just return.
690 if (index >= pclp->m_argc)
693 // Move all the arguments after the index one up.
694 while(i < (pclp->m_argv_len-1))
696 strcpy(pclp->m_argv[i], pclp->m_argv[i+1]);
701 // Delete the last one and free memory.
702 if ( pclp->m_argv[i] )
704 free(pclp->m_argv[i]);
705 pclp->m_argv[i] = NULL;
709 pclp->m_argc--; // Decrement the number of arguments.
717 /*============================================================================================
719 Function : fnMy_MkTemp
721 Description : This is a standard ANSI C mktemp for NetWare
723 Parameters : templatestr (IN) - Input temp filename.
727 ==============================================================================================*/
729 char* fnMy_MkTemp(char* templatestr)
732 char numbuf[50]={'\0'};
736 char termchar = '\0';
741 if (templatestr && (pXs = strstr(templatestr, (char *)"XXXXXX")))
743 // generate temp name
745 ltoa(GetThreadID(), numbuf, 16);
746 // numbuf[sizeof(numbuf)-1] = '\0';
747 numbuf[strlen(numbuf)-1] = '\0';
748 // beware! thread IDs are 8 hex digits on NW 4.11 and only the
749 // lower digits seem to change, whereas on NW 5 they are in the
750 // range of < 1000 hex or 3 hex digits in length. So the following
751 // logic ensures we use the least significant portion of the number.
752 if (strlen(numbuf) > 5)
753 pPid = &numbuf[strlen(numbuf)-5];
758 Backtick operation uses temp files that are stored under NWDEFPERLTEMP
759 directory. They are temporarily used and then cleaned up after usage.
760 In cases where multiple backtick operations are used that call some
761 complex scripts, new temp files will be created before the old ones are
762 deleted. So, we need to have a provision to create many temp files.
763 Hence the below logic. It is found that provision for 26 files may
764 not be enough in some cases.
766 This below logic allows 26 files (like, pla00015.tmp through plz00015.tmp)
767 plus 6x26=676 (like, plaa0015.tmp through plzz0015.tmp)
773 sprintf(pXs, (char *)"%c%05.5s", letter, pPid);
775 if (access(templatestr, 0) != 0) // File does not exist
780 } while (letter <= 'z');
788 sprintf(pXs, (char *)"%c%c%04.5s", letter1, letter, pPid);
790 if (access(templatestr, 0) != 0) // File does not exist
795 } while (letter <= 'z');
797 } while (letter1 <= 'z');
811 /*============================================================================================
813 Function : fnSystemCommand
815 Description : This function constructs a system command from the given
816 null-terminated argv array and runs the command on the system console.
818 Parameters : argv (IN) - Array of input commands.
819 argc (IN) - Number of input parameters.
823 ==============================================================================================*/
825 void fnSystemCommand (char** argv, int argc)
827 // calculate the size of a temp buffer needed
831 char* tempCmd = NULL;
835 for(k=0; k<argc; k++)
836 totalSize += strlen(argv[k]) + 1;
838 tempCmd = (char *) malloc((totalSize+1) * sizeof(char));
843 for(k=0; k<argc; k++)
844 tptr += sprintf(tptr, (char *)"%s ", argv[k]);
847 if (stricmp(argv[0], PERL_COMMAND_NAME) == 0)
848 fnInternalPerlLaunchHandler(tempCmd); // Launch perl.