/** * Try to handle special commands like "cd" or "export" internally. This will * work if we have a simple commend with no redirection, piping, or such. * * @param context The Exit oontext. * @param cmd The command string. * @param rc The operating system return code. * * @return True if command was handled, false otherwise. */ logical_t handleCommandInternally(RexxExitContext *context, char *cmd, RexxObjectPtr rc) { /* check for redirection symbols, ignore them when enclosed in double quotes. escaped quotes are ignored. */ bool noDirectInvoc = false; bool inQuotes = false; bool escape = false; size_t i; for (i = 0; i<strlen(cmd); i++) { if (escape) { escape = false; } else if (cmd[i] == '\\') { escape = true; } else if (cmd[i] == '"') { inQuotes = !inQuotes; } else { /* if we're in the unquoted part and the current character is one of */ /* the redirection characters or the & for multiple commands then we */ /* will no longer try to invoke the command directly */ if (!inQuotes && (strchr("<>|&;", cmd[i]) != NULL)) { noDirectInvoc = true; break; } } } if (!noDirectInvoc) { // execute special commands in the same process // we currently don't handle formats like " cd" or "'cd'" internally if (strcmp("cd", cmd) == 0 || strncmp("cd ", cmd, 3) == 0) { return sys_process_cd(context, cmd, rc); } if (strncmp("set ", cmd, 4) == 0) { return sys_process_export(context, cmd, rc, SET_FLAG); } if (strncmp("unset ", cmd, 6) == 0) { return sys_process_export(context, cmd, rc, UNSET_FLAG); } if (strncmp("export ", cmd, 7) == 0) { return sys_process_export(context, cmd, rc, EXPORT_FLAG); } } return false; }
RexxObjectPtr RexxEntry systemCommandHandler(RexxExitContext *context, RexxStringObject address, RexxStringObject command) { const char *cmd = context->StringData(command); const char *envName = context->StringData(address); RexxObjectPtr rc = NULLOBJECT; /* check for redirection symbols, ignore them when enclosed in double quotes. escaped quotes are ignored. */ bool noDirectInvoc = false; bool inQuotes = false; bool escape = false; size_t i; for (i = 0; i<strlen(cmd); i++) { if (escape) { escape = false; } else if (cmd[i] == '\\') { escape = true; } else if (cmd[i] == '"') { inQuotes = !inQuotes; } else { /* if we're in the unquoted part and the current character is one of */ /* the redirection characters or the & for multiple commands then we */ /* will no longer try to invoke the command directly */ if (!inQuotes && (strchr("<>|&", cmd[i]) != NULL)) { noDirectInvoc = true; break; } } } if (!noDirectInvoc) { /* execute 'cd' in the same process */ size_t commandLen = strlen(cmd); if (strcmp(cmd, "cd") == 0) { if (sys_process_cd(context, cmd, rc)) { return rc; } } else if (commandLen >= 3) { char tmp[16]; strncpy(tmp, cmd, 3); tmp[3] = '\0'; if (strcmp("cd ",tmp) == 0) { if (sys_process_cd(context, cmd, rc)) { return rc; } } strncpy(tmp, cmd, 4); tmp[4] = '\0'; if (strcmp("set ",tmp) == 0) { if (sys_process_export(context, cmd, rc, SET_FLAG)) /*unset works fine for me*/ { return rc; } } strncpy(tmp, cmd, 6); tmp[6] = '\0'; if (Utilities::strCaselessCompare("unset ", tmp) == 0) { if (sys_process_export(context, cmd, rc, UNSET_FLAG)) { return rc; } } strncpy(tmp, cmd, 7); tmp[7] = '\0'; if (Utilities::strCaselessCompare("export ", tmp) == 0) { if (sys_process_export(context, cmd, rc, EXPORT_FLAG)) { return rc; } } } } /****************************************************************************/ /* Invoke the system command handler to execute the command */ /****************************************************************************/ // if this is the null string, then use the default address environment // for the platform if (strlen(envName) == 0) { envName = SYSINITIALADDRESS; } int errCode = 0; #ifdef LINUX if (Utilities::strCaselessCompare("bash", envName) == 0) { errCode = system( cmd ); if ( errCode >= 256 ) { errCode = errCode / 256; } } else #endif { int pid = fork(); int status; if (pid != 0) /* spawn a child process to run the */ { waitpid ( pid, &status, 0); /* command and wait for it to finish */ if (WIFEXITED(status)) /* If cmd process ended normal */ { /* Give 'em the exit code */ errCode = WEXITSTATUS(status); } else /* Else process died ugly, so */ { errCode = -(WTERMSIG(status)); if (errCode == 1) /* If process was stopped */ { errCode = -1; /* Give 'em a -1. */ } } } else { /* run the command in the child */ if (Utilities::strCaselessCompare("sh", envName) == 0) { #ifdef ANDROID execl("/system/bin/sh", "sh", "-c", cmd, NULL); #else execl("/bin/sh", "sh", "-c", cmd, NULL); #endif } else if (Utilities::strCaselessCompare("ksh", envName) == 0) { execl("/bin/ksh", "ksh", "-c", cmd, NULL); } else if (Utilities::strCaselessCompare("bsh", envName) == 0) { execl("/bin/bsh", "bsh", "-c", cmd, NULL); } else if (Utilities::strCaselessCompare("csh", envName) == 0) { execl("/bin/csh", "csh", "-c", cmd, NULL); } else if (Utilities::strCaselessCompare("bash", envName) == 0) { execl("/bin/bash", "bash", "-c", cmd, NULL); } else if (Utilities::strCaselessCompare("cmd", envName) == 0) { char * args[MAX_COMMAND_ARGS+1]; /* Array for argument parsing */ if (!scan_cmd(cmd, args)) /* Parse cmd into arguments */ { exit(1); } execvp(args[0], args); /* Invoke command directly */ exit(1); /* we couldn't run the */ } else { execl("/bin/sh", "sh", "-c", cmd, NULL); } } } // unknown command code? if (errCode == UNKNOWN_COMMAND) { // failure condition context->RaiseCondition("FAILURE", context->String(cmd), NULL, context->WholeNumberToObject(errCode)); } else if (errCode != 0) { // non-zero is an error condition context->RaiseCondition("ERROR", context->String(cmd), NULL, context->WholeNumberToObject(errCode)); } return context->False(); // zero return code }
RexxObjectPtr RexxEntry systemCommandHandler(RexxExitContext *context, RexxStringObject address, RexxStringObject command) { // address the command information const char *cmd = context->StringData(command); const char *cl_opt = " /c "; // The "/c" opt for system commandd handler const char *interncmd; // Remove the "quiet sign" if present if (cmd[0] == '@') { interncmd = cmd + 1; } else { interncmd = cmd; } /* Check for redirection symbols, ignore them when enclosed in double * quotes. If there are more than 2 double qoutes, cmd.exe strips off the * first and last. To preserve what the user sent to us, we count the * double quotes, and, if more than two, add a double quote to the front and * end of the string. */ size_t quoteCount = 0; bool noDirectInvoc = false; bool inQuotes = false; size_t i; for (i = 0; i < strlen(interncmd); i++) { if (interncmd[i] == '"') { inQuotes = !inQuotes; quoteCount++; } else { /* if we're in the unquoted part and the current character is one of */ /* the redirection characters or the & for multiple commands then we */ /* will no longer try to invoke the command directly */ if (!inQuotes && (strchr("<>|&", interncmd[i]) != NULL)) { noDirectInvoc = true; if ( quoteCount > 2 ) { break; } } } } i = 0; // reset to zero for next usage // scan for the first non-whitespace character size_t j = 0; while (interncmd[j] == ' ') { j++; } RexxObjectPtr result = NULLOBJECT; if (!noDirectInvoc) { char tmp[8]; strncpy(tmp, &interncmd[j], 4); tmp[4] = '\0'; if (!stricmp("set ",tmp)) { if (sys_process_set(context, cmd, &interncmd[j], result)) { return result; } } else { strncpy(tmp, &interncmd[j], 3); tmp[3] = '\0'; if (!stricmp("cd ",tmp)) { if (sys_process_cd(context, cmd, &interncmd[j], result)) { return result; } } else { // Check if the command is to change drive if ((tmp[1] == ':') && ((tmp[2] == ' ') || (!tmp[2]))) { int code = _chdrive(toupper( tmp[0] ) - 'A' + 1); if (code != 0) { context->RaiseCondition("ERROR", command, NULLOBJECT, context->WholeNumberToObject(code)); return NULLOBJECT; } else { // 0 result. return context->False(); } } else { // Check if a START command is specified, if so do not // invoke the command directly. strncpy(tmp, &interncmd[j], 6); tmp[6] = '\0'; noDirectInvoc = stricmp("start ",tmp) == 0; } } } } const char *sys_cmd_handler; // Pointer to system cmd handler // Determine the system command interpreter. This could be the full path // name if COMSPEC is set, or a default name if it isn't. We no longer // suport Windows 95, so the default will be cmd.exe. But, it is still // possible for people to set COMSPEC to command.com, so we could end up // with command.com if ( (sys_cmd_handler = getenv(COMSPEC)) == NULL ) { sys_cmd_handler = CMDDEFNAMENT; } // Determine the maximum possible buffer size needed to pass the final // command to sysCommandNT(). size_t maxBufferSize = strlen(sys_cmd_handler) + 1 + strlen(cl_opt) + strlen(&interncmd[j]) + 2 // Two possible extra quotes + 1; // Terminating null char cmdstring[CMDBUFSIZENT]; // Default static buffer. char *cmdstring_ptr = cmdstring; // Will point to static buffer. if ( maxBufferSize > CMDBUFSIZENT ) { // Allocate dynamic memory and set cmdstring_ptr to point to it. cmdstring_ptr = (char *)LocalAlloc(LPTR, maxBufferSize); if ( cmdstring_ptr == NULL ) { context->RaiseException1(Rexx_Error_System_resources_user_defined, context->String("Failed to allocate memory")); return NULLOBJECT; } } else { // We want maxBufferSize to relect the actual size of the buffer we are // using so that we can test the return from SearchPath() maxBufferSize = CMDBUFSIZENT; } SystemInterpreter::exceptionConsole = false; SystemInterpreter::explicitConsole = false; // Check whether or not the command to invoke is cmd.exe or command.com _strupr(strcpy(cmdstring_ptr, &interncmd[j])); bool searchFile = strstr(cmdstring_ptr, "CMD") != NULL; if (searchFile) { if (cmdstring_ptr[0] == '\"') { cmdstring_ptr++; while (cmdstring_ptr[i] && (cmdstring_ptr[i] != '\"')) { i++; } cmdstring_ptr[i]='\0'; } else if (cmdstring_ptr[0] == '\'') { cmdstring_ptr++; while (cmdstring_ptr[i] && (cmdstring_ptr[i] != '\'')) { i++; } cmdstring_ptr[i]='\0'; } else { while (cmdstring_ptr[i] && (cmdstring_ptr[i] != ' ')) { i++; } cmdstring_ptr[i]='\0'; } LPSTR filepart; uint32_t count = SearchPath(NULL, cmdstring_ptr, ".EXE", (uint32_t)(maxBufferSize - 1), cmdstring, &filepart); bool fileFound = count != 0 && count <= maxBufferSize; // Set pointer back again to cmd buffer (might have been increased) cmdstring_ptr = cmdstring; if (fileFound && !stricmp(sys_cmd_handler, cmdstring_ptr)) { SystemInterpreter::exceptionConsole = true; SystemInterpreter::explicitConsole = true; } } // First check whether we can run the command directly as a program. (There // can be no file redirection when not using cmd.exe or command.com) if (SystemInterpreter::explicitConsole || !noDirectInvoc) { // Invoke this directly. If we fail, we fall through and try again. if (sysCommandNT(context, cmd, &interncmd[j], true, result)) { if ( cmdstring_ptr != cmdstring ) { // Not pointing to static buffer so we need to free it. LocalFree(cmdstring_ptr); } return result; } } // We couldn't invoke the command directly, or we tried and failed. So, // pass the command to cmd.exe or command.com. // Start the command buffer with the system cmd handler strcpy(cmdstring_ptr,sys_cmd_handler); // Check whether or not the user specified the /k option. If so do not use // the /c option. The /k option can only be specified as the first // argument, and if used, keeps the command handler process open after the // command has finished. Normally the /c option would be usee to close the // command handler process when the command is finished.. if (!( (strlen(interncmd) > j+1) && (interncmd[j] == '/') && ((interncmd[j+1] == 'k') || (interncmd[j+1] == 'K')) && ((interncmd[j+2] == ' ') || (interncmd[j+2] == '\0')) )) { strcat(cmdstring_ptr,cl_opt); } else { SystemInterpreter::exceptionConsole = true; strcat(cmdstring_ptr," "); } // Add cmd to be executed, possibly quoting it to preserve embedded quotes. if ( quoteCount> 2 ) { strcat(cmdstring_ptr,"\""); strcat(cmdstring_ptr,interncmd); strcat(cmdstring_ptr,"\""); } else { strcat(cmdstring_ptr,interncmd); } // Invoke the command if (!sysCommandNT(context, cmd, cmdstring_ptr, false, result)) { // Failed, get error code and return context->RaiseCondition("FAILURE", context->String(cmd), NULLOBJECT, context->WholeNumberToObject(GetLastError())); if ( cmdstring_ptr != cmdstring ) { // Not pointing to static buffer so we need to free it. LocalFree(cmdstring_ptr); } return NULLOBJECT; } if ( cmdstring_ptr != cmdstring ) { // Not pointing to static buffer so we need to free it. LocalFree(cmdstring_ptr); } SystemInterpreter::exceptionConsole = false; return result; }