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
476 // junk characters. 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 "perl" is executed the second time, a new blank screen should come up
479 // which allows for editing also. This was not consistently working well.
480 // More so when the command was like, "perl ", that is the name "perl" followed
481 // by a few blank spaces, it used to give error in opening file:
482 // "unable to open the file" since the filename would have some junk characters.
484 // These issues are fixed through the code below.
485 for(i=pclp->m_argc; i<pclp->m_argv_len; i++)
486 strncpy(pclp->m_argv[i], "", (MAX_DN_BYTES * sizeof(char))); // MAX_DN_BYTES is the size of pclp->m_argv[].
489 // Fix for empty command line double quote abend - perl <.pl> ""
490 if ((new_arg==NULL) || ((strlen(new_arg))<=0))
492 pclp->m_argc--; // Decrement the number of parameters appended.
493 pclp->m_isValid = FALSE;
503 /*============================================================================================
505 Function : fnSkipToken
507 Description : This function collects the next command-line argument, breaking on
508 unquoted white space. The quote symbols are copied into the output.
509 White space has already been skipped.
511 Parameters : s (IN) - Input string in which the token is skipped.
512 r (IN) - The resultant return string.
516 ==============================================================================================*/
518 char *fnSkipToken(char *s, char *r)
520 register char *t=NULL;
521 register char quote = '\0'; // NULL, single quote, or double quote
529 if (isspace(ch)) // if unquoted whitespace...
531 break; // ...end of token found
533 else if (ch=='"' || ch=='\'') // if opening quote...
535 quote = ch; // ...enter quote mode
540 if (ch=='\\' && t[1]==quote) // if escaped quote...
542 t++; // ...skip backslash
544 else if (ch==quote) // if close quote...
546 quote = 0; // ...leave quote mode
551 r = fnStashString(s, r, t-s); // get heap-allocated token string
552 t = fnSkipWhite(t); // skip any trailing white space
553 s = t; // return updated source pointer
555 s2 = t; // return updated global source pointer
557 return r; // return heap-allocated token string
562 /*============================================================================================
564 Function : fnScanToken
566 Description : This function collects the next command-line argument, breaking on
567 unquoted white space or I/O redirection symbols. Quote symbols are not
568 copied into the output.
569 When called, any leading white space has already been skipped.
571 Parameters : x (IN) - Input string in which the token is scanned.
572 r (IN) - The resultant return string.
576 ==============================================================================================*/
578 char *fnScanToken(char *x, char *r)
580 register char *s = x; // input string position
581 register char *t = x; // output string position
582 register char quote = '\0'; // either NULL, or single quote, or double quote
583 register char ch = '\0';
584 register char c = '\0';
588 ch = *s; // invariant: ch != 0
590 // look to see if we've reached the end of the token
591 if (!quote) // but don't look for token break if we're inside quotes
594 break; // break on whitespace
596 break; // break on ">" (redirect stdout)
598 break; // break on "<" (redirect stdin)
599 if (ch=='&' && x[1]=='>')
600 break; // break on "&>" (redirect both stdout & stderr)
603 // process the next source character
604 if (ch=='\\' && (c=s[1]) && (c=='\\'||c=='>'||c=='<'||c==quote))
606 //-----------------if an escaped '\\', '>', '<', or quote...
607 s++; // ...skip over the backslash...
608 *t++ = *s++; // ...and copy the escaped character
610 else if (ch==quote) // (won't match unless inside quotes because invariant ch!=0)
612 //-----------------if close quote...
613 s++; // ...skip over the quote...
614 quote=0; // ...and leave quote mode
616 else if (!quote && (ch=='"' || ch=='\''))
618 //-----------------if opening quote...
619 quote = *s++; // ...enter quote mode (remembering quote char, and skipping the quote)
622 { //----------if normal character...
623 *t++ = *s++; // ...copy the character
627 // clean up return values
628 r = fnStashString(x, r, t-x); // get heap-allocated token string
629 s = fnSkipWhite(s); // skip any trailing white space
630 x = s; // return updated source pointer
632 s1 = s; // return updated global source pointer
639 /*============================================================================================
641 Function : fnStashString
643 Description : This function return the heap-allocated token string.
645 Parameters : s (IN) - Input string from which the token is extracted.
646 buffer (IN) - Return string.
647 length (IN) - Length of the token to be extracted.
651 ==============================================================================================*/
653 char *fnStashString(char *s, char *buffer, int length)
657 // Copy "" instead of NULL since "" indicates that there is memory allocated having no/null value.
658 // NULL indicates that there is no memory allocated to it!
663 strncpy(buffer, s, length);
664 buffer[length] = '\0';
672 /*============================================================================================
674 Function : fnDeleteArgument
676 Description : This function deletes an argument (that was originally appended) from the list.
678 Parameters : pclp (IN) - CommandLine structure.
679 index (IN) - Index of the argument to be deleted.
683 ==============================================================================================*/
685 void fnDeleteArgument(PCOMMANDLINEPARSER pclp, int index)
690 // If index is greater than the no. of arguments, just return.
691 if (index >= pclp->m_argc)
694 // Move all the arguments after the index one up.
695 while(i < (pclp->m_argv_len-1))
697 strcpy(pclp->m_argv[i], pclp->m_argv[i+1]);
702 // Delete the last one and free memory.
703 if ( pclp->m_argv[i] )
705 free(pclp->m_argv[i]);
706 pclp->m_argv[i] = NULL;
710 pclp->m_argc--; // Decrement the number of arguments.
718 /*============================================================================================
720 Function : fnMy_MkTemp
722 Description : This is a standard ANSI C mktemp for NetWare
724 Parameters : templatestr (IN) - Input temp filename.
728 ==============================================================================================*/
730 char* fnMy_MkTemp(char* templatestr)
733 char numbuf[50]={'\0'};
737 char termchar = '\0';
742 if (templatestr && (pXs = strstr(templatestr, (char *)"XXXXXX")))
744 // generate temp name
746 ltoa(GetThreadID(), numbuf, 16);
747 // numbuf[sizeof(numbuf)-1] = '\0';
748 numbuf[strlen(numbuf)-1] = '\0';
749 // beware! thread IDs are 8 hex digits on NW 4.11 and only the
750 // lower digits seem to change, whereas on NW 5 they are in the
751 // range of < 1000 hex or 3 hex digits in length. So the following
752 // logic ensures we use the least significant portion of the number.
753 if (strlen(numbuf) > 5)
754 pPid = &numbuf[strlen(numbuf)-5];
759 Backtick operation uses temp files that are stored under NWDEFPERLTEMP
760 directory. They are temporarily used and then cleaned up after usage.
761 In cases where multiple backtick operations are used that call some
762 complex scripts, new temp files will be created before the old ones are
763 deleted. So, we need to have a provision to create many temp files.
764 Hence the below logic. It is found that provision for 26 files may
765 not be enough in some cases.
767 This below logic allows 26 files (like, pla00015.tmp through plz00015.tmp)
768 plus 6x26=676 (like, plaa0015.tmp through plzz0015.tmp)
774 sprintf(pXs, (char *)"%c%05.5s", letter, pPid);
776 if (access(templatestr, 0) != 0) // File does not exist
781 } while (letter <= 'z');
789 sprintf(pXs, (char *)"%c%c%04.5s", letter1, letter, pPid);
791 if (access(templatestr, 0) != 0) // File does not exist
796 } while (letter <= 'z');
798 } while (letter1 <= 'z');
812 /*============================================================================================
814 Function : fnSystemCommand
816 Description : This function constructs a system command from the given
817 null-terminated argv array and runs the command on the system console.
819 Parameters : argv (IN) - Array of input commands.
820 argc (IN) - Number of input parameters.
824 ==============================================================================================*/
826 void fnSystemCommand (char** argv, int argc)
828 // calculate the size of a temp buffer needed
832 char* tempCmd = NULL;
836 for(k=0; k<argc; k++)
837 totalSize += strlen(argv[k]) + 1;
839 tempCmd = (char *) malloc((totalSize+1) * sizeof(char));
844 for(k=0; k<argc; k++)
845 tptr += sprintf(tptr, (char *)"%s ", argv[k]);
848 if (stricmp(argv[0], PERL_COMMAND_NAME) == 0)
849 fnInternalPerlLaunchHandler(tempCmd); // Launch perl.