UNIXworkcode

/******************************************************************************* * * * Copyright 2004 The NEdit Developers * * * * This is free software; you can redistribute it and/or modify it under the * * terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. In addition, you may distribute versions of this program linked to * * Motif or Open Motif. See README for details. * * * * This software is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * software; if not, write to the Free Software Foundation, Inc., 59 Temple * * Place, Suite 330, Boston, MA 02111-1307 USA * * * *******************************************************************************/ #include "filter.h" #include "window.h" #include "nedit.h" #include "preferences.h" #include "../util/nedit_malloc.h" #include "../util/misc.h" #include "../util/managedList.h" #include "../util/DialogF.h" #include "../util/ec_glob.h" #include "help.h" #include <string.h> #include <ctype.h> #include <unistd.h> #include <errno.h> #include <pthread.h> #include <sys/wait.h> #include <Xm/XmAll.h> static IOFilter **filters; static size_t numFilters; typedef struct { Widget shell; Widget managedListW; Widget nameW; Widget patternW; Widget extW; Widget cmdInW; Widget cmdOutW; IOFilter **filters; int nfilters; int filteralloc; } filterDialog; static filterDialog fd = { NULL, NULL, NULL, NULL, NULL, NULL}; static char* create_ec_pattern(char *pattern); static void fdDestroyCB(Widget w, XtPointer clientData, XtPointer callData); static void fdOkCB(Widget w, XtPointer clientData, XtPointer callData); static void fdApplyCB(Widget w, XtPointer clientData, XtPointer callData); static void fdCloseCB(Widget w, XtPointer clientData, XtPointer callData); static void fdHelpCB(Widget w, XtPointer clientData, XtPointer callData); static int fdUpdateList(void); static int fdIsEmpty(void) { Widget w[] = { fd.nameW, fd.patternW, fd.extW, fd.cmdInW, fd.cmdOutW }; size_t n = sizeof(w) / sizeof(Widget); for(int i=0;i<n;i++) { char *s = XmTextGetString(w[i]); size_t len = s ? strlen(s) : 0; XtFree(s); if(len > 0) { return 0; } } return 1; } static void fdFreeItemCB(void *item) { IOFilter *f = item; XtFree(f->name); XtFree(f->pattern); XtFree(f->ext); XtFree(f->cmdin); XtFree(f->cmdout); NEditFree(f->ec_pattern); NEditFree(f); } static void *fdGetDisplayedCB(void *oldItem, int explicitRequest, int *abort, void *cbArg) { if(fdIsEmpty()) { return NULL; } IOFilter *filter = NEditMalloc(sizeof(IOFilter)); filter->name = XmTextGetString(fd.nameW); filter->pattern = XmTextGetString(fd.patternW); filter->ext = XmTextGetString(fd.extW); filter->cmdin = XmTextGetString(fd.cmdInW); filter->cmdout = XmTextGetString(fd.cmdOutW); filter->ec_pattern = create_ec_pattern(filter->pattern); if (strlen(filter->name) == 0 || strlen(filter->pattern) == 0 || strlen(filter->ext) == 0 || strlen(filter->cmdin) == 0 || strlen(filter->cmdout) == 0 || fd.nfilters == fd.filteralloc) { fdFreeItemCB(filter); *abort = 1; } return filter; } static void fdSetDisplayedCB(void *item, void *cbArg) { if(!item) { XmTextSetString(fd.nameW, ""); XmTextSetString(fd.patternW, ""); XmTextSetString(fd.extW, ""); XmTextSetString(fd.cmdInW, ""); XmTextSetString(fd.cmdOutW, ""); } else { IOFilter *f = item; XmTextSetString(fd.nameW, f->name); XmTextSetString(fd.patternW, f->pattern); XmTextSetString(fd.extW, f->ext); XmTextSetString(fd.cmdInW, f->cmdin); XmTextSetString(fd.cmdOutW, f->cmdout); } } static IOFilter* fdCopyFilter(IOFilter *f) { IOFilter *cp = NEditMalloc(sizeof(IOFilter)); cp->name = NEditStrdup(f->name ? f->name : ""); cp->pattern = NEditStrdup(f->pattern ? f->pattern : ""); cp->ext = NEditStrdup(f->ext ? f->ext : ""); cp->cmdin = NEditStrdup(f->cmdin ? f->cmdin : ""); cp->cmdout = NEditStrdup(f->cmdout ? f->cmdout : ""); cp->ec_pattern = f->ec_pattern ? NEditStrdup(f->ec_pattern) : NULL; return cp; } #define FD_LIST_RIGHT 30 #define FD_LEFT_MARGIN_POS 1 #define FD_RIGHT_MARGIN_POS 99 #define FD_H_MARGIN 10 void FilterSettings(WindowInfo *window) { XmString s1; if(fd.shell) { RaiseDialogWindow(fd.shell); return; } fd.filteralloc = 256; fd.filters = NEditCalloc(sizeof(IOFilter), fd.filteralloc); fd.nfilters = numFilters; for(int i=0;i<numFilters;i++) { fd.filters[i] = fdCopyFilter(filters[i]); } int ac = 0; Arg args[20]; //XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++; XtSetArg(args[ac], XmNtitle, "Filters"); ac++; fd.shell = CreateWidget(TheAppShell, "filters", topLevelShellWidgetClass, args, ac); AddSmallIcon(fd.shell); Widget form = XtVaCreateManagedWidget("filtersForm", xmFormWidgetClass, fd.shell, XmNautoUnmanage, False, XmNresizePolicy, XmRESIZE_NONE, NULL); Widget topLbl = XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form, XmNlabelString, s1=MKSTRING( "To modify the properties of an existing filter, select the name\n\ from the list on the left. Select \"New\" to add a new filter to the list."), XmNmnemonic, 'N', XmNtopAttachment, XmATTACH_POSITION, XmNtopPosition, 2, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 6, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 6, NULL); XmStringFree(s1); Widget okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form, XmNlabelString, s1=XmStringCreateSimple("OK"), XmNmarginWidth, BUTTON_WIDTH_MARGIN, XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, 4, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 21, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 6, NULL); XtAddCallback(okBtn, XmNactivateCallback, fdOkCB, NULL); XmStringFree(s1); Widget applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form, XmNlabelString, s1=XmStringCreateSimple("Apply"), XmNmnemonic, 'A', XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, 29, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 46, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 6, NULL); XtAddCallback(applyBtn, XmNactivateCallback, fdApplyCB, NULL); XmStringFree(s1); Widget closeBtn = XtVaCreateManagedWidget("close", xmPushButtonWidgetClass, form, XmNlabelString, s1=XmStringCreateSimple("Close"), XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, 54, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 71, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 6, NULL); XtAddCallback(closeBtn, XmNactivateCallback, fdCloseCB, NULL); XmStringFree(s1); Widget helpBtn = XtVaCreateManagedWidget("help", xmPushButtonWidgetClass, form, XmNlabelString, s1=XmStringCreateSimple("Help"), XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, 79, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 96, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 6, NULL); XtAddCallback(helpBtn, XmNactivateCallback, fdHelpCB, NULL); XmStringFree(s1); Widget sep1 = XtVaCreateManagedWidget("sep1", xmSeparatorGadgetClass, form, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNleftOffset, 6, XmNrightOffset, 6, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, okBtn, XmNbottomOffset, 4, NULL); ac = 0; XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; XtSetArg(args[ac], XmNtopOffset, FD_H_MARGIN); ac++; XtSetArg(args[ac], XmNtopWidget, topLbl); ac++; XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++; XtSetArg(args[ac], XmNleftPosition, FD_LEFT_MARGIN_POS); ac++; XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++; XtSetArg(args[ac], XmNrightPosition, FD_LIST_RIGHT-1); ac++; XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; XtSetArg(args[ac], XmNbottomWidget, sep1); ac++; XtSetArg(args[ac], XmNbottomOffset, FD_H_MARGIN); ac++; fd.managedListW = CreateManagedList(form, "list", args, ac, (void **)fd.filters, &fd.nfilters, 256, 20, fdGetDisplayedCB, NULL, fdSetDisplayedCB, form, fdFreeItemCB); s1 = XmStringCreateLocalized("Name"); Widget lblName = XtVaCreateManagedWidget("nameLbl", xmLabelGadgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, topLbl, XmNtopOffset, FD_H_MARGIN, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, fd.managedListW, XmNrightAttachment, XmATTACH_FORM, XmNleftOffset, 6, XmNrightOffset, 6, XmNlabelString, s1, XmNalignment, XmALIGNMENT_BEGINNING, NULL); XmStringFree(s1); fd.nameW = XtVaCreateManagedWidget("patternLbl", xmTextWidgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, lblName, XmNtopOffset, 6, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, fd.managedListW, XmNrightAttachment, XmATTACH_FORM, XmNleftOffset, 6, XmNrightOffset, 6, NULL); s1 = XmStringCreateLocalized("File Pattern"); Widget lblPattern = XtVaCreateManagedWidget("nameLbl", xmLabelGadgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, fd.nameW, XmNtopOffset, 6, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, fd.managedListW, //XmNrightAttachment, XmATTACH_POSITION, //XmNrightPosition, 75, XmNleftOffset, 6, XmNlabelString, s1, XmNalignment, XmALIGNMENT_BEGINNING, NULL); XmStringFree(s1); fd.patternW = XtVaCreateManagedWidget("patternLbl", xmTextWidgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, lblPattern, XmNtopOffset, 6, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, fd.managedListW, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 75, XmNleftOffset, 6, NULL); s1 = XmStringCreateLocalized("Default Extension"); Widget lblExt = XtVaCreateManagedWidget("extLbl", xmLabelGadgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, fd.nameW, XmNtopOffset, 6, XmNrightAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, fd.patternW, XmNleftOffset, 6, XmNleftPosition, 76, XmNrightOffset, 6, XmNlabelString, s1, XmNalignment, XmALIGNMENT_BEGINNING, NULL); XmStringFree(s1); fd.extW = XtVaCreateManagedWidget("patternLbl", xmTextWidgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, lblExt, XmNtopOffset, 6, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, fd.patternW, XmNleftOffset, 6, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 6, NULL); s1 = XmStringCreateLocalized("Input Filter Command"); Widget lblCmdIn = XtVaCreateManagedWidget("cmdInLbl", xmLabelGadgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, fd.patternW, XmNtopOffset, 6, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, fd.managedListW, XmNrightAttachment, XmATTACH_FORM, XmNleftOffset, 6, XmNrightOffset, 6, XmNlabelString, s1, XmNalignment, XmALIGNMENT_BEGINNING, NULL); XmStringFree(s1); ac = 0; XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; XtSetArg(args[ac], XmNtopWidget, lblCmdIn); ac++; XtSetArg(args[ac], XmNtopOffset, 6); ac++; XtSetArg(args[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; XtSetArg(args[ac], XmNleftWidget, fd.managedListW); ac++; XtSetArg(args[ac], XmNleftOffset, 6); ac++; XtSetArg(args[ac], XmNrightAttachment, XmATTACH_FORM); ac++; XtSetArg(args[ac], XmNrightOffset, 6); ac++; XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++; XtSetArg(args[ac], XmNbottomPosition, 61); ac++; XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++; XtSetArg(args[ac], XmNrows, 4); ac++; fd.cmdInW = XmCreateScrolledText(form, "cmdInText", args, ac); XtManageChild(fd.cmdInW); s1 = XmStringCreateLocalized("Output Filter Command"); Widget lblCmdOut = XtVaCreateManagedWidget("cmdOutLbl", xmLabelGadgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, fd.cmdInW, XmNtopOffset, FD_H_MARGIN, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, fd.managedListW, XmNrightAttachment, XmATTACH_FORM, XmNleftOffset, 6, XmNrightOffset, 6, XmNlabelString, s1, XmNalignment, XmALIGNMENT_BEGINNING, NULL); XmStringFree(s1); ac = 0; XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; XtSetArg(args[ac], XmNtopWidget, lblCmdOut); ac++; XtSetArg(args[ac], XmNtopOffset, 6); ac++; XtSetArg(args[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; XtSetArg(args[ac], XmNleftWidget, fd.managedListW); ac++; XtSetArg(args[ac], XmNleftOffset, 6); ac++; XtSetArg(args[ac], XmNrightAttachment, XmATTACH_FORM); ac++; XtSetArg(args[ac], XmNrightOffset, 6); ac++; XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; XtSetArg(args[ac], XmNbottomWidget, sep1); ac++; XtSetArg(args[ac], XmNbottomOffset, FD_H_MARGIN); ac++; XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++; XtSetArg(args[ac], XmNrows, 4); ac++; fd.cmdOutW = XmCreateScrolledText(form, "cmdInText", args, ac); XtManageChild(fd.cmdOutW); /* Set initial default button */ XtVaSetValues(form, XmNdefaultButton, okBtn, NULL); XtVaSetValues(form, XmNcancelButton, closeBtn, NULL); XtAddCallback(form, XmNdestroyCallback, fdDestroyCB, NULL); AddMotifCloseCallback(fd.shell, fdCloseCB, NULL); RealizeWithoutForcingPosition(fd.shell); } static void fdDestroyCB(Widget w, XtPointer clientData, XtPointer callData) { for(int i=0;i<fd.nfilters;i++) { fdFreeItemCB(fd.filters[i]); } NEditFree(fd.filters); } static void fdApplyCB(Widget w, XtPointer clientData, XtPointer callData) { fdUpdateList(); } static void fdOkCB(Widget w, XtPointer clientData, XtPointer callData) { if(!fdUpdateList()) { return; } XtDestroyWidget(fd.shell); fd.shell = NULL; } static void fdCloseCB(Widget w, XtPointer clientData, XtPointer callData) { XtDestroyWidget(fd.shell); fd.shell = NULL; } static void fdHelpCB(Widget w, XtPointer clientData, XtPointer callData) { Help(HELP_FILTERS); } static int fdUpdateList(void) { if (!UpdateManagedList(fd.managedListW, True)) return False; for(int i=0;i<numFilters;i++) { fdFreeItemCB(filters[i]); } NEditFree(filters); filters = NEditCalloc(fd.nfilters, sizeof(IOFilter*)); numFilters = fd.nfilters; for(int i=0;i<numFilters;i++) { filters[i] = fdCopyFilter(fd.filters[i]); } /* Note that preferences have been changed */ MarkPrefsChanged(); return True; } static int valueListNext(char *str, int len) { for(int i=0;i<len;i++) { if(str[i] == ';') { str[i] = '\0'; len = i; break; } } return len; } static char* create_ec_pattern(char *pattern) { if(!pattern) { return NULL; } char *ecPattern = NULL; if(pattern[0] == '/') { ecPattern = NEditStrdup(pattern); } else { // add **/ size_t len = strlen(pattern); int newlen = len+3; ecPattern = NEditMalloc(newlen+1); memcpy(ecPattern, "**/", 3); memcpy(ecPattern+3, pattern, len); ecPattern[newlen] = 0; } return ecPattern; } static IOFilter* ParseFilterStr(char *str, int len) { char *name = str; int namelen = valueListNext(str, len); int skip_space = 0; while(isspace(*name) && namelen > 0) { name++; namelen--; skip_space++; } if(namelen <= 0) { return NULL; } str += skip_space + namelen + 1; len -= skip_space + namelen + 1; char *pattern = str; int patternlen = valueListNext(str, len); if(patternlen <= 0) { return NULL; } str += patternlen + 1; len -= patternlen + 1; char *ext = str; int extlen = valueListNext(str, len); if(extlen <= 0) { return NULL; } str += extlen + 1; len -= extlen + 1; char *cmdin = str; int cmdinlen = valueListNext(str, len); if(cmdinlen <= 0) { return NULL; } str += cmdinlen + 1; len -= cmdinlen + 1; char *cmdout = str; int cmdoutlen = valueListNext(str, len); if(cmdoutlen <= 0) { return NULL; } //str += cmdoutlen + 1; //len -= cmdoutlen + 1; IOFilter *filter = NEditMalloc(sizeof(IOFilter)); filter->name = NEditStrndup(name, namelen); filter->pattern = NEditStrndup(pattern, patternlen); filter->ext = NEditStrndup(ext, extlen); filter->cmdin = NEditStrndup(cmdin, cmdinlen); filter->cmdout = NEditStrndup(cmdout, cmdoutlen); filter->ec_pattern = create_ec_pattern(filter->pattern); return filter; } void ParseFilterSettings(char *str) { size_t filterAlloc = 8; filters = NEditCalloc(filterAlloc, sizeof(IOFilter*)); numFilters = 0; size_t len = strlen(str); int lineStart = 0; for(int i=0;i<=len;i++) { if(i == len || str[i] == '\n') { IOFilter *filter = ParseFilterStr(str+lineStart, i-lineStart); lineStart = i+1; if(filter) { if(numFilters == filterAlloc) { filterAlloc += 8; NEditRealloc(filters, filterAlloc * sizeof(IOFilter*)); } filters[numFilters] = filter; numFilters++; } } } } char* WriteFilterString(void) { size_t str_alloc = 2048; size_t len = 0; char *str = NEditMalloc(str_alloc); str[0] = 0; for(int i=0;i<numFilters;i++) { IOFilter *filter = filters[i]; char *prefix = i > 0 ? "\t" : ""; char *suffix = i+1 != numFilters ? "\\n\\\n" : ""; for(;;) { size_t remaining = str_alloc - len; size_t w = snprintf( str + len, remaining, "%s%s;%s;%s;%s;%s%s", prefix, filter->name, filter->pattern, filter->ext, filter->cmdin, filter->cmdout, suffix); if(w < remaining) { len += w; break; } else { str_alloc += 1024; str = NEditRealloc(str, str_alloc); } } } return str; } IOFilter** GetFilterList(size_t *num) { *num = numFilters; return filters; } IOFilter* GetFilterFromName(const char *name) { if(!name) { return NULL; } for(int i=0;i<numFilters;i++) { if(!strcmp(filters[i]->name, name)) { return filters[i]; } } return NULL; } IOFilter* GetFilterForPath(const char *path) { IOFilter *filter = NULL; for(int i=0;i<numFilters;i++) { if(!ec_glob(filters[i]->ec_pattern, path)) { filter = filters[i]; break; } } return filter; } /* ----------------------------- FileStream -----------------------------*/ typedef struct FilterIOThreadData { XtAppContext appcontext; Widget widget; FILE *file; int fd_in; int fd_out; pid_t pid; } FilterIOThreadData; typedef struct FilterCmdError { Widget w; int status; int io_errno; } FilterCmdError; static void filter_command_error(XtPointer clientData, XtIntervalId *id) { FilterCmdError *error = clientData; // The widget error->w should be in the WindowList // if it is not in the list, it could already be destroyed Widget w = NULL; for (WindowInfo *win=WindowList; win!=NULL; win=win->next) { if(win->shell == error->w) { w = error->w; break; } } if(w) { if(error->io_errno != 0) { (void)DialogF(DF_WARN, error->w, 1, "Command Failure", "Filter IO Error: %s\n", "OK", strerror(error->io_errno)); } else { (void)DialogF(DF_WARN, error->w, 1, "Command Failure", "Filter command reported failed exit status: %d\n", "OK", error->status); } } NEditFree(error); } static void* file_input_thread(void *data) { FilterIOThreadData *stream = data; int ioerror = 0; int io_errno = 0; char buf[16384]; size_t r; while((r = fread(buf, 1, 16384, stream->file)) > 0) { char *wbuf = buf; while(r > 0) { ssize_t w = write(stream->fd_in, wbuf, r); if(w <= 0) { ioerror = 1; io_errno = errno; break; } r -= w; wbuf += w; } if(ioerror) { break; } } close(stream->fd_in); int status = -1; waitpid(stream->pid, &status, 0); if(status != 0 || ioerror) { FilterCmdError *error = NEditMalloc(sizeof(FilterCmdError)); error->w = stream->widget; error->status = status; error->io_errno = io_errno; XtAppAddTimeOut( stream->appcontext, 0, filter_command_error, error); } NEditFree(stream); return NULL; } #define FILTER_IO_BUFSIZE 16384 static void* file_output_thread(void *data) { FilterIOThreadData *stream = data; char buf[FILTER_IO_BUFSIZE]; ssize_t r; while((r = read(stream->fd_out, buf, FILTER_IO_BUFSIZE)) > 0) { fwrite(buf, 1, r, stream->file); } close(stream->fd_out); fclose(stream->file); int status = -1; waitpid(stream->pid, &status, 0); if(status != 0) { FilterCmdError *error = NEditMalloc(sizeof(FilterCmdError)); error->w = stream->widget; error->status = status; XtAppAddTimeOut( stream->appcontext, 0, filter_command_error, error); } NEditFree(stream); return NULL; } static int filestream_create_pipes(FileStream *stream) { if(pipe(stream->pin)) { return 1; } if(pipe(stream->pout)) { close(stream->pin[0]); close(stream->pin[1]); return 1; } return 0; } static FileStream* filestream_open(Widget w, FILE *f, const char *filter_cmd, int mode) { FileStream *stream = NEditMalloc(sizeof(FileStream)); stream->file = f; stream->filter_cmd = filter_cmd ? NEditStrdup(filter_cmd) : NULL; stream->pid = 0; stream->hdrbufpos = 0; stream->hdrbuflen = 0; stream->mode = mode; if(filter_cmd) { if(filestream_create_pipes(stream)) { NEditFree(stream->filter_cmd); NEditFree(stream); fclose(f); fprintf(stderr, "Failed to create pipe: %s\n", strerror(errno)); return NULL; } pid_t child = fork(); if(child == 0) { close(STDIN_FILENO); close(STDOUT_FILENO); // we need stdin, stdout and stderr refer to the previously // created pipes if(dup2(stream->pin[0], STDIN_FILENO) == -1) { perror("dup2"); exit(1); } if(dup2(stream->pout[1], STDOUT_FILENO) == -1) { perror("dup2"); exit(1); } close(stream->pin[1]); // execute the command using the shell specified by preferences //fprintf(stderr, "info: input filter command: %s\n", filter_cmd); execlp(GetPrefShell(), GetPrefShell(), "-c", filter_cmd, NULL); // execlp only returns if an error occured fprintf(stderr, "Error starting shell: %s\n", GetPrefShell()); exit(1); } else { stream->pid = child; FilterIOThreadData *data = NEditMalloc(sizeof(FilterIOThreadData)); data->appcontext = XtWidgetToApplicationContext(w); data->widget = w; data->file = stream->file; data->fd_in = stream->pin[1]; data->fd_out = stream->pout[0]; data->pid = child; pthread_t tid; if(pthread_create(&tid, NULL, mode == 0 ? file_input_thread : file_output_thread, data)) { fprintf(stderr, "Errro: cannot create file input thread: %s\n", strerror(errno)); NEditFree(data); close(stream->pin[0]); close(stream->pin[1]); close(stream->pout[0]); close(stream->pout[1]); fclose(f); NEditFree(stream->filter_cmd); NEditFree(stream); return NULL; } if(mode == 1) { stream->file = NULL; // file will be closed by file_output_thread } close(stream->pout[1]); } } return stream; } FileStream* filestream_open_r(Widget w, FILE *f, const char *filter_cmd) { return filestream_open(w, f, filter_cmd, 0); } FileStream* filestream_open_w(Widget w, FILE *f, const char *filter_cmd) { return filestream_open(w, f, filter_cmd, 1); } int filestream_reset(FileStream *stream, int pos) { if(stream->pid == 0) { fseek(stream->file, pos, SEEK_SET); } else { if(pos > stream->hdrbuflen || stream->hdrbufpos > stream->hdrbuflen) { return 1; } stream->hdrbufpos = pos; } return 0; } size_t filestream_read(void *buffer, size_t nbytes, FileStream *stream) { if(stream->pid == 0) { return fread(buffer, 1, nbytes, stream->file); } else { if(stream->hdrbufpos < stream->hdrbuflen) { // get bytes from hdrbuf before reading more bytes from the pipe size_t r = stream->hdrbuflen - stream->hdrbufpos; if(r > nbytes) { r = nbytes; } memcpy(buffer, stream->hdrbuf, r); nbytes -= r; buffer = ((char*)buffer) + r; stream->hdrbufpos += r; if(nbytes > 0) { r += filestream_read(buffer, nbytes, stream); } return r; } ssize_t sr = read(stream->pout[0], buffer, nbytes); //fwrite(buffer, 1, sr, stdout); //fflush(stdout); if(sr < 0) { return 0; } // the first bytes we read from the pipe are stored in hdrbuf // because we may want to reset the stream if(stream->hdrbuflen < FILESTREAM_HDR_BUFLEN) { size_t buflen = FILESTREAM_HDR_BUFLEN - stream->hdrbuflen; if(buflen > sr) { buflen = sr; } memcpy(stream->hdrbuf + stream->hdrbuflen, buffer, buflen); stream->hdrbuflen += buflen; } stream->hdrbufpos += sr; return (size_t)sr; } } size_t filestream_write(const void *buffer, size_t nbytes, FileStream *stream) { if(stream->pid == 0) { return fwrite(buffer, 1, nbytes, stream->file); } else { ssize_t w = write(stream->pin[1], buffer, nbytes); if(w < 0) { w = 0; } return w; } } int filestream_close(FileStream *stream) { if(stream->pid != 0) { if(stream->mode == 0) { if(close(stream->pin[0])) { perror("pipe pin[0] close"); } if(close(stream->pout[0])) { perror("pipe pout[0] close"); } } else { if(close(stream->pin[1])) { perror("pipe pin[1] close"); } } } int err = 0; if(stream->file) { err = fclose(stream->file); } NEditFree(stream->filter_cmd); NEditFree(stream); return err; }