?? shell.c
字號:
cmdDone: XtFree((char *)cmdData); window->shellCmdData = NULL; if (fromMacro) ResumeMacroExecution(window);}/*** Fork a subprocess to execute a command, return file descriptors for pipes** connected to the subprocess' stdin, stdout, and stderr streams. cmdDir** sets the default directory for the subprocess. If stderrFD is passed as** NULL, the pipe represented by stdoutFD is connected to both stdin and** stderr. The function value returns the pid of the new subprocess, or -1** if an error occured.*/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; /* Ignore SIGPIPE signals generated when user attempts to provide input for commands which don't take input */ signal(SIGPIPE, SIG_IGN); /* Create pipes to communicate with the sub process. One end of each is returned to the caller, the other half is spliced to stdin, stdout and stderr in the child process */ if (pipe(pipeFDs) != 0) { perror("NEdit: Internal error (opening stdout pipe)"); return -1; } *stdoutFD = pipeFDs[0]; childStdoutFD = pipeFDs[1]; if (pipe(pipeFDs) != 0) { perror("NEdit: Internal error (opening stdin pipe)"); return -1; } *stdinFD = pipeFDs[1]; childStdinFD = pipeFDs[0]; if (stderrFD == NULL) childStderrFD = childStdoutFD; else { if (pipe(pipeFDs) != 0) { perror("NEdit: Internal error (opening stdin pipe)"); return -1; } *stderrFD = pipeFDs[0]; childStderrFD = pipeFDs[1]; } /* Fork the process */ childPid = fork(); /* ** Child process context (fork returned 0), clean up the ** child ends of the pipes and execute the command */ if (0 == childPid) { /* close the parent end of the pipes in the child process */ close(*stdinFD); close(*stdoutFD); if (stderrFD != NULL) close(*stderrFD); /* close current stdin, stdout, and stderr file descriptors before substituting pipes */ close(fileno(stdin)); close(fileno(stdout)); close(fileno(stderr)); /* duplicate the child ends of the pipes to have the same numbers as stdout & stderr, so it can substitute for stdout & 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"); /* make this process the leader of a new process group, so the sub processes can be killed, if necessary, with a killpg call */#ifndef __EMX__ /* OS/2 doesn't have this */ setsid();#endif /* change the current working directory to the directory of the current file. */ if(cmdDir[0] != 0) if(chdir(cmdDir) == -1) perror("chdir to directory of current file failed"); /* execute the command using the shell specified by preferences */ execl(GetPrefShell(), GetPrefShell(), "-c", command, (char *)0); /* if we reach here, execl failed */ fprintf(stderr, "Error starting shell: %s\n", GetPrefShell()); exit(EXIT_FAILURE); } /* Parent process context, check if fork succeeded */ if (childPid == -1) { DialogF(DF_ERR, parent, 1, "Shell Command", "Error starting shell command process\n(fork failed)", "Dismiss"); } /* close the child ends of the pipes */ close(childStdinFD); close(childStdoutFD); if (stderrFD != NULL) close(childStderrFD); return childPid;} /*** Add a buffer full of output to a buffer list*/static void addOutput(buffer **bufList, buffer *buf){ buf->next = *bufList; *bufList = buf;}/*** coalesce the contents of a list of buffers into a contiguous memory block,** freeing the memory occupied by the buffer list. Returns the memory block** as the function result, and its length as parameter "length".*/static char *coalesceOutput(buffer **bufList, int *outLength){ buffer *buf, *rBufList = NULL; char *outBuf, *outPtr, *p; int i, length = 0; /* find the total length of data read */ for (buf=*bufList; buf!=NULL; buf=buf->next) length += buf->length; /* allocate contiguous memory for returning data */ outBuf = XtMalloc(length+1); /* reverse the buffer list */ while (*bufList != NULL) { buf = *bufList; *bufList = buf->next; buf->next = rBufList; rBufList = buf; } /* copy the buffers into the output buffer */ outPtr = outBuf; for (buf=rBufList; buf!=NULL; buf=buf->next) { p = buf->contents; for (i=0; i<buf->length; i++) *outPtr++ = *p++; } /* terminate with a null */ *outPtr = '\0'; /* free the buffer list */ freeBufList(&rBufList); *outLength = outPtr - outBuf; return outBuf;}static void freeBufList(buffer **bufList){ buffer *buf; while (*bufList != NULL) { buf = *bufList; *bufList = buf->next; XtFree((char *)buf); }}/*** Remove trailing newlines from a string by substituting nulls*/static void removeTrailingNewlines(char *string){ char *endPtr = &string[strlen(string)-1]; while (endPtr >= string && *endPtr == '\n') *endPtr-- = '\0';}/*** Create a dialog for the output of a shell command. The dialog lives until** the user presses the Dismiss button, and is then destroyed*/static void createOutputDialog(Widget parent, char *text){ Arg al[50]; int ac, rows, cols, hasScrollBar, wrapped; Widget form, textW, button; XmString st1; /* measure the width and height of the text to determine size for dialog */ 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; /* Without completely emulating Motif's wrapping algorithm, we can't be sure that we haven't underestimated the number of lines in case a line has wrapped, so let's assume that some lines could be obscured */ if (wrapped) hasScrollBar = True; ac = 0; form = CreateFormDialog(parent, "shellOutForm", al, ac); ac = 0; XtSetArg(al[ac], XmNlabelString, st1=MKSTRING("Dismiss")); ac++; XtSetArg(al[ac], XmNhighlightThickness, 0); ac++; XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; button = XmCreatePushButtonGadget(form, "dismiss", al, ac); XtManageChild(button); XtVaSetValues(form, XmNdefaultButton, 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, False); ac++; XtSetArg(al[ac], XmNwordWrap, True); ac++; XtSetArg(al[ac], XmNscrollHorizontal, False); ac++; XtSetArg(al[ac], XmNscrollVertical, hasScrollBar); ac++; XtSetArg(al[ac], XmNhighlightThickness, 0); 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); XtManageChild(textW); XtVaSetValues(XtParent(form), XmNtitle, "Output from Command", NULL); ManageDialogCenteredOnPointer(form);}/*** Dispose of the command output dialog when user presses Dismiss button*/static void destroyOutDialogCB(Widget w, XtPointer callback, XtPointer closure){ XtDestroyWidget((Widget)callback);}/*** Measure the width and height of a string of text. Assumes 8 character** tabs. wrapWidth specifies a number of columns at which text wraps.*/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; /* Tabs at end of line are not drawn when wrapped */ } else if (*c == ' ') { col++; wrapCol = 0; /* Spaces at end of line are not drawn when wrapped */ } else { col++; wrapCol = 1; } /* Note: there is a small chance that the number of lines is over-estimated when a line ends with a space or a tab (ie, followed by a newline) and that whitespace crosses the boundary, because whitespace at the end of a line does not cause wrapping. Taking this into account is very hard, but an over-estimation is harmless. The worst that can happen is that some extra blank lines are shown at the end of the dialog (in contrast to an under-estimation, which could make the last lines invisible). On the other hand, without emulating Motif's wrapping algorithm completely, we can't be sure that we don't underestimate the number of lines (Motif uses word wrap, and this counting algorithm uses character wrap). Therefore, we remember whether there is a line that has wrapped. In that case we allways install a scroll bar. */ if (col > wrapWidth) { line++; *wrapped = 1; col = wrapCol; } else if (col > maxCols) { maxCols = col; } } *rows = line; *cols = maxCols;}/*** Truncate a string to a maximum of length characters. If it shortens the** string, it appends "..." to show that it has been shortened. It assumes** that the string that it is passed is writeable.*/static void truncateString(char *string, int length){ if ((int)strlen(string) > length) memcpy(&string[length-3], "...", 4);}/*** Substitute the string fileStr in inStr wherever % appears and** lineStr in inStr wherever # appears, storing the** result in outStr. If predictOnly is non-zero, the result string length** is predicted without creating the string. Returns the length of the result** string or -1 in case of an error.***/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 = malloc(cmdLen); if (subsCmdStr) { cmdLen = shellSubstituter(subsCmdStr, inStr, fileStr, lineStr, cmdLen, 0); if (cmdLen < 0) { free(subsCmdStr); subsCmdStr = NULL; } } } return(subsCmdStr);}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -