UNIXworkcode

1 /******************************************************************************* 2 * * 3 * Copyright 2004 The NEdit Developers * 4 * * 5 * This is free software; you can redistribute it and/or modify it under the * 6 * terms of the GNU General Public License as published by the Free Software * 7 * Foundation; either version 2 of the License, or (at your option) any later * 8 * version. In addition, you may distribute versions of this program linked to * 9 * Motif or Open Motif. See README for details. * 10 * * 11 * This software is distributed in the hope that it will be useful, but WITHOUT * 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 14 * more details. * 15 * * 16 * You should have received a copy of the GNU General Public License along with * 17 * software; if not, write to the Free Software Foundation, Inc., 59 Temple * 18 * Place, Suite 330, Boston, MA 02111-1307 USA * 19 * * 20 *******************************************************************************/ 21 22 #include "filter.h" 23 #include "window.h" 24 #include "nedit.h" 25 #include "preferences.h" 26 27 #include "../util/nedit_malloc.h" 28 #include "../util/misc.h" 29 #include "../util/managedList.h" 30 #include "../util/DialogF.h" 31 #include "../util/ec_glob.h" 32 33 #include "help.h" 34 35 #include <string.h> 36 #include <ctype.h> 37 #include <unistd.h> 38 #include <errno.h> 39 #include <pthread.h> 40 #include <sys/wait.h> 41 #include <Xm/XmAll.h> 42 43 static IOFilter **filters; 44 static size_t numFilters; 45 46 47 typedef struct { 48 Widget shell; 49 Widget managedListW; 50 Widget nameW; 51 Widget patternW; 52 Widget extW; 53 Widget cmdInW; 54 Widget cmdOutW; 55 56 IOFilter **filters; 57 int nfilters; 58 int filteralloc; 59 } filterDialog; 60 61 static filterDialog fd = { NULL, NULL, NULL, NULL, NULL, NULL}; 62 63 static char* create_ec_pattern(char *pattern); 64 65 static void fdDestroyCB(Widget w, XtPointer clientData, XtPointer callData); 66 static void fdOkCB(Widget w, XtPointer clientData, XtPointer callData); 67 static void fdApplyCB(Widget w, XtPointer clientData, XtPointer callData); 68 static void fdCloseCB(Widget w, XtPointer clientData, XtPointer callData); 69 static void fdHelpCB(Widget w, XtPointer clientData, XtPointer callData); 70 71 static int fdUpdateList(void); 72 73 74 static int fdIsEmpty(void) 75 { 76 Widget w[] = { fd.nameW, fd.patternW, fd.extW, fd.cmdInW, fd.cmdOutW }; 77 size_t n = sizeof(w) / sizeof(Widget); 78 for(int i=0;i<n;i++) { 79 char *s = XmTextGetString(w[i]); 80 size_t len = s ? strlen(s) : 0; 81 XtFree(s); 82 if(len > 0) { 83 return 0; 84 } 85 } 86 return 1; 87 } 88 89 static void fdFreeItemCB(void *item) 90 { 91 IOFilter *f = item; 92 XtFree(f->name); 93 XtFree(f->pattern); 94 XtFree(f->ext); 95 XtFree(f->cmdin); 96 XtFree(f->cmdout); 97 NEditFree(f->ec_pattern); 98 NEditFree(f); 99 } 100 101 static void *fdGetDisplayedCB(void *oldItem, int explicitRequest, int *abort, 102 void *cbArg) 103 { 104 if(fdIsEmpty()) { 105 return NULL; 106 } 107 108 IOFilter *filter = NEditMalloc(sizeof(IOFilter)); 109 filter->name = XmTextGetString(fd.nameW); 110 filter->pattern = XmTextGetString(fd.patternW); 111 filter->ext = XmTextGetString(fd.extW); 112 filter->cmdin = XmTextGetString(fd.cmdInW); 113 filter->cmdout = XmTextGetString(fd.cmdOutW); 114 filter->ec_pattern = create_ec_pattern(filter->pattern); 115 116 if (strlen(filter->name) == 0 || 117 strlen(filter->pattern) == 0 || 118 strlen(filter->ext) == 0 || 119 strlen(filter->cmdin) == 0 || 120 strlen(filter->cmdout) == 0 || 121 fd.nfilters == fd.filteralloc) 122 { 123 fdFreeItemCB(filter); 124 *abort = 1; 125 } 126 127 return filter; 128 } 129 130 static void fdSetDisplayedCB(void *item, void *cbArg) 131 { 132 if(!item) { 133 XmTextSetString(fd.nameW, ""); 134 XmTextSetString(fd.patternW, ""); 135 XmTextSetString(fd.extW, ""); 136 XmTextSetString(fd.cmdInW, ""); 137 XmTextSetString(fd.cmdOutW, ""); 138 } else { 139 IOFilter *f = item; 140 XmTextSetString(fd.nameW, f->name); 141 XmTextSetString(fd.patternW, f->pattern); 142 XmTextSetString(fd.extW, f->ext); 143 XmTextSetString(fd.cmdInW, f->cmdin); 144 XmTextSetString(fd.cmdOutW, f->cmdout); 145 } 146 } 147 148 static IOFilter* fdCopyFilter(IOFilter *f) { 149 IOFilter *cp = NEditMalloc(sizeof(IOFilter)); 150 cp->name = NEditStrdup(f->name ? f->name : ""); 151 cp->pattern = NEditStrdup(f->pattern ? f->pattern : ""); 152 cp->ext = NEditStrdup(f->ext ? f->ext : ""); 153 cp->cmdin = NEditStrdup(f->cmdin ? f->cmdin : ""); 154 cp->cmdout = NEditStrdup(f->cmdout ? f->cmdout : ""); 155 cp->ec_pattern = f->ec_pattern ? NEditStrdup(f->ec_pattern) : NULL; 156 return cp; 157 } 158 159 #define FD_LIST_RIGHT 30 160 #define FD_LEFT_MARGIN_POS 1 161 #define FD_RIGHT_MARGIN_POS 99 162 #define FD_H_MARGIN 10 163 164 void FilterSettings(WindowInfo *window) 165 { 166 XmString s1; 167 168 if(fd.shell) { 169 RaiseDialogWindow(fd.shell); 170 return; 171 } 172 173 fd.filteralloc = 256; 174 fd.filters = NEditCalloc(sizeof(IOFilter), fd.filteralloc); 175 fd.nfilters = numFilters; 176 for(int i=0;i<numFilters;i++) { 177 fd.filters[i] = fdCopyFilter(filters[i]); 178 } 179 180 int ac = 0; 181 Arg args[20]; 182 //XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++; 183 XtSetArg(args[ac], XmNtitle, "Filters"); ac++; 184 fd.shell = CreateWidget(TheAppShell, "filters", 185 topLevelShellWidgetClass, args, ac); 186 AddSmallIcon(fd.shell); 187 Widget form = XtVaCreateManagedWidget("filtersForm", xmFormWidgetClass, 188 fd.shell, XmNautoUnmanage, False, 189 XmNresizePolicy, XmRESIZE_NONE, NULL); 190 191 Widget topLbl = XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form, 192 XmNlabelString, s1=MKSTRING( 193 "To modify the properties of an existing filter, select the name\n\ 194 from the list on the left. Select \"New\" to add a new filter to the list."), 195 XmNmnemonic, 'N', 196 XmNtopAttachment, XmATTACH_POSITION, 197 XmNtopPosition, 2, 198 XmNleftAttachment, XmATTACH_FORM, 199 XmNleftOffset, 6, 200 XmNrightAttachment, XmATTACH_FORM, 201 XmNrightOffset, 6, 202 NULL); 203 XmStringFree(s1); 204 205 Widget okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form, 206 XmNlabelString, s1=XmStringCreateSimple("OK"), 207 XmNmarginWidth, BUTTON_WIDTH_MARGIN, 208 XmNleftAttachment, XmATTACH_POSITION, 209 XmNleftPosition, 4, 210 XmNrightAttachment, XmATTACH_POSITION, 211 XmNrightPosition, 21, 212 XmNbottomAttachment, XmATTACH_FORM, 213 XmNbottomOffset, 6, 214 NULL); 215 XtAddCallback(okBtn, XmNactivateCallback, fdOkCB, NULL); 216 XmStringFree(s1); 217 218 Widget applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form, 219 XmNlabelString, s1=XmStringCreateSimple("Apply"), 220 XmNmnemonic, 'A', 221 XmNleftAttachment, XmATTACH_POSITION, 222 XmNleftPosition, 29, 223 XmNrightAttachment, XmATTACH_POSITION, 224 XmNrightPosition, 46, 225 XmNbottomAttachment, XmATTACH_FORM, 226 XmNbottomOffset, 6, 227 NULL); 228 XtAddCallback(applyBtn, XmNactivateCallback, fdApplyCB, NULL); 229 XmStringFree(s1); 230 231 Widget closeBtn = XtVaCreateManagedWidget("close", 232 xmPushButtonWidgetClass, form, 233 XmNlabelString, s1=XmStringCreateSimple("Close"), 234 XmNleftAttachment, XmATTACH_POSITION, 235 XmNleftPosition, 54, 236 XmNrightAttachment, XmATTACH_POSITION, 237 XmNrightPosition, 71, 238 XmNbottomAttachment, XmATTACH_FORM, 239 XmNbottomOffset, 6, 240 NULL); 241 XtAddCallback(closeBtn, XmNactivateCallback, fdCloseCB, NULL); 242 XmStringFree(s1); 243 244 Widget helpBtn = XtVaCreateManagedWidget("help", 245 xmPushButtonWidgetClass, form, 246 XmNlabelString, s1=XmStringCreateSimple("Help"), 247 XmNleftAttachment, XmATTACH_POSITION, 248 XmNleftPosition, 79, 249 XmNrightAttachment, XmATTACH_POSITION, 250 XmNrightPosition, 96, 251 XmNbottomAttachment, XmATTACH_FORM, 252 XmNbottomOffset, 6, 253 NULL); 254 XtAddCallback(helpBtn, XmNactivateCallback, fdHelpCB, NULL); 255 XmStringFree(s1); 256 257 Widget sep1 = XtVaCreateManagedWidget("sep1", xmSeparatorGadgetClass, form, 258 XmNleftAttachment, XmATTACH_FORM, 259 XmNrightAttachment, XmATTACH_FORM, 260 XmNleftOffset, 6, 261 XmNrightOffset, 6, 262 XmNbottomAttachment, XmATTACH_WIDGET, 263 XmNbottomWidget, okBtn, 264 XmNbottomOffset, 4, 265 NULL); 266 267 ac = 0; 268 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; 269 XtSetArg(args[ac], XmNtopOffset, FD_H_MARGIN); ac++; 270 XtSetArg(args[ac], XmNtopWidget, topLbl); ac++; 271 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++; 272 XtSetArg(args[ac], XmNleftPosition, FD_LEFT_MARGIN_POS); ac++; 273 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++; 274 XtSetArg(args[ac], XmNrightPosition, FD_LIST_RIGHT-1); ac++; 275 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; 276 XtSetArg(args[ac], XmNbottomWidget, sep1); ac++; 277 XtSetArg(args[ac], XmNbottomOffset, FD_H_MARGIN); ac++; 278 fd.managedListW = CreateManagedList(form, "list", args, ac, 279 (void **)fd.filters, &fd.nfilters, 280 256, 20, fdGetDisplayedCB, NULL, fdSetDisplayedCB, 281 form, fdFreeItemCB); 282 283 s1 = XmStringCreateLocalized("Name"); 284 Widget lblName = XtVaCreateManagedWidget("nameLbl", xmLabelGadgetClass, form, 285 XmNtopAttachment, XmATTACH_WIDGET, 286 XmNtopWidget, topLbl, 287 XmNtopOffset, FD_H_MARGIN, 288 XmNleftAttachment, XmATTACH_WIDGET, 289 XmNleftWidget, fd.managedListW, 290 XmNrightAttachment, XmATTACH_FORM, 291 XmNleftOffset, 6, 292 XmNrightOffset, 6, 293 XmNlabelString, s1, 294 XmNalignment, XmALIGNMENT_BEGINNING, 295 NULL); 296 XmStringFree(s1); 297 298 fd.nameW = XtVaCreateManagedWidget("patternLbl", xmTextWidgetClass, form, 299 XmNtopAttachment, XmATTACH_WIDGET, 300 XmNtopWidget, lblName, 301 XmNtopOffset, 6, 302 XmNleftAttachment, XmATTACH_WIDGET, 303 XmNleftWidget, fd.managedListW, 304 XmNrightAttachment, XmATTACH_FORM, 305 XmNleftOffset, 6, 306 XmNrightOffset, 6, 307 NULL); 308 309 s1 = XmStringCreateLocalized("File Pattern"); 310 Widget lblPattern = XtVaCreateManagedWidget("nameLbl", xmLabelGadgetClass, form, 311 XmNtopAttachment, XmATTACH_WIDGET, 312 XmNtopWidget, fd.nameW, 313 XmNtopOffset, 6, 314 XmNleftAttachment, XmATTACH_WIDGET, 315 XmNleftWidget, fd.managedListW, 316 //XmNrightAttachment, XmATTACH_POSITION, 317 //XmNrightPosition, 75, 318 XmNleftOffset, 6, 319 XmNlabelString, s1, 320 XmNalignment, XmALIGNMENT_BEGINNING, 321 NULL); 322 XmStringFree(s1); 323 324 fd.patternW = XtVaCreateManagedWidget("patternLbl", xmTextWidgetClass, form, 325 XmNtopAttachment, XmATTACH_WIDGET, 326 XmNtopWidget, lblPattern, 327 XmNtopOffset, 6, 328 XmNleftAttachment, XmATTACH_WIDGET, 329 XmNleftWidget, fd.managedListW, 330 XmNrightAttachment, XmATTACH_POSITION, 331 XmNrightPosition, 75, 332 XmNleftOffset, 6, 333 NULL); 334 335 s1 = XmStringCreateLocalized("Default Extension"); 336 Widget lblExt = XtVaCreateManagedWidget("extLbl", xmLabelGadgetClass, form, 337 XmNtopAttachment, XmATTACH_WIDGET, 338 XmNtopWidget, fd.nameW, 339 XmNtopOffset, 6, 340 XmNrightAttachment, XmATTACH_FORM, 341 XmNleftAttachment, XmATTACH_WIDGET, 342 XmNleftWidget, fd.patternW, 343 XmNleftOffset, 6, 344 XmNleftPosition, 76, 345 XmNrightOffset, 6, 346 XmNlabelString, s1, 347 XmNalignment, XmALIGNMENT_BEGINNING, 348 NULL); 349 XmStringFree(s1); 350 351 fd.extW = XtVaCreateManagedWidget("patternLbl", xmTextWidgetClass, form, 352 XmNtopAttachment, XmATTACH_WIDGET, 353 XmNtopWidget, lblExt, 354 XmNtopOffset, 6, 355 XmNleftAttachment, XmATTACH_WIDGET, 356 XmNleftWidget, fd.patternW, 357 XmNleftOffset, 6, 358 XmNrightAttachment, XmATTACH_FORM, 359 XmNrightOffset, 6, 360 NULL); 361 362 s1 = XmStringCreateLocalized("Input Filter Command"); 363 Widget lblCmdIn = XtVaCreateManagedWidget("cmdInLbl", xmLabelGadgetClass, form, 364 XmNtopAttachment, XmATTACH_WIDGET, 365 XmNtopWidget, fd.patternW, 366 XmNtopOffset, 6, 367 XmNleftAttachment, XmATTACH_WIDGET, 368 XmNleftWidget, fd.managedListW, 369 XmNrightAttachment, XmATTACH_FORM, 370 XmNleftOffset, 6, 371 XmNrightOffset, 6, 372 XmNlabelString, s1, 373 XmNalignment, XmALIGNMENT_BEGINNING, 374 NULL); 375 XmStringFree(s1); 376 377 ac = 0; 378 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; 379 XtSetArg(args[ac], XmNtopWidget, lblCmdIn); ac++; 380 XtSetArg(args[ac], XmNtopOffset, 6); ac++; 381 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; 382 XtSetArg(args[ac], XmNleftWidget, fd.managedListW); ac++; 383 XtSetArg(args[ac], XmNleftOffset, 6); ac++; 384 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_FORM); ac++; 385 XtSetArg(args[ac], XmNrightOffset, 6); ac++; 386 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++; 387 XtSetArg(args[ac], XmNbottomPosition, 61); ac++; 388 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++; 389 XtSetArg(args[ac], XmNrows, 4); ac++; 390 fd.cmdInW = XmCreateScrolledText(form, "cmdInText", args, ac); 391 XtManageChild(fd.cmdInW); 392 393 s1 = XmStringCreateLocalized("Output Filter Command"); 394 Widget lblCmdOut = XtVaCreateManagedWidget("cmdOutLbl", xmLabelGadgetClass, form, 395 XmNtopAttachment, XmATTACH_WIDGET, 396 XmNtopWidget, fd.cmdInW, 397 XmNtopOffset, FD_H_MARGIN, 398 XmNleftAttachment, XmATTACH_WIDGET, 399 XmNleftWidget, fd.managedListW, 400 XmNrightAttachment, XmATTACH_FORM, 401 XmNleftOffset, 6, 402 XmNrightOffset, 6, 403 XmNlabelString, s1, 404 XmNalignment, XmALIGNMENT_BEGINNING, 405 NULL); 406 XmStringFree(s1); 407 408 ac = 0; 409 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; 410 XtSetArg(args[ac], XmNtopWidget, lblCmdOut); ac++; 411 XtSetArg(args[ac], XmNtopOffset, 6); ac++; 412 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; 413 XtSetArg(args[ac], XmNleftWidget, fd.managedListW); ac++; 414 XtSetArg(args[ac], XmNleftOffset, 6); ac++; 415 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_FORM); ac++; 416 XtSetArg(args[ac], XmNrightOffset, 6); ac++; 417 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; 418 XtSetArg(args[ac], XmNbottomWidget, sep1); ac++; 419 XtSetArg(args[ac], XmNbottomOffset, FD_H_MARGIN); ac++; 420 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++; 421 XtSetArg(args[ac], XmNrows, 4); ac++; 422 fd.cmdOutW = XmCreateScrolledText(form, "cmdInText", args, ac); 423 XtManageChild(fd.cmdOutW); 424 425 /* Set initial default button */ 426 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL); 427 XtVaSetValues(form, XmNcancelButton, closeBtn, NULL); 428 429 XtAddCallback(form, XmNdestroyCallback, fdDestroyCB, NULL); 430 AddMotifCloseCallback(fd.shell, fdCloseCB, NULL); 431 432 RealizeWithoutForcingPosition(fd.shell); 433 } 434 435 static void fdDestroyCB(Widget w, XtPointer clientData, XtPointer callData) 436 { 437 for(int i=0;i<fd.nfilters;i++) { 438 fdFreeItemCB(fd.filters[i]); 439 } 440 NEditFree(fd.filters); 441 } 442 443 static void fdApplyCB(Widget w, XtPointer clientData, XtPointer callData) 444 { 445 fdUpdateList(); 446 } 447 448 static void fdOkCB(Widget w, XtPointer clientData, XtPointer callData) 449 { 450 if(!fdUpdateList()) { 451 return; 452 } 453 454 XtDestroyWidget(fd.shell); 455 fd.shell = NULL; 456 } 457 458 static void fdCloseCB(Widget w, XtPointer clientData, XtPointer callData) 459 { 460 XtDestroyWidget(fd.shell); 461 fd.shell = NULL; 462 } 463 464 static void fdHelpCB(Widget w, XtPointer clientData, XtPointer callData) 465 { 466 Help(HELP_FILTERS); 467 } 468 469 static int fdUpdateList(void) 470 { 471 if (!UpdateManagedList(fd.managedListW, True)) 472 return False; 473 474 for(int i=0;i<numFilters;i++) { 475 fdFreeItemCB(filters[i]); 476 } 477 NEditFree(filters); 478 479 filters = NEditCalloc(fd.nfilters, sizeof(IOFilter*)); 480 numFilters = fd.nfilters; 481 for(int i=0;i<numFilters;i++) { 482 filters[i] = fdCopyFilter(fd.filters[i]); 483 } 484 485 /* Note that preferences have been changed */ 486 MarkPrefsChanged(); 487 488 return True; 489 } 490 491 492 static int valueListNext(char *str, int len) 493 { 494 for(int i=0;i<len;i++) { 495 if(str[i] == ';') { 496 str[i] = '\0'; 497 len = i; 498 break; 499 } 500 } 501 return len; 502 } 503 504 static char* create_ec_pattern(char *pattern) 505 { 506 if(!pattern) { 507 return NULL; 508 } 509 510 char *ecPattern = NULL; 511 if(pattern[0] == '/') { 512 ecPattern = NEditStrdup(pattern); 513 } else { 514 // add **/ 515 size_t len = strlen(pattern); 516 int newlen = len+3; 517 ecPattern = NEditMalloc(newlen+1); 518 memcpy(ecPattern, "**/", 3); 519 memcpy(ecPattern+3, pattern, len); 520 ecPattern[newlen] = 0; 521 } 522 523 return ecPattern; 524 } 525 526 static IOFilter* ParseFilterStr(char *str, int len) 527 { 528 char *name = str; 529 int namelen = valueListNext(str, len); 530 int skip_space = 0; 531 while(isspace(*name) && namelen > 0) { 532 name++; 533 namelen--; 534 skip_space++; 535 } 536 if(namelen <= 0) { 537 return NULL; 538 } 539 str += skip_space + namelen + 1; 540 len -= skip_space + namelen + 1; 541 char *pattern = str; 542 int patternlen = valueListNext(str, len); 543 if(patternlen <= 0) { 544 return NULL; 545 } 546 str += patternlen + 1; 547 len -= patternlen + 1; 548 char *ext = str; 549 int extlen = valueListNext(str, len); 550 if(extlen <= 0) { 551 return NULL; 552 } 553 str += extlen + 1; 554 len -= extlen + 1; 555 char *cmdin = str; 556 int cmdinlen = valueListNext(str, len); 557 if(cmdinlen <= 0) { 558 return NULL; 559 } 560 str += cmdinlen + 1; 561 len -= cmdinlen + 1; 562 char *cmdout = str; 563 564 int cmdoutlen = valueListNext(str, len); 565 if(cmdoutlen <= 0) { 566 return NULL; 567 } 568 //str += cmdoutlen + 1; 569 //len -= cmdoutlen + 1; 570 571 IOFilter *filter = NEditMalloc(sizeof(IOFilter)); 572 filter->name = NEditStrndup(name, namelen); 573 filter->pattern = NEditStrndup(pattern, patternlen); 574 filter->ext = NEditStrndup(ext, extlen); 575 filter->cmdin = NEditStrndup(cmdin, cmdinlen); 576 filter->cmdout = NEditStrndup(cmdout, cmdoutlen); 577 filter->ec_pattern = create_ec_pattern(filter->pattern); 578 return filter; 579 } 580 581 void ParseFilterSettings(char *str) 582 { 583 size_t filterAlloc = 8; 584 filters = NEditCalloc(filterAlloc, sizeof(IOFilter*)); 585 numFilters = 0; 586 587 size_t len = strlen(str); 588 int lineStart = 0; 589 for(int i=0;i<=len;i++) { 590 if(i == len || str[i] == '\n') { 591 IOFilter *filter = ParseFilterStr(str+lineStart, i-lineStart); 592 lineStart = i+1; 593 594 if(filter) { 595 if(numFilters == filterAlloc) { 596 filterAlloc += 8; 597 NEditRealloc(filters, filterAlloc * sizeof(IOFilter*)); 598 } 599 filters[numFilters] = filter; 600 numFilters++; 601 } 602 } 603 } 604 } 605 606 char* WriteFilterString(void) 607 { 608 size_t str_alloc = 2048; 609 size_t len = 0; 610 char *str = NEditMalloc(str_alloc); 611 str[0] = 0; 612 613 for(int i=0;i<numFilters;i++) { 614 IOFilter *filter = filters[i]; 615 char *prefix = i > 0 ? "\t" : ""; 616 char *suffix = i+1 != numFilters ? "\\n\\\n" : ""; 617 for(;;) { 618 size_t remaining = str_alloc - len; 619 size_t w = snprintf( 620 str + len, 621 remaining, 622 "%s%s;%s;%s;%s;%s%s", 623 prefix, 624 filter->name, 625 filter->pattern, 626 filter->ext, 627 filter->cmdin, 628 filter->cmdout, 629 suffix); 630 if(w < remaining) { 631 len += w; 632 break; 633 } else { 634 str_alloc += 1024; 635 str = NEditRealloc(str, str_alloc); 636 } 637 } 638 } 639 640 return str; 641 } 642 643 IOFilter** GetFilterList(size_t *num) 644 { 645 *num = numFilters; 646 return filters; 647 } 648 649 IOFilter* GetFilterFromName(const char *name) 650 { 651 if(!name) { 652 return NULL; 653 } 654 for(int i=0;i<numFilters;i++) { 655 if(!strcmp(filters[i]->name, name)) { 656 return filters[i]; 657 } 658 } 659 return NULL; 660 } 661 662 IOFilter* GetFilterForPath(const char *path) { 663 IOFilter *filter = NULL; 664 for(int i=0;i<numFilters;i++) { 665 if(!ec_glob(filters[i]->ec_pattern, path)) { 666 filter = filters[i]; 667 break; 668 } 669 } 670 return filter; 671 } 672 673 /* ----------------------------- FileStream -----------------------------*/ 674 675 typedef struct FilterIOThreadData { 676 XtAppContext appcontext; 677 Widget widget; 678 FILE *file; 679 int fd_in; 680 int fd_out; 681 pid_t pid; 682 } FilterIOThreadData; 683 684 typedef struct FilterCmdError { 685 Widget w; 686 int status; 687 int io_errno; 688 } FilterCmdError; 689 690 static void filter_command_error(XtPointer clientData, XtIntervalId *id) { 691 FilterCmdError *error = clientData; 692 // The widget error->w should be in the WindowList 693 // if it is not in the list, it could already be destroyed 694 Widget w = NULL; 695 for (WindowInfo *win=WindowList; win!=NULL; win=win->next) { 696 if(win->shell == error->w) { 697 w = error->w; 698 break; 699 } 700 } 701 if(w) { 702 if(error->io_errno != 0) { 703 (void)DialogF(DF_WARN, error->w, 1, "Command Failure", 704 "Filter IO Error: %s\n", 705 "OK", 706 strerror(error->io_errno)); 707 } else { 708 (void)DialogF(DF_WARN, error->w, 1, "Command Failure", 709 "Filter command reported failed exit status: %d\n", 710 "OK", 711 error->status); 712 } 713 } 714 715 NEditFree(error); 716 } 717 718 static void* file_input_thread(void *data) { 719 FilterIOThreadData *stream = data; 720 721 int ioerror = 0; 722 int io_errno = 0; 723 724 char buf[16384]; 725 size_t r; 726 while((r = fread(buf, 1, 16384, stream->file)) > 0) { 727 char *wbuf = buf; 728 while(r > 0) { 729 ssize_t w = write(stream->fd_in, wbuf, r); 730 if(w <= 0) { 731 ioerror = 1; 732 io_errno = errno; 733 break; 734 } 735 r -= w; 736 wbuf += w; 737 } 738 739 if(ioerror) { 740 break; 741 } 742 } 743 744 close(stream->fd_in); 745 746 int status = -1; 747 waitpid(stream->pid, &status, 0); 748 if(status != 0 || ioerror) { 749 FilterCmdError *error = NEditMalloc(sizeof(FilterCmdError)); 750 error->w = stream->widget; 751 error->status = status; 752 error->io_errno = io_errno; 753 XtAppAddTimeOut( 754 stream->appcontext, 755 0, 756 filter_command_error, 757 error); 758 } 759 760 NEditFree(stream); 761 762 return NULL; 763 } 764 765 #define FILTER_IO_BUFSIZE 16384 766 767 static void* file_output_thread(void *data) { 768 FilterIOThreadData *stream = data; 769 770 char buf[FILTER_IO_BUFSIZE]; 771 ssize_t r; 772 while((r = read(stream->fd_out, buf, FILTER_IO_BUFSIZE)) > 0) { 773 fwrite(buf, 1, r, stream->file); 774 } 775 776 close(stream->fd_out); 777 fclose(stream->file); 778 779 int status = -1; 780 waitpid(stream->pid, &status, 0); 781 if(status != 0) { 782 FilterCmdError *error = NEditMalloc(sizeof(FilterCmdError)); 783 error->w = stream->widget; 784 error->status = status; 785 XtAppAddTimeOut( 786 stream->appcontext, 787 0, 788 filter_command_error, 789 error); 790 } 791 792 NEditFree(stream); 793 794 return NULL; 795 } 796 797 static int filestream_create_pipes(FileStream *stream) { 798 if(pipe(stream->pin)) { 799 return 1; 800 } 801 if(pipe(stream->pout)) { 802 close(stream->pin[0]); 803 close(stream->pin[1]); 804 return 1; 805 } 806 return 0; 807 } 808 809 static FileStream* filestream_open(Widget w, FILE *f, const char *filter_cmd, int mode) { 810 FileStream *stream = NEditMalloc(sizeof(FileStream)); 811 stream->file = f; 812 stream->filter_cmd = filter_cmd ? NEditStrdup(filter_cmd) : NULL; 813 stream->pid = 0; 814 stream->hdrbufpos = 0; 815 stream->hdrbuflen = 0; 816 stream->mode = mode; 817 818 if(filter_cmd) { 819 if(filestream_create_pipes(stream)) { 820 NEditFree(stream->filter_cmd); 821 NEditFree(stream); 822 fclose(f); 823 fprintf(stderr, "Failed to create pipe: %s\n", strerror(errno)); 824 return NULL; 825 } 826 827 pid_t child = fork(); 828 if(child == 0) { 829 close(STDIN_FILENO); 830 close(STDOUT_FILENO); 831 832 // we need stdin, stdout and stderr refer to the previously 833 // created pipes 834 if(dup2(stream->pin[0], STDIN_FILENO) == -1) { 835 perror("dup2"); 836 exit(1); 837 } 838 if(dup2(stream->pout[1], STDOUT_FILENO) == -1) { 839 perror("dup2"); 840 exit(1); 841 } 842 843 close(stream->pin[1]); 844 845 // execute the command using the shell specified by preferences 846 //fprintf(stderr, "info: input filter command: %s\n", filter_cmd); 847 execlp(GetPrefShell(), GetPrefShell(), "-c", filter_cmd, NULL); 848 849 // execlp only returns if an error occured 850 fprintf(stderr, "Error starting shell: %s\n", GetPrefShell()); 851 exit(1); 852 } else { 853 stream->pid = child; 854 855 FilterIOThreadData *data = NEditMalloc(sizeof(FilterIOThreadData)); 856 data->appcontext = XtWidgetToApplicationContext(w); 857 data->widget = w; 858 data->file = stream->file; 859 data->fd_in = stream->pin[1]; 860 data->fd_out = stream->pout[0]; 861 data->pid = child; 862 863 pthread_t tid; 864 if(pthread_create(&tid, NULL, mode == 0 ? file_input_thread : file_output_thread, data)) { 865 fprintf(stderr, "Errro: cannot create file input thread: %s\n", strerror(errno)); 866 NEditFree(data); 867 close(stream->pin[0]); 868 close(stream->pin[1]); 869 close(stream->pout[0]); 870 close(stream->pout[1]); 871 fclose(f); 872 NEditFree(stream->filter_cmd); 873 NEditFree(stream); 874 return NULL; 875 } 876 877 if(mode == 1) { 878 stream->file = NULL; // file will be closed by file_output_thread 879 } 880 881 close(stream->pout[1]); 882 } 883 } 884 885 return stream; 886 } 887 888 FileStream* filestream_open_r(Widget w, FILE *f, const char *filter_cmd) { 889 return filestream_open(w, f, filter_cmd, 0); 890 } 891 892 FileStream* filestream_open_w(Widget w, FILE *f, const char *filter_cmd) { 893 return filestream_open(w, f, filter_cmd, 1); 894 } 895 896 int filestream_reset(FileStream *stream, int pos) { 897 if(stream->pid == 0) { 898 fseek(stream->file, pos, SEEK_SET); 899 } else { 900 if(pos > stream->hdrbuflen || stream->hdrbufpos > stream->hdrbuflen) { 901 return 1; 902 } 903 stream->hdrbufpos = pos; 904 } 905 return 0; 906 } 907 908 size_t filestream_read(void *buffer, size_t nbytes, FileStream *stream) { 909 if(stream->pid == 0) { 910 return fread(buffer, 1, nbytes, stream->file); 911 } else { 912 if(stream->hdrbufpos < stream->hdrbuflen) { 913 // get bytes from hdrbuf before reading more bytes from the pipe 914 size_t r = stream->hdrbuflen - stream->hdrbufpos; 915 if(r > nbytes) { 916 r = nbytes; 917 } 918 memcpy(buffer, stream->hdrbuf, r); 919 nbytes -= r; 920 buffer = ((char*)buffer) + r; 921 stream->hdrbufpos += r; 922 if(nbytes > 0) { 923 r += filestream_read(buffer, nbytes, stream); 924 } 925 return r; 926 } 927 928 ssize_t sr = read(stream->pout[0], buffer, nbytes); 929 //fwrite(buffer, 1, sr, stdout); 930 //fflush(stdout); 931 if(sr < 0) { 932 return 0; 933 } 934 935 // the first bytes we read from the pipe are stored in hdrbuf 936 // because we may want to reset the stream 937 if(stream->hdrbuflen < FILESTREAM_HDR_BUFLEN) { 938 size_t buflen = FILESTREAM_HDR_BUFLEN - stream->hdrbuflen; 939 if(buflen > sr) { 940 buflen = sr; 941 } 942 memcpy(stream->hdrbuf + stream->hdrbuflen, buffer, buflen); 943 stream->hdrbuflen += buflen; 944 } 945 946 stream->hdrbufpos += sr; 947 return (size_t)sr; 948 } 949 } 950 951 size_t filestream_write(const void *buffer, size_t nbytes, FileStream *stream) { 952 if(stream->pid == 0) { 953 return fwrite(buffer, 1, nbytes, stream->file); 954 } else { 955 ssize_t w = write(stream->pin[1], buffer, nbytes); 956 if(w < 0) { 957 w = 0; 958 } 959 return w; 960 } 961 } 962 963 int filestream_close(FileStream *stream) { 964 if(stream->pid != 0) { 965 if(stream->mode == 0) { 966 if(close(stream->pin[0])) { 967 perror("pipe pin[0] close"); 968 } 969 if(close(stream->pout[0])) { 970 perror("pipe pout[0] close"); 971 } 972 } else { 973 if(close(stream->pin[1])) { 974 perror("pipe pin[1] close"); 975 } 976 } 977 } 978 int err = 0; 979 if(stream->file) { 980 err = fclose(stream->file); 981 } 982 NEditFree(stream->filter_cmd); 983 NEditFree(stream); 984 return err; 985 } 986