#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include "shell.h"
#include "textBuf.h"
#include "text.h"
#include "nedit.h"
#include "window.h"
#include "preferences.h"
#include "file.h"
#include "macro.h"
#include "interpret.h"
#include "../util/DialogF.h"
#include "../util/misc.h"
#include "../util/nedit_malloc.h"
#include "menu.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#ifdef notdef
#ifdef IBM
#define NBBY 8
#include <sys/select.h>
#endif
#include <time.h>
#endif
#ifdef __EMX__
#include <process.h>
#endif
#include <Xm/Xm.h>
#include <Xm/MessageB.h>
#include <Xm/Text.h>
#include <Xm/Form.h>
#include <Xm/PushBG.h>
#ifdef HAVE_DEBUG_H
#include "../debug.h"
#endif
#define IO_BUF_SIZE 4096
#define MAX_OUT_DIALOG_ROWS 30
#define MAX_OUT_DIALOG_COLS 80
#define OUTPUT_FLUSH_FREQ 1000
#define BANNER_WAIT_TIME 6000
#define ACCUMULATE 1
#define ERROR_DIALOGS 2
#define REPLACE_SELECTION 4
#define RELOAD_FILE_AFTER 8
#define OUTPUT_TO_DIALOG 16
#define OUTPUT_TO_STRING 32
typedef struct bufElem {
struct bufElem *next;
int length;
char contents[
IO_BUF_SIZE];
} buffer;
typedef struct {
int flags;
int stdinFD, stdoutFD, stderrFD;
pid_t childPid;
XtInputId stdinInputID, stdoutInputID, stderrInputID;
buffer *outBufs, *errBufs;
char *input;
char *inPtr;
Widget textW;
int leftPos, rightPos;
int inLength;
XtIntervalId bannerTimeoutID, flushTimeoutID;
char bannerIsUp;
char fromMacro;
} shellCmdInfo;
static void issueCommand(WindowInfo *window,
const char *command,
char *input,
int inputLen,
int flags, Widget textW,
int replaceLeft,
int replaceRight,
int fromMacro);
static void stdoutReadProc(XtPointer clientData,
int *source, XtInputId *id);
static void stderrReadProc(XtPointer clientData,
int *source, XtInputId *id);
static void stdinWriteProc(XtPointer clientData,
int *source, XtInputId *id);
static void finishCmdExecution(WindowInfo *window,
int terminatedOnError);
static pid_t forkCommand(Widget parent,
const char *command,
const char *cmdDir,
int *stdinFD,
int *stdoutFD,
int *stderrFD);
static void addOutput(buffer **bufList, buffer *buf);
static char *coalesceOutput(buffer **bufList,
int *length);
static void freeBufList(buffer **bufList);
static void removeTrailingNewlines(
char *string);
static void createOutputDialog(Widget parent,
char *text);
static void destroyOutDialogCB(Widget w, XtPointer callback, XtPointer closure);
static void measureText(
char *text,
int wrapWidth,
int *rows,
int *cols,
int *wrapped);
static void truncateString(
char *string,
int length);
static void bannerTimeoutProc(XtPointer clientData, XtIntervalId *id);
static void flushTimeoutProc(XtPointer clientData, XtIntervalId *id);
static void safeBufReplace(textBuffer *buf,
int *start,
int *end,
const char *text);
static char *shellCommandSubstitutes(
const char *inStr,
const char *fileStr,
const char *lineStr);
static int shellSubstituter(
char *outStr,
const char *inStr,
const char *fileStr,
const char *lineStr,
int outLen,
int predictOnly);
void FilterSelection(WindowInfo *window,
const char *command,
int fromMacro)
{
int left, right, textLen;
char *text;
if (window->shellCmdData !=
NULL) {
XBell(TheDisplay,
0);
return;
}
text = BufGetSelectionText(window->buffer);
if (*text ==
'\0') {
NEditFree(text);
XBell(TheDisplay,
0);
return;
}
textLen = strlen(text);
BufUnsubstituteNullChars(text, window->buffer);
left = window->buffer->primary.start;
right = window->buffer->primary.end;
issueCommand(window, command, text, textLen,
ACCUMULATE |
ERROR_DIALOGS |
REPLACE_SELECTION, window->lastFocus, left, right, fromMacro);
}
void ExecShellCommand(WindowInfo *window,
const char *command,
int fromMacro)
{
int left, right, flags =
0;
char *subsCommand, fullName[
MAXPATHLEN];
int pos, line, column;
char lineNumber[
16];
if (window->shellCmdData !=
NULL) {
XBell(TheDisplay,
0);
return;
}
pos = TextGetCursorPos(window->lastFocus);
if (GetSimpleSelection(window->buffer, &left, &right))
flags =
ACCUMULATE |
REPLACE_SELECTION;
else
left = right = pos;
strcpy(fullName, window->path);
strcat(fullName, window->filename);
if(!TextPosToLineAndCol(window->lastFocus, pos, &line, &column)) {
line = BufCountLines(window->buffer,
0, pos) +
1;
}
snprintf(lineNumber,
16,
"%d", line);
subsCommand = shellCommandSubstitutes(command, fullName, lineNumber);
if (subsCommand ==
NULL)
{
DialogF(
DF_ERR, window->shell,
1,
"Shell Command",
"Shell command is too long due to\n"
"filename substitutions with ''%%'' or\n"
"line number substitutions with ''#''",
"OK");
return;
}
issueCommand(window, subsCommand,
NULL,
0, flags, window->lastFocus, left,
right, fromMacro);
free(subsCommand);
}
void ShellCmdToMacroString(WindowInfo *window,
const char *command,
const char *input)
{
char *inputCopy;
inputCopy = *input ==
'\0' ?
NULL : NEditStrdup(input);
issueCommand(window, command, inputCopy, strlen(input),
ACCUMULATE |
OUTPUT_TO_STRING,
NULL,
0,
0, True);
}
void ExecCursorLine(WindowInfo *window,
int fromMacro)
{
char *cmdText;
int left, right, insertPos;
char *subsCommand, fullName[
MAXPATHLEN];
int pos, line, column;
char lineNumber[
16];
if (window->shellCmdData !=
NULL) {
XBell(TheDisplay,
0);
return;
}
pos = TextGetCursorPos(window->lastFocus);
if (!GetSimpleSelection(window->buffer, &left, &right)) {
left = right = pos;
left = BufStartOfLine(window->buffer, left);
right = BufEndOfLine(window->buffer, right);
insertPos = right;
}
else
insertPos = BufEndOfLine(window->buffer, right);
cmdText = BufGetRange(window->buffer, left, right);
BufUnsubstituteNullChars(cmdText, window->buffer);
BufInsert(window->buffer, insertPos,
"\n");
strcpy(fullName, window->path);
strcat(fullName, window->filename);
if(!TextPosToLineAndCol(window->lastFocus, pos, &line, &column)) {
line = BufCountLines(window->buffer,
0, pos) +
1;
}
snprintf(lineNumber,
16,
"%d", line);
subsCommand = shellCommandSubstitutes(cmdText, fullName, lineNumber);
if (subsCommand ==
NULL)
{
DialogF(
DF_ERR, window->shell,
1,
"Shell Command",
"Shell command is too long due to\n"
"filename substitutions with ''%%'' or\n"
"line number substitutions with ''#''",
"OK");
return;
}
issueCommand(window, subsCommand,
NULL,
0,
0, window->lastFocus, insertPos+
1,
insertPos+
1, fromMacro);
free(subsCommand);
NEditFree(cmdText);
}
void DoShellMenuCmd(WindowInfo *window,
const char *command,
int input,
int output,
int outputReplacesInput,
int saveFirst,
int loadAfter,
int fromMacro)
{
int flags =
0;
char *text;
char *subsCommand, fullName[
MAXPATHLEN];
int left =
0, right =
0, textLen;
int pos, line, column;
char lineNumber[
16];
WindowInfo *inWindow = window;
Widget outWidget;
if (window->shellCmdData !=
NULL) {
XBell(TheDisplay,
0);
return;
}
strcpy(fullName, window->path);
strcat(fullName, window->filename);
pos = TextGetCursorPos(window->lastFocus);
if(!TextPosToLineAndCol(window->lastFocus, pos, &line, &column)) {
line = BufCountLines(window->buffer,
0, pos) +
1;
}
snprintf(lineNumber,
16,
"%d", line);
subsCommand = shellCommandSubstitutes(command, fullName, lineNumber);
if (subsCommand ==
NULL)
{
DialogF(
DF_ERR, window->shell,
1,
"Shell Command",
"Shell command is too long due to\n"
"filename substitutions with ''%%'' or\n"
"line number substitutions with ''#''",
"OK");
return;
}
if (input ==
FROM_SELECTION) {
text = BufGetSelectionText(window->buffer);
if (*text ==
'\0') {
NEditFree(text);
NEditFree(subsCommand);
XBell(TheDisplay,
0);
return;
}
flags |=
ACCUMULATE |
ERROR_DIALOGS;
}
else if (input ==
FROM_WINDOW) {
text = BufGetAll(window->buffer);
flags |=
ACCUMULATE |
ERROR_DIALOGS;
}
else if (input ==
FROM_EITHER) {
text = BufGetSelectionText(window->buffer);
if (*text ==
'\0') {
NEditFree(text);
text = BufGetAll(window->buffer);
}
flags |=
ACCUMULATE |
ERROR_DIALOGS;
}
else
text =
NULL;
if (text !=
NULL) {
textLen = strlen(text);
BufUnsubstituteNullChars(text, window->buffer);
}
else
textLen =
0;
if (output ==
TO_DIALOG) {
outWidget =
NULL;
flags |=
OUTPUT_TO_DIALOG;
left = right =
0;
}
else if (output ==
TO_NEW_WINDOW) {
EditNewFile(GetPrefOpenInTab()?inWindow:
NULL,
NULL, False,
NULL, window->path);
outWidget = WindowList->textArea;
inWindow = WindowList;
left = right =
0;
CheckCloseDim();
}
else {
outWidget = window->lastFocus;
if (outputReplacesInput && input !=
FROM_NONE) {
if (input ==
FROM_WINDOW) {
left =
0;
right = window->buffer->length;
}
else if (input ==
FROM_SELECTION) {
GetSimpleSelection(window->buffer, &left, &right);
flags |=
ACCUMULATE |
REPLACE_SELECTION;
}
else if (input ==
FROM_EITHER) {
if (GetSimpleSelection(window->buffer, &left, &right))
flags |=
ACCUMULATE |
REPLACE_SELECTION;
else {
left =
0;
right = window->buffer->length;
}
}
}
else {
if (GetSimpleSelection(window->buffer, &left, &right))
flags |=
ACCUMULATE |
REPLACE_SELECTION;
else
left = right = TextGetCursorPos(window->lastFocus);
}
}
if (saveFirst) {
if (!SaveWindow(window)) {
if (input !=
FROM_NONE)
NEditFree(text);
free(subsCommand);
return;
}
}
if (loadAfter)
flags |=
RELOAD_FILE_AFTER;
issueCommand(inWindow, subsCommand, text, textLen, flags, outWidget, left,
right, fromMacro);
free(subsCommand);
}
void AbortShellCommand(WindowInfo *window)
{
shellCmdInfo *cmdData = window->shellCmdData;
if (cmdData ==
NULL)
return;
kill(- cmdData->childPid,
SIGTERM);
finishCmdExecution(window, True);
}
static void issueCommand(WindowInfo *window,
const char *command,
char *input,
int inputLen,
int flags, Widget textW,
int replaceLeft,
int replaceRight,
int fromMacro)
{
int stdinFD, stdoutFD, stderrFD =
0;
XtAppContext context = XtWidgetToApplicationContext(window->shell);
shellCmdInfo *cmdData;
pid_t childPid;
if ((flags &
ERROR_DIALOGS || flags &
REPLACE_SELECTION ||
flags &
OUTPUT_TO_STRING) && !(flags &
ACCUMULATE))
return;
if (fromMacro)
window = MacroRunWindow();
if (!fromMacro)
BeginWait(window->shell);
if (!fromMacro)
SetSensitive(window, window->cancelShellItem, True);
childPid = forkCommand(window->shell, command, window->path, &stdinFD,
&stdoutFD, (flags &
ERROR_DIALOGS) ? &stderrFD :
NULL);
if (fcntl(stdinFD,
F_SETFL,
O_NONBLOCK) <
0)
perror(
"xnedit: Internal error (fcntl)");
if (fcntl(stdoutFD,
F_SETFL,
O_NONBLOCK) <
0)
perror(
"xnedit: Internal error (fcntl1)");
if (flags &
ERROR_DIALOGS) {
if (fcntl(stderrFD,
F_SETFL,
O_NONBLOCK) <
0)
perror(
"xnedit: Internal error (fcntl2)");
}
if (input ==
NULL)
close(stdinFD);
cmdData = (shellCmdInfo *)NEditMalloc(
sizeof(shellCmdInfo));
window->shellCmdData = cmdData;
cmdData->flags = flags;
cmdData->stdinFD = stdinFD;
cmdData->stdoutFD = stdoutFD;
cmdData->stderrFD = stderrFD;
cmdData->childPid = childPid;
cmdData->outBufs =
NULL;
cmdData->errBufs =
NULL;
cmdData->input = input;
cmdData->inPtr = input;
cmdData->textW = textW;
cmdData->bannerIsUp = False;
cmdData->fromMacro = fromMacro;
cmdData->leftPos = replaceLeft;
cmdData->rightPos = replaceRight;
cmdData->inLength = inputLen;
if (fromMacro)
cmdData->bannerTimeoutID =
0;
else
cmdData->bannerTimeoutID = XtAppAddTimeOut(context,
BANNER_WAIT_TIME,
bannerTimeoutProc, window);
if ((flags &
ACCUMULATE) || textW ==
NULL)
cmdData->flushTimeoutID =
0;
else
cmdData->flushTimeoutID = XtAppAddTimeOut(context,
OUTPUT_FLUSH_FREQ,
flushTimeoutProc, window);
cmdData->stdoutInputID = XtAppAddInput(context, stdoutFD,
(XtPointer)XtInputReadMask, stdoutReadProc, window);
if (input !=
NULL)
cmdData->stdinInputID = XtAppAddInput(context, stdinFD,
(XtPointer)XtInputWriteMask, stdinWriteProc, window);
else
cmdData->stdinInputID =
0;
if (flags &
ERROR_DIALOGS)
cmdData->stderrInputID = XtAppAddInput(context, stderrFD,
(XtPointer)XtInputReadMask, stderrReadProc, window);
else
cmdData->stderrInputID =
0;
if (fromMacro)
PreemptMacro();
}
static void stdoutReadProc(XtPointer clientData,
int *source, XtInputId *id)
{
WindowInfo *window = (WindowInfo *)clientData;
shellCmdInfo *cmdData = window->shellCmdData;
buffer *buf;
int nRead;
buf = (buffer *)NEditMalloc(
sizeof(buffer));
nRead = read(cmdData->stdoutFD, buf->contents,
IO_BUF_SIZE);
if (nRead == -
1) {
if (errno !=
EWOULDBLOCK && errno !=
EAGAIN) {
perror(
"xnedit: Error reading shell command output");
NEditFree(buf);
finishCmdExecution(window, True);
}
return;
}
if (nRead ==
0) {
NEditFree(buf);
XtRemoveInput(cmdData->stdoutInputID);
cmdData->stdoutInputID =
0;
if (cmdData->stderrInputID ==
0)
finishCmdExecution(window, False);
return;
}
buf->length = nRead;
addOutput(&cmdData->outBufs, buf);
}
static void stderrReadProc(XtPointer clientData,
int *source, XtInputId *id)
{
WindowInfo *window = (WindowInfo *)clientData;
shellCmdInfo *cmdData = window->shellCmdData;
buffer *buf;
int nRead;
buf = (buffer *)NEditMalloc(
sizeof(buffer));
nRead = read(cmdData->stderrFD, buf->contents,
IO_BUF_SIZE);
if (nRead == -
1) {
if (errno !=
EWOULDBLOCK && errno !=
EAGAIN) {
perror(
"xnedit: Error reading shell command error stream");
NEditFree(buf);
finishCmdExecution(window, True);
}
return;
}
if (nRead ==
0) {
NEditFree(buf);
XtRemoveInput(cmdData->stderrInputID);
cmdData->stderrInputID =
0;
if (cmdData->stdoutInputID ==
0)
finishCmdExecution(window, False);
return;
}
buf->length = nRead;
addOutput(&cmdData->errBufs, buf);
}
static void stdinWriteProc(XtPointer clientData,
int *source, XtInputId *id)
{
WindowInfo *window = (WindowInfo *)clientData;
shellCmdInfo *cmdData = window->shellCmdData;
int nWritten;
nWritten = write(cmdData->stdinFD, cmdData->inPtr, cmdData->inLength);
if (nWritten == -
1) {
if (errno ==
EPIPE) {
XtRemoveInput(cmdData->stdinInputID);
cmdData->stdinInputID =
0;
close(cmdData->stdinFD);
cmdData->inPtr =
NULL;
}
else if (errno !=
EWOULDBLOCK && errno !=
EAGAIN) {
perror(
"xnedit: Write to shell command failed");
finishCmdExecution(window, True);
}
}
else {
cmdData->inPtr += nWritten;
cmdData->inLength -= nWritten;
if (cmdData->inLength <=
0) {
XtRemoveInput(cmdData->stdinInputID);
cmdData->stdinInputID =
0;
close(cmdData->stdinFD);
cmdData->inPtr =
NULL;
}
}
}
#define MAX_TIMEOUT_MSG_LEN (
MAX_ACCEL_LEN +
60)
static void bannerTimeoutProc(XtPointer clientData, XtIntervalId *id)
{
WindowInfo *window = (WindowInfo *)clientData;
shellCmdInfo *cmdData = window->shellCmdData;
XmString xmCancel;
char* cCancel;
char message[
MAX_TIMEOUT_MSG_LEN];
cmdData->bannerIsUp = True;
XtVaGetValues(window->cancelShellItem, XmNacceleratorText, &xmCancel,
NULL);
cCancel = GetXmStringText(xmCancel);
XmStringFree(xmCancel);
if (
'\0' == cCancel[
0])
{
strncpy(message,
"Shell Command in Progress",
MAX_TIMEOUT_MSG_LEN);
message[
MAX_TIMEOUT_MSG_LEN -
1] =
'\0';
}
else
{
snprintf(message,
MAX_TIMEOUT_MSG_LEN,
"Shell Command in Progress -- Press %s to Cancel",
cCancel);
}
NEditFree(cCancel);
SetModeMessage(window, message);
cmdData->bannerTimeoutID =
0;
}
static void safeBufReplace(textBuffer *buf,
int *start,
int *end,
const char *text)
{
if (*start > buf->length)
*start = buf->length;
if (*end > buf->length)
*end = buf->length;
BufReplace(buf, *start, *end, text);
}
static void flushTimeoutProc(XtPointer clientData, XtIntervalId *id)
{
WindowInfo *window = (WindowInfo *)clientData;
shellCmdInfo *cmdData = window->shellCmdData;
textBuffer *buf = TextGetBuffer(cmdData->textW);
int len;
char *outText;
if (cmdData->textW ==
NULL)
return;
outText = coalesceOutput(&cmdData->outBufs, &len);
if (len !=
0) {
if (BufSubstituteNullChars(outText, len, buf)) {
safeBufReplace(buf, &cmdData->leftPos, &cmdData->rightPos, outText);
TextSetCursorPos(cmdData->textW, cmdData->leftPos+strlen(outText));
cmdData->leftPos += len;
cmdData->rightPos = cmdData->leftPos;
}
else
fprintf(stderr,
"xnedit: Too much binary data\n");
}
NEditFree(outText);
cmdData->flushTimeoutID = XtAppAddTimeOut(
XtWidgetToApplicationContext(window->shell),
OUTPUT_FLUSH_FREQ, flushTimeoutProc, clientData);
}
static void finishCmdExecution(WindowInfo *window,
int terminatedOnError)
{
shellCmdInfo *cmdData = window->shellCmdData;
textBuffer *buf;
int status, failure, errorReport, reselectStart, outTextLen, errTextLen;
int resp, cancel = False, fromMacro = cmdData->fromMacro;
char *outText, *errText =
NULL;
if (cmdData->stdoutInputID !=
0)
XtRemoveInput(cmdData->stdoutInputID);
if (cmdData->stdinInputID !=
0)
XtRemoveInput(cmdData->stdinInputID);
if (cmdData->stderrInputID !=
0)
XtRemoveInput(cmdData->stderrInputID);
close(cmdData->stdoutFD);
if (cmdData->flags &
ERROR_DIALOGS)
close(cmdData->stderrFD);
if (cmdData->inPtr !=
NULL)
close(cmdData->stdinFD);
NEditFree(cmdData->input);
if (cmdData->flushTimeoutID !=
0)
XtRemoveTimeOut(cmdData->flushTimeoutID);
if (cmdData->bannerTimeoutID !=
0)
XtRemoveTimeOut(cmdData->bannerTimeoutID);
if (!cmdData->fromMacro) {
EndWait(window->shell);
SetSensitive(window, window->cancelShellItem, False);
if (cmdData->bannerIsUp)
ClearModeMessage(window);
}
if (terminatedOnError) {
freeBufList(&cmdData->outBufs);
freeBufList(&cmdData->errBufs);
waitpid(cmdData->childPid, &status,
0);
goto cmdDone;
}
outText = coalesceOutput(&cmdData->outBufs, &outTextLen);
if (cmdData->flags &
ERROR_DIALOGS)
errText = coalesceOutput(&cmdData->errBufs, &errTextLen);
waitpid(cmdData->childPid, &status,
0);
if (cmdData->flags &
ERROR_DIALOGS)
{
failure =
WIFEXITED(status) &&
WEXITSTATUS(status) !=
0;
errorReport = *errText !=
'\0';
if (failure && errorReport)
{
removeTrailingNewlines(errText);
truncateString(errText,
DF_MAX_MSG_LENGTH);
resp = DialogF(
DF_WARN, window->shell,
2,
"Warning",
"%s",
"Cancel",
"Proceed", errText);
cancel = resp ==
1;
}
else if (failure)
{
truncateString(outText,
DF_MAX_MSG_LENGTH-
70);
resp = DialogF(
DF_WARN, window->shell,
2,
"Command Failure",
"Command reported failed exit status.\n"
"Output from command:\n%s",
"Cancel",
"Proceed", outText);
cancel = resp ==
1;
}
else if (errorReport)
{
removeTrailingNewlines(errText);
truncateString(errText,
DF_MAX_MSG_LENGTH);
resp = DialogF(
DF_INF, window->shell,
2,
"Information",
"%s",
"Proceed",
"Cancel", errText);
cancel = resp ==
2;
}
NEditFree(errText);
if (cancel)
{
NEditFree(outText);
goto cmdDone;
}
}
if (cmdData->flags &
OUTPUT_TO_DIALOG) {
removeTrailingNewlines(outText);
if (*outText !=
'\0')
createOutputDialog(window->shell, outText);
}
else if (cmdData->flags &
OUTPUT_TO_STRING) {
ReturnShellCommandOutput(window,outText,
WEXITSTATUS(status));
}
else {
buf = TextGetBuffer(cmdData->textW);
if (!BufSubstituteNullChars(outText, outTextLen, buf)) {
fprintf(stderr,
"xnedit: Too much binary data in shell cmd output\n");
outText[
0] =
'\0';
}
if (cmdData->flags &
REPLACE_SELECTION) {
reselectStart = buf->primary.rectangular ? -
1 : buf->primary.start;
BufReplaceSelected(buf, outText);
TextSetCursorPos(cmdData->textW, buf->cursorPosHint);
if (reselectStart != -
1)
BufSelect(buf, reselectStart, reselectStart + strlen(outText));
}
else {
safeBufReplace(buf, &cmdData->leftPos, &cmdData->rightPos, outText);
TextSetCursorPos(cmdData->textW, cmdData->leftPos+strlen(outText));
}
}
if (cmdData->flags &
RELOAD_FILE_AFTER)
RevertToSaved(window,
NULL);
NEditFree(outText);
cmdDone:
NEditFree(cmdData);
window->shellCmdData =
NULL;
if (fromMacro)
ResumeMacroExecution(window);
}
static pid_t forkCommand(Widget parent,
const char *command,
const char *cmdDir,
int *stdinFD,
int *stdoutFD,
int *stderrFD)
{
int childStdoutFD, childStdinFD, childStderrFD, pipeFDs[
2];
int dupFD;
pid_t childPid;
signal(
SIGPIPE,
SIG_IGN);
if (pipe(pipeFDs) !=
0) {
perror(
"xnedit: Internal error (opening stdout pipe)");
return -
1;
}
*stdoutFD = pipeFDs[
0];
childStdoutFD = pipeFDs[
1];
if (pipe(pipeFDs) !=
0) {
perror(
"xnedit: Internal error (opening stdin pipe)");
return -
1;
}
*stdinFD = pipeFDs[
1];
childStdinFD = pipeFDs[
0];
if (stderrFD ==
NULL)
childStderrFD = childStdoutFD;
else {
if (pipe(pipeFDs) !=
0) {
perror(
"xnedit: Internal error (opening stdin pipe)");
return -
1;
}
*stderrFD = pipeFDs[
0];
childStderrFD = pipeFDs[
1];
}
#ifdef VMS
childPid = vfork();
#else
childPid = fork();
#endif
if (
0 == childPid) {
close(*stdinFD);
close(*stdoutFD);
if (stderrFD !=
NULL)
close(*stderrFD);
close(fileno(stdin));
close(fileno(stdout));
close(fileno(stderr));
dupFD = dup2(childStdinFD, fileno(stdin));
if (dupFD == -
1) {
perror(
"dup of stdin failed");
}
dupFD = dup2(childStdoutFD, fileno(stdout));
if (dupFD == -
1) {
perror(
"dup of stdout failed");
}
dupFD = dup2(childStderrFD, fileno(stderr));
if (dupFD == -
1) {
perror(
"dup of stderr failed");
}
close(childStdinFD);
close(childStdoutFD);
close(childStderrFD);
setsid();
if (cmdDir[
0] !=
0) {
if (chdir(cmdDir) == -
1) {
perror(
"chdir to directory of current file failed");
}
}
execlp(GetPrefShell(), GetPrefShell(),
"-c", command,
NULL);
fprintf(stderr,
"Error starting shell: %s\n", GetPrefShell());
exit(
EXIT_FAILURE);
}
if (childPid == -
1)
{
DialogF(
DF_ERR, parent,
1,
"Shell Command",
"Error starting shell command process\n(fork failed)",
"OK");
}
close(childStdinFD);
close(childStdoutFD);
if (stderrFD !=
NULL)
close(childStderrFD);
return childPid;
}
static void addOutput(buffer **bufList, buffer *buf)
{
buf->next = *bufList;
*bufList = buf;
}
static char *coalesceOutput(buffer **bufList,
int *outLength)
{
buffer *buf, *rBufList =
NULL;
char *outBuf, *outPtr, *p;
int i, length =
0;
for (buf=*bufList; buf!=
NULL; buf=buf->next)
length += buf->length;
outBuf = (
char*)NEditMalloc(length+
1);
while (*bufList !=
NULL) {
buf = *bufList;
*bufList = buf->next;
buf->next = rBufList;
rBufList = buf;
}
outPtr = outBuf;
for (buf=rBufList; buf!=
NULL; buf=buf->next) {
p = buf->contents;
for (i=
0; i<buf->length; i++)
*outPtr++ = *p++;
}
*outPtr =
'\0';
freeBufList(&rBufList);
*outLength = outPtr - outBuf;
return outBuf;
}
static void freeBufList(buffer **bufList)
{
buffer *buf;
while (*bufList !=
NULL) {
buf = *bufList;
*bufList = buf->next;
NEditFree(buf);
}
}
static void removeTrailingNewlines(
char *string)
{
char *endPtr = &string[strlen(string)-
1];
while (endPtr >= string && *endPtr ==
'\n')
*endPtr-- =
'\0';
}
static void createOutputDialog(Widget parent,
char *text)
{
Arg al[
50];
int ac, rows, cols, hasScrollBar, wrapped;
Widget form, textW, button;
XmString st1;
measureText(text,
MAX_OUT_DIALOG_COLS, &rows, &cols, &wrapped);
if (rows >
MAX_OUT_DIALOG_ROWS) {
rows =
MAX_OUT_DIALOG_ROWS;
hasScrollBar = True;
}
else
hasScrollBar = False;
if (cols >
MAX_OUT_DIALOG_COLS)
cols =
MAX_OUT_DIALOG_COLS;
if (cols ==
0)
cols =
1;
if (wrapped)
hasScrollBar = True;
ac =
0;
form = CreateFormDialog(parent,
"shellOutForm", al, ac);
ac =
0;
XtSetArg(al[ac], XmNlabelString, st1=
MKSTRING(
"OK")); ac++;
XtSetArg(al[ac], XmNmarginWidth,
BUTTON_WIDTH_MARGIN); ac++;
XtSetArg(al[ac], XmNhighlightThickness,
2); ac++;
XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
button = XmCreatePushButtonGadget(form,
"ok", al, ac);
XtManageChild(button);
XtVaSetValues(form, XmNdefaultButton, button,
NULL);
XtVaSetValues(form, XmNcancelButton, button,
NULL);
XmStringFree(st1);
XtAddCallback(button, XmNactivateCallback, destroyOutDialogCB,
XtParent(form));
ac =
0;
XtSetArg(al[ac], XmNrows, rows); ac++;
XtSetArg(al[ac], XmNcolumns, cols); ac++;
XtSetArg(al[ac], XmNresizeHeight, False); ac++;
XtSetArg(al[ac], XmNtraversalOn, True); ac++;
XtSetArg(al[ac], XmNwordWrap, True); ac++;
XtSetArg(al[ac], XmNscrollHorizontal, False); ac++;
XtSetArg(al[ac], XmNscrollVertical, hasScrollBar); ac++;
XtSetArg(al[ac], XmNhighlightThickness,
2); ac++;
XtSetArg(al[ac], XmNspacing,
0); ac++;
XtSetArg(al[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
XtSetArg(al[ac], XmNeditable, False); ac++;
XtSetArg(al[ac], XmNvalue, text); ac++;
XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
XtSetArg(al[ac], XmNbottomWidget, button); ac++;
textW = XmCreateScrolledText(form,
"outText", al, ac);
AddMouseWheelSupport(textW);
MakeSingleLineTextW(textW);
XtManageChild(textW);
XtVaSetValues(XtParent(form), XmNtitle,
"Output from Command",
NULL);
ManageDialogCenteredOnPointer(form);
#ifdef LESSTIF_VERSION
XmProcessTraversal(button, XmTRAVERSE_CURRENT);
#else
XmProcessTraversal(textW, XmTRAVERSE_CURRENT);
#endif
}
static void destroyOutDialogCB(Widget w, XtPointer callback, XtPointer closure)
{
XtDestroyWidget((Widget)callback);
}
static void measureText(
char *text,
int wrapWidth,
int *rows,
int *cols,
int *wrapped)
{
int maxCols =
0, line =
1, col =
0, wrapCol;
char *c;
*wrapped =
0;
for (c=text; *c!=
'\0'; c++) {
if (*c==
'\n') {
line++;
col =
0;
continue;
}
if (*c ==
'\t') {
col +=
8 - (col %
8);
wrapCol =
0;
}
else if (*c ==
' ') {
col++;
wrapCol =
0;
}
else {
col++;
wrapCol =
1;
}
if (col > wrapWidth) {
line++;
*wrapped =
1;
col = wrapCol;
}
else if (col > maxCols) {
maxCols = col;
}
}
*rows = line;
*cols = maxCols;
}
static void truncateString(
char *string,
int length)
{
if ((
int)strlen(string) > length)
memcpy(&string[length-
3],
"...",
4);
}
static int shellSubstituter(
char *outStr,
const char *inStr,
const char *fileStr,
const char *lineStr,
int outLen,
int predictOnly)
{
const char *inChar;
char *outChar =
NULL;
int outWritten =
0;
int fileLen, lineLen;
inChar = inStr;
if (!predictOnly) {
outChar = outStr;
}
fileLen = strlen(fileStr);
lineLen = strlen(lineStr);
while (*inChar !=
'\0') {
if (!predictOnly && outWritten >= outLen) {
return(-
1);
}
if (*inChar ==
'%') {
if (*(inChar +
1) ==
'%') {
inChar +=
2;
if (!predictOnly) {
*outChar++ =
'%';
}
outWritten++;
}
else {
if (!predictOnly) {
if (outWritten + fileLen >= outLen) {
return(-
1);
}
strncpy(outChar, fileStr, fileLen);
outChar += fileLen;
}
outWritten += fileLen;
inChar++;
}
}
else if (*inChar ==
'#') {
if (*(inChar +
1) ==
'#') {
inChar +=
2;
if (!predictOnly) {
*outChar++ =
'#';
}
outWritten++;
}
else {
if (!predictOnly) {
if (outWritten + lineLen >= outLen) {
return(-
1);
}
strncpy(outChar, lineStr, lineLen);
outChar += lineLen;
}
outWritten += lineLen;
inChar++;
}
}
else {
if (!predictOnly) {
*outChar++ = *inChar;
}
inChar++;
outWritten++;
}
}
if (!predictOnly) {
if (outWritten >= outLen) {
return(-
1);
}
*outChar =
'\0';
}
++outWritten;
return(outWritten);
}
static char *shellCommandSubstitutes(
const char *inStr,
const char *fileStr,
const char *lineStr)
{
int cmdLen;
char *subsCmdStr =
NULL;
cmdLen = shellSubstituter(
NULL, inStr, fileStr, lineStr,
0,
1);
if (cmdLen >=
0) {
subsCmdStr = (
char*)NEditMalloc(cmdLen);
if (subsCmdStr) {
cmdLen = shellSubstituter(subsCmdStr, inStr, fileStr, lineStr, cmdLen,
0);
if (cmdLen <
0) {
free(subsCmdStr);
subsCmdStr =
NULL;
}
}
}
return(subsCmdStr);
}