commands.c revision 5dfecf96
1/* $XConsortium: commands.c,v 1.33 91/10/21 14:32:18 eswu Exp $ */ 2 3/* 4 * COPYRIGHT 1987 5 * DIGITAL EQUIPMENT CORPORATION 6 * MAYNARD, MASSACHUSETTS 7 * ALL RIGHTS RESERVED. 8 * 9 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND 10 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION. 11 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR 12 * ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. 13 * 14 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS, 15 * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT 16 * SET FORTH ABOVE. 17 * 18 * 19 * Permission to use, copy, modify, and distribute this software and its 20 * documentation for any purpose and without fee is hereby granted, provided 21 * that the above copyright notice appear in all copies and that both that 22 * copyright notice and this permission notice appear in supporting 23 * documentation, and that the name of Digital Equipment Corporation not be 24 * used in advertising or publicity pertaining to distribution of the software 25 * without specific, written prior permission. 26 */ 27/* $XFree86: xc/programs/xedit/commands.c,v 1.29tsi Exp $ */ 28 29#include <X11/Xfuncs.h> 30#include <X11/Xos.h> 31#include "xedit.h" 32#ifdef INCLUDE_XPRINT_SUPPORT 33#include "printdialog.h" 34#include "print.h" 35#endif /* INCLUDE_XPRINT_SUPPORT */ 36#ifdef CRAY 37#include <unistd.h> 38#endif 39#include <stdlib.h> 40#include <stdio.h> 41#include <limits.h> 42#include <string.h> 43#include <dirent.h> 44#include <pwd.h> 45#include <sys/stat.h> 46#include <X11/Xmu/SysUtil.h> 47#include <X11/IntrinsicP.h> 48#include <X11/Xaw/TextSrcP.h> 49 50/* Turn a NULL pointer string into an empty string */ 51#define NULLSTR(x) (((x)!=NULL)?(x):("")) 52 53#define Error(x) { printf x ; exit(EXIT_FAILURE); } 54#define Assertion(expr, msg) { if (!(expr)) { Error msg } } 55#define Log(x) { if (True) printf x; } 56 57#ifdef INCLUDE_XPRINT_SUPPORT 58static Widget printdialog_shell = NULL; 59static Widget printdialog = NULL; 60static char printJobNameBuffer[PATH_MAX+256]; 61#endif /* INCLUDE_XPRINT_SUPPORT */ 62 63void ResetSourceChanged(xedit_flist_item*); 64static void ResetDC(Widget, XtPointer, XtPointer); 65 66static void AddDoubleClickCallback(Widget, Bool); 67static Bool ReallyDoLoad(char*, char*); 68static char *makeBackupName(String, String, unsigned); 69 70extern Widget scratch, texts[3], labels[3]; 71static Boolean double_click = FALSE; 72 73#define DC_UNSAVED 1 74#define DC_LOADED 2 75#define DC_CLOBBER 3 76#define DC_KILL 4 77#define DC_SAVE 5 78static int dc_state; 79 80/* Function Name: AddDoubleClickCallback(w) 81 * Description: Adds a callback that will reset the double_click flag 82 * to false when the text is changed. 83 * Arguments: w - widget to set callback upon. 84 * state - If true add the callback, else remove it. 85 * Returns: none. 86 */ 87static void 88AddDoubleClickCallback(Widget w, Bool state) 89{ 90 Arg args[1]; 91 static XtCallbackRec cb[] = { {NULL, NULL}, {NULL, NULL} }; 92 93 if (XtIsSubclass(w, asciiSrcObjectClass)) { 94 if (state) 95 XtAddCallback(w, XtNcallback, ResetDC, NULL); 96 else 97 XtRemoveCallback(w, XtNcallback, ResetDC, NULL); 98 } 99 else { 100 if (state) 101 cb[0].callback = ResetDC; 102 else 103 cb[0].callback = NULL; 104 105 XtSetArg(args[0], XtNcallback, cb); 106 XtSetValues(w, args, ONE); 107 } 108} 109 110/* Function Name: ResetDC 111 * Description: Resets the double click flag. 112 * Arguments: w - the text widget. 113 * junk, garbage - *** NOT USED *** 114 * Returns: none. 115 */ 116 117/* ARGSUSED */ 118static void 119ResetDC(Widget w, XtPointer junk, XtPointer garbage) 120{ 121 double_click = FALSE; 122 123 AddDoubleClickCallback(w, FALSE); 124} 125 126/*ARGSUSED*/ 127void 128QuitAction(Widget w, XEvent *event, String *params, Cardinal *num_params) 129{ 130 DoQuit(w, NULL, NULL); 131} 132 133/*ARGSUSED*/ 134void 135DoQuit(Widget w, XtPointer client_data, XtPointer call_data) 136{ 137 unsigned i; 138 Bool source_changed = False; 139 140 if (!double_click || (dc_state && dc_state != DC_UNSAVED)) { 141 for (i = 0; i < flist.num_itens; i++) 142 if (flist.itens[i]->flags & CHANGED_BIT) { 143 source_changed = True; 144 break; 145 } 146 } 147 if(!source_changed) { 148#ifndef __UNIXOS2__ 149 XeditLispCleanUp(); 150#endif 151 exit(0); 152 } 153 154 XeditPrintf("Unsaved changes. Save them, or Quit again.\n"); 155 Feep(); 156 double_click = TRUE; 157 dc_state = DC_UNSAVED; 158 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 159} 160 161static char * 162makeBackupName(String buf, String filename, unsigned len) 163{ 164 if (app_resources.backupNamePrefix 165 && strlen(app_resources.backupNamePrefix)) { 166 if (strchr(app_resources.backupNamePrefix, '/')) 167 XmuSnprintf(buf, len, "%s%s%s", app_resources.backupNamePrefix, 168 filename, app_resources.backupNameSuffix); 169 else { 170 char fname[BUFSIZ]; 171 char *name, ch; 172 173 strncpy(fname, filename, sizeof(fname) - 1); 174 fname[sizeof(fname) - 1] = '\0'; 175 if ((name = strrchr(fname, '/')) != NULL) 176 ++name; 177 else 178 name = filename; 179 ch = *name; 180 *name = '\0'; 181 ++name; 182 XmuSnprintf(buf, len, "%s%s%c%s%s", 183 fname, app_resources.backupNamePrefix, ch, name, 184 app_resources.backupNameSuffix); 185 } 186 } 187 else 188 XmuSnprintf(buf, len, "%s%s", 189 filename, app_resources.backupNameSuffix); 190 191 return (strcmp(filename, buf) ? buf : NULL); 192} 193 194#if defined(USG) && !defined(CRAY) 195int rename (from, to) 196 char *from, *to; 197{ 198 (void) unlink (to); 199 if (link (from, to) == 0) { 200 unlink (from); 201 return 0; 202 } else { 203 return -1; 204 } 205} 206#endif 207 208/*ARGSUSED*/ 209void 210SaveFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 211{ 212 DoSave(w, NULL, NULL); 213} 214 215/*ARGSUSED*/ 216void 217DoSave(Widget w, XtPointer client_data, XtPointer call_data) 218{ 219 String name = GetString(filenamewindow); 220 String filename = ResolveName(name); 221 char buf[BUFSIZ]; 222 FileAccess file_access; 223 xedit_flist_item *item; 224 Boolean exists; 225 Widget source = XawTextGetSource(textwindow); 226 227 if (!filename) { 228 XeditPrintf("Save: Can't resolve pathname -- nothing saved.\n"); 229 Feep(); 230 return; 231 } 232 else if (*name == '\0') { 233 XeditPrintf("Save: No filename specified -- nothing saved.\n"); 234 Feep(); 235 return; 236 } 237 else { 238 struct stat st; 239 240 if (stat(filename, &st) == 0 && !S_ISREG(st.st_mode)) { 241 XmuSnprintf(buf, sizeof(buf), 242 "Save: file %s is not a regular file -- nothing saved.\n", 243 name); 244 XeditPrintf(buf); 245 Feep(); 246 return; 247 } 248 } 249 250 item = FindTextSource(NULL, filename); 251 if (item != NULL && item->source != source) { 252 if (!double_click || (dc_state && dc_state != DC_LOADED)) { 253 XmuSnprintf(buf, sizeof(buf), 254 "Save: file %s is already loaded, " 255 "Save again to unload it -- nothing saved.\n", 256 name); 257 XeditPrintf(buf); 258 Feep(); 259 double_click = TRUE; 260 dc_state = DC_LOADED; 261 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 262 return; 263 } 264 KillTextSource(item); 265 item = FindTextSource(source = XawTextGetSource(textwindow), NULL); 266 double_click = FALSE; 267 dc_state = 0; 268 } 269 else if (item && !(item->flags & CHANGED_BIT)) { 270 if (!double_click || (dc_state && dc_state != DC_SAVE)) { 271 XeditPrintf("Save: No changes need to be saved, " 272 "Save again to override.\n"); 273 Feep(); 274 double_click = TRUE; 275 dc_state = DC_SAVE; 276 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 277 return; 278 } 279 double_click = FALSE; 280 dc_state = 0; 281 } 282 283 file_access = CheckFilePermissions(filename, &exists); 284 if (!item || strcmp(item->filename, filename)) { 285 if (file_access == WRITE_OK && exists) { 286 if (!double_click || (dc_state && dc_state != DC_CLOBBER)) { 287 XmuSnprintf(buf, sizeof(buf), 288 "Save: file %s already exists, " 289 "Save again to overwrite it -- nothing saved.\n", 290 name); 291 XeditPrintf(buf); 292 Feep(); 293 double_click = TRUE; 294 dc_state = DC_CLOBBER; 295 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 296 return; 297 } 298 double_click = FALSE; 299 dc_state = 0; 300 } 301 if (!item) 302 item = FindTextSource(source, NULL); 303 } 304 305 if (app_resources.enableBackups && exists) { 306 char backup_file[BUFSIZ]; 307 308 if (makeBackupName(backup_file, filename, sizeof(backup_file)) == NULL 309 || rename(filename, backup_file) != 0) { 310 XmuSnprintf(buf, sizeof(buf),"error backing up file: %s\n", 311 filename); 312 XeditPrintf(buf); 313 } 314 } 315 316 switch( file_access = MaybeCreateFile(filename)) { 317 case NO_READ: 318 case READ_OK: 319 XmuSnprintf(buf, sizeof(buf), 320 "File %s could not be opened for writing.\n", name); 321 Feep(); 322 break; 323 case WRITE_OK: 324 if ( XawAsciiSaveAsFile(source, filename) ) { 325 int i; 326 Arg args[1]; 327 char label_buf[BUFSIZ]; 328 329 /* Keep file protection mode */ 330 if (item && item->mode) 331 chmod(filename, item->mode); 332 333 XmuSnprintf(label_buf, sizeof(label_buf), 334 "%s Read - Write", name); 335 XtSetArg(args[0], XtNlabel, label_buf); 336 for (i = 0; i < 3; i++) 337 if (XawTextGetSource(texts[i]) == source) 338 XtSetValues(labels[i], args, 1); 339 340 XmuSnprintf(buf, sizeof(buf), "Saved file: %s\n", name); 341 342 if (item && item->source != scratch) { 343 XtSetArg(args[0], XtNlabel, filename); 344 XtSetValues(item->sme, args, 1); 345 346 XtSetArg(args[0], XtNeditType, XawtextEdit); 347 XtSetValues(item->source, args, 1); 348 349 XtFree(item->name); 350 XtFree(item->filename); 351 item->name = XtNewString(name); 352 item->filename = XtNewString(filename); 353 item->flags = EXISTS_BIT; 354 } 355 else { 356 item = flist.itens[0]; 357 XtRemoveCallback(scratch, XtNcallback, SourceChanged, 358 (XtPointer)item); 359 item->source = scratch = 360 XtVaCreateWidget("textSource", 361 multiSrcObjectClass, 362 topwindow, 363 XtNtype, XawAsciiFile, 364 XtNeditType, XawtextEdit, 365 NULL, NULL); 366 ResetSourceChanged(item); 367 XtAddCallback(scratch, XtNcallback, SourceChanged, 368 (XtPointer)item); 369 370 item = AddTextSource(source, name, filename, EXISTS_BIT, 371 file_access); 372 XtAddCallback(item->source, XtNcallback, SourceChanged, 373 (XtPointer)item); 374 } 375 item->flags |= EXISTS_BIT; 376 ResetSourceChanged(item); 377 } 378 else { 379 XmuSnprintf(buf, sizeof(buf), "Error saving file: %s\n", name); 380 Feep(); 381 } 382 break; 383 default: 384 XmuSnprintf(buf, sizeof(buf), "%s %s", 385 "Internal function MaybeCreateFile()", 386 "returned unexpected value.\n"); 387 Feep(); 388 break; 389 } 390 391 XeditPrintf(buf); 392} 393 394/*ARGSUSED*/ 395void 396DoLoad(Widget w, XtPointer client_data, XtPointer call_data) 397{ 398 if (ReallyDoLoad(GetString(filenamewindow), ResolveName(NULL))) { 399 SwitchDirWindow(False); 400 XtSetKeyboardFocus(topwindow, textwindow); 401 } 402} 403 404static Bool 405ReallyDoLoad(char *name, char *filename) 406{ 407 Arg args[5]; 408 Cardinal num_args = 0; 409 char buf[BUFSIZ]; 410 xedit_flist_item *item; 411 Widget source = XawTextGetSource(textwindow); 412 413 if (!filename) { 414 XeditPrintf("Load: Can't resolve pathname.\n"); 415 Feep(); 416 return (False); 417 } 418 else if (*name == '\0') { 419 XeditPrintf("Load: No file specified.\n"); 420 Feep(); 421 } 422 if ((item = FindTextSource(NULL, filename)) != NULL) { 423 SwitchTextSource(item); 424 return (True); 425 } 426 else { 427 struct stat st; 428 429 if (stat(filename, &st) == 0 && !S_ISREG(st.st_mode)) { 430 if (S_ISDIR(st.st_mode)) { 431 char path[BUFSIZ + 1]; 432 433 strncpy(path, filename, sizeof(path) - 2); 434 path[sizeof(path) - 2] = '\0'; 435 if (*path) { 436 if (path[strlen(path) - 1] != '/') 437 strcat(path, "/"); 438 } 439 else 440 strcpy(path, "./"); 441 XtSetArg(args[0], XtNlabel, ""); 442 XtSetValues(dirlabel, args, 1); 443 SwitchDirWindow(True); 444 DirWindowCB(dirwindow, path, NULL); 445 return (False); 446 } 447 } 448 } 449 450 { 451 Boolean exists; 452 int flags; 453 FileAccess file_access; 454 455 switch( file_access = CheckFilePermissions(filename, &exists) ) { 456 case NO_READ: 457 if (exists) 458 XmuSnprintf(buf, sizeof(buf), "File %s, %s", name, 459 "exists, and could not be opened for reading.\n"); 460 else 461 XmuSnprintf(buf, sizeof(buf), "File %s %s %s", name, 462 "does not exist, and", 463 "the directory could not be opened for writing.\n"); 464 465 XeditPrintf(buf); 466 Feep(); 467 return (False); 468 case READ_OK: 469 XtSetArg(args[num_args], XtNeditType, XawtextRead); num_args++; 470 XmuSnprintf(buf, sizeof(buf), "File %s opened READ ONLY.\n", 471 name); 472 break; 473 case WRITE_OK: 474 XtSetArg(args[num_args], XtNeditType, XawtextEdit); num_args++; 475 XmuSnprintf(buf, sizeof(buf), "File %s opened read - write.\n", 476 name); 477 break; 478 default: 479 XmuSnprintf(buf, sizeof(buf), "%s %s", 480 "Internal function MaybeCreateFile()", 481 "returned unexpected value.\n"); 482 XeditPrintf(buf); 483 Feep(); 484 return (False); 485 } 486 487 XeditPrintf(buf); 488 489 if (exists) { 490 flags = EXISTS_BIT; 491 XtSetArg(args[num_args], XtNstring, filename); num_args++; 492 } 493 else { 494 flags = 0; 495 XtSetArg(args[num_args], XtNstring, NULL); num_args++; 496 } 497 498 source = XtVaCreateWidget("textSource", 499 multiSrcObjectClass, 500 topwindow, 501 XtNtype, XawAsciiFile, 502 XtNeditType, XawtextEdit, 503 NULL, NULL); 504 XtSetValues(source, args, num_args); 505 506 item = AddTextSource(source, name, filename, flags, file_access); 507 XtAddCallback(item->source, XtNcallback, SourceChanged, 508 (XtPointer)item); 509 if (exists && file_access == WRITE_OK) { 510 struct stat st; 511 512 if (stat(filename, &st) == 0) 513 item->mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); 514 } 515 SwitchTextSource(item); 516 ResetSourceChanged(item); 517 } 518 519 return (True); 520} 521 522#ifdef INCLUDE_XPRINT_SUPPORT 523static void 524printshellDestroyXtProc(Widget w, XtPointer client_data, XtPointer callData) 525{ 526 XawPrintDialogClosePrinterConnection(printdialog, False); 527} 528 529static void 530printOKXtProc(Widget w, XtPointer client_data, XtPointer callData) 531{ 532 XawPrintDialogCallbackStruct *pdcs = (XawPrintDialogCallbackStruct *)callData; 533 Cardinal n; 534 Arg args[2]; 535 Widget textsource; 536 537 Log(("printOKXtProc: OK.\n")); 538 539 /* Get TextSource object */ 540 n = 0; 541 XtSetArg(args[n], XtNtextSource, &textsource); n++; 542 XtGetValues(textwindow, args, n); 543 544 Assertion(textsource != NULL, (("printOKXtProc: textsource == NULL.\n"))); 545 546 /* ||printJobNameBuffer| must live as long the print job prints 547 * because it is used for the job title AND the page headers... */ 548 sprintf(printJobNameBuffer, "Xedit print job"); 549 550 DoPrintTextSource("Xedit", 551 textsource, topwindow, 552 pdcs->pdpy, pdcs->pcontext, pdcs->colorspace, 553 printshellDestroyXtProc, 554 printJobNameBuffer, 555 pdcs->printToFile?pdcs->printToFileName:NULL); 556 557 XtPopdown(printdialog_shell); 558} 559 560static void 561printCancelXtProc(Widget w, XtPointer client_data, XtPointer callData) 562{ 563 Log(("printCancelXtProc: cancel.\n")); 564 XtPopdown(printdialog_shell); 565 566 Log(("destroying print dialog shell...\n")); 567 XtDestroyWidget(printdialog_shell); 568 printdialog_shell = NULL; 569 printdialog = NULL; 570 Log(("... done\n")); 571} 572 573 574/*ARGSUSED*/ 575void 576PrintFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 577{ 578 DoPrint(w, NULL, NULL); 579} 580 581/*ARGSUSED*/ 582void 583DoPrint(Widget w, XtPointer client_data, XtPointer call_data) 584{ 585 Dimension width, height; 586 Position x, y; 587 Widget parent = topwindow; 588 Log(("print!\n")); 589 590 if (!printdialog) { 591 int n; 592 Arg args[20]; 593 594 n = 0; 595 XtSetArg(args[n], XtNallowShellResize, True); n++; 596 printdialog_shell = XtCreatePopupShell("printdialogshell", 597 transientShellWidgetClass, 598 topwindow, args, n); 599 n = 0; 600 printdialog = XtCreateManagedWidget("printdialog", printDialogWidgetClass, 601 printdialog_shell, args, n); 602 XtAddCallback(printdialog, XawNOkCallback, printOKXtProc, NULL); 603 XtAddCallback(printdialog, XawNCancelCallback, printCancelXtProc, NULL); 604 605 XtRealizeWidget(printdialog_shell); 606 } 607 608 /* Center dialog */ 609 XtVaGetValues(printdialog_shell, 610 XtNwidth, &width, 611 XtNheight, &height, 612 NULL); 613 614 x = (Position)(XWidthOfScreen( XtScreen(parent)) - width) / 2; 615 y = (Position)(XHeightOfScreen(XtScreen(parent)) - height) / 3; 616 617 XtVaSetValues(printdialog_shell, 618 XtNx, x, 619 XtNy, y, 620 NULL); 621 622 XtPopup(printdialog_shell, XtGrabNonexclusive); 623} 624#endif /* INCLUDE_XPRINT_SUPPORT */ 625 626/* Function Name: SourceChanged 627 * Description: A callback routine called when the source has changed. 628 * Arguments: w - the text source that has changed. 629 * client_data - xedit_flist_item associated with text buffer. 630 * call_data - NULL is unchanged 631 * Returns: none. 632 */ 633/*ARGSUSED*/ 634void 635SourceChanged(Widget w, XtPointer client_data, XtPointer call_data) 636{ 637 xedit_flist_item *item = (xedit_flist_item*)client_data; 638 Bool changed = (Bool)(long)call_data; 639 640 if (changed) { 641 if (item->flags & CHANGED_BIT) 642 return; 643 item->flags |= CHANGED_BIT; 644 } 645 else { 646 if (item->flags & CHANGED_BIT) 647 ResetSourceChanged(item); 648 return; 649 } 650 651 if (flist.pixmap) { 652 Arg args[1]; 653 Cardinal num_args; 654 int i; 655 656 num_args = 0; 657 XtSetArg(args[num_args], XtNleftBitmap, flist.pixmap); ++num_args; 658 XtSetValues(item->sme, args, num_args); 659 660 for (i = 0; i < 3; i++) 661 if (XawTextGetSource(texts[i]) == item->source) 662 XtSetValues(labels[i], args, num_args); 663 } 664} 665 666/* Function Name: ResetSourceChanged. 667 * Description: Sets the source changed to FALSE, and 668 * registers a callback to set it to TRUE when 669 * the source has changed. 670 * Arguments: item - item with widget to register the callback on. 671 * Returns: none. 672 */ 673 674void 675ResetSourceChanged(xedit_flist_item *item) 676{ 677 Arg args[1]; 678 Cardinal num_args; 679 int i; 680 681 num_args = 0; 682 XtSetArg(args[num_args], XtNleftBitmap, None); ++num_args; 683 XtSetValues(item->sme, args, num_args); 684 685 dc_state = 0; 686 double_click = FALSE; 687 for (i = 0; i < 3; i++) { 688 if (XawTextGetSource(texts[i]) == item->source) 689 XtSetValues(labels[i], args, num_args); 690 AddDoubleClickCallback(XawTextGetSource(texts[i]), False); 691 } 692 693 num_args = 0; 694 XtSetArg(args[num_args], XtNsourceChanged, False); ++num_args; 695 XtSetValues(item->source, args, num_args); 696 697 item->flags &= ~CHANGED_BIT; 698} 699 700/*ARGSUSED*/ 701void 702KillFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 703{ 704 xedit_flist_item *item = FindTextSource(XawTextGetSource(textwindow), NULL); 705 706 if (item->source == scratch) { 707 Feep(); 708 return; 709 } 710 711 if (item->flags & CHANGED_BIT) { 712 if (!double_click || (dc_state && dc_state != DC_KILL)) { 713 XeditPrintf("Kill: Unsaved changes. Kill again to override.\n"); 714 Feep(); 715 double_click = TRUE; 716 dc_state = DC_KILL; 717 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 718 return; 719 } 720 double_click = FALSE; 721 dc_state = 0; 722 } 723 KillTextSource(item); 724} 725 726/*ARGSUSED*/ 727void 728FindFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 729{ 730 char *string = GetString(filenamewindow); 731 char *slash = NULL; 732 XawTextBlock block; 733 XawTextPosition end = XawTextSourceScan(XawTextGetSource(filenamewindow), 734 0, XawstAll, XawsdRight, 1, True); 735 736 if (string) 737 slash = strrchr(string, '/'); 738 block.firstPos = 0; 739 block.format = FMT8BIT; 740 block.ptr = string; 741 block.length = slash ? slash - string + 1 : 0; 742 743 if (block.length != end) 744 XawTextReplace(filenamewindow, 0, end, &block); 745 XawTextSetInsertionPoint(filenamewindow, end); 746 XtSetKeyboardFocus(topwindow, filenamewindow); 747 line_edit = False; 748} 749 750/*ARGSUSED*/ 751void 752LoadFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 753{ 754 if (line_edit) 755 LineEdit(textwindow); 756 else if (ReallyDoLoad(GetString(filenamewindow), ResolveName(NULL))) { 757 SwitchDirWindow(False); 758 XtSetKeyboardFocus(topwindow, textwindow); 759 } 760} 761 762/*ARGSUSED*/ 763void 764CancelFindFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 765{ 766 Arg args[1]; 767 xedit_flist_item *item; 768 769 XtSetKeyboardFocus(topwindow, textwindow); 770 771 item = FindTextSource(XawTextGetSource(textwindow), NULL); 772 773 if (item->source != scratch) 774 XtSetArg(args[0], XtNstring, item->name); 775 else 776 XtSetArg(args[0], XtNstring, NULL); 777 778 XtSetValues(filenamewindow, args, 1); 779 780 if (XtIsManaged(XtParent(dirwindow))) 781 SwitchDirWindow(False); 782 783 line_edit = False; 784} 785 786static int 787compar(_Xconst void *a, _Xconst void *b) 788{ 789 return (strcmp(*(char **)a, *(char **)b)); 790} 791 792/*ARGSUSED*/ 793void 794FileCompletion(Widget w, XEvent *event, String *params, Cardinal *num_params) 795{ 796 XawTextBlock block; 797 String text; 798 int length; 799 char **matches, *save, *dir_name, *file_name, match[257]; 800 unsigned n_matches, len, mlen, buflen; 801 DIR *dir; 802 Bool changed, slash = False, has_dot = False; 803#define SM_NEVER 0 804#define SM_HINT 1 805#define SM_ALWAYS 2 806 int show_matches; 807 808 text = GetString(filenamewindow); 809 810 if (!text) { 811 Feep(); 812 return; 813 } 814 else if (line_edit) { 815 Feep(); 816 line_edit = 0; 817 } 818 819 { 820 XawTextPosition pos = XawTextGetInsertionPoint(w); 821 char *cslash = strchr(&text[pos], '/'), *cdot = strchr(&text[pos], '.'); 822 823 if (cslash != NULL || cdot != NULL) { 824 if (cslash != NULL && (cdot == NULL || cdot > cslash)) { 825 length = cslash - text; 826 slash = True; 827 } 828 else { 829 length = cdot - text; 830 has_dot = True; 831 } 832 } 833 else 834 length = strlen(text); 835 } 836 837 if (*num_params == 1 && length == strlen(text)) { 838 switch (params[0][0]) { 839 case 'n': /* Never */ 840 case 'N': 841 show_matches = SM_NEVER; 842 break; 843 case 'h': /* Hint */ 844 case 'H': 845 show_matches = SM_HINT; 846 break; 847 case 'a': /* Always */ 848 case 'A': 849 show_matches = SM_ALWAYS; 850 break; 851 default: 852 show_matches = SM_NEVER; 853 XtAppWarning(XtWidgetToApplicationContext(w), 854 "Bad argument to file-completion, " 855 "must be Never, Hint or Always"); 856 break; 857 } 858 } 859 else 860 show_matches = SM_NEVER; 861 862 matches = NULL; 863 n_matches = buflen = 0; 864 save = XtMalloc(length + 1); 865 memmove(save, text, length); 866 save[length] = '\0'; 867 868 if (save[0] == '~' && save[1]) { 869 char *slash2 = strchr(save, '/'); 870 871 if (slash2) { 872 struct passwd *pw; 873 char home[BUFSIZ]; 874 char *name; 875 int slen = strlen(save), diff = slash2 - save; 876 877 *slash2 = '\0'; 878 name = save + 1; 879 if (strlen(name) != 0) 880 pw = getpwnam(name); 881 else 882 pw = getpwuid(getuid()); 883 884 if (pw) { 885 char fname[BUFSIZ]; 886 int hlen; 887 888 strncpy(home, pw->pw_dir, sizeof(home) - 1); 889 home[sizeof(home) - 1] = '\0'; 890 hlen = strlen(home); 891 strncpy(fname, slash2 + 1, sizeof(fname) - 1); 892 fname[sizeof(fname) - 1] = '\0'; 893 save = XtRealloc(save, slen - diff + hlen + 2); 894 (void)memmove(&save[hlen], slash2, slen - diff + 1); 895 (void)memmove(save, home, hlen); 896 save[hlen] = '/'; 897 strcpy(&save[hlen + 1], fname); 898 899 /* expand directory */ 900 block.length = strlen(save); 901 block.ptr = save; 902 block.firstPos = 0; 903 block.format = FMT8BIT; 904 XawTextReplace(filenamewindow, 0, length, &block); 905 XawTextSetInsertionPoint(filenamewindow, length = block.length); 906 } 907 else 908 *slash2 = '/'; 909 } 910 } 911 912 if ((file_name = strrchr(save, '/')) != NULL) { 913 *file_name = '\0'; 914 ++file_name; 915 dir_name = save; 916 if (!file_name[0]) 917 slash = True; 918 if (!dir_name[0]) 919 dir_name = "/"; 920 } 921 else { 922 dir_name = "."; 923 file_name = save; 924 } 925 len = strlen(file_name); 926 927 if ((dir = opendir(dir_name)) != NULL) { 928 char path[BUFSIZ], *pptr; 929 struct dirent *ent; 930 int isdir = 0, first = 1, bytes; 931 932 XmuSnprintf(path, sizeof(path), "%s/", dir_name); 933 pptr = path + strlen(path); 934 bytes = sizeof(path) - (pptr - path) - 1; 935 936 mlen = 0; 937 match[0] = '\0'; 938 (void)readdir(dir); /* "." */ 939 (void)readdir(dir); /* ".." */ 940 while ((ent = readdir(dir)) != NULL) { 941 unsigned d_namlen = strlen(ent->d_name); 942 943 if (d_namlen >= len && strncmp(ent->d_name, file_name, len) == 0) { 944 char *tmp = &(ent->d_name[len]), *mat = match; 945 struct stat st; 946 Bool is_dir = FALSE; 947 948 strncpy(pptr, ent->d_name, bytes); 949 pptr[bytes] = '\0'; 950 if (stat(path, &st) != 0) 951 /* Should check errno, may be a broken symbolic link 952 * a directory with r-- permission, etc */ 953 continue; 954 else if (first || show_matches != SM_NEVER) { 955 is_dir = S_ISDIR(st.st_mode); 956 } 957 958 if (first) { 959 strncpy(match, tmp, sizeof(match) - 1); 960 match[sizeof(match) - 2] = '\0'; 961 mlen = strlen(match); 962 first = 0; 963 isdir = is_dir; 964 } 965 else { 966 while (*tmp && *mat && *tmp++ == *mat) 967 ++mat; 968 if (mlen > mat - match) { 969 mlen = mat - match; 970 match[mlen] = '\0'; 971 } 972 } 973 if (show_matches != SM_NEVER) { 974 matches = (char **)XtRealloc((char*)matches, sizeof(char**) 975 * (n_matches + 1)); 976 buflen += d_namlen + 1; 977 if (is_dir) { 978 matches[n_matches] = XtMalloc(d_namlen + 2); 979 strcpy(matches[n_matches], ent->d_name); 980 strcat(matches[n_matches], "/"); 981 ++buflen; 982 } 983 else 984 matches[n_matches] = XtNewString(ent->d_name); 985 } 986 else if (mlen == 0 && n_matches >= 1) { 987 ++n_matches; 988 break; 989 } 990 ++n_matches; 991 } 992 } 993 994 closedir(dir); 995 changed = mlen != 0; 996 997 if (n_matches) { 998 Bool free_matches = True, add_slash = n_matches == 1 && isdir && !slash; 999 1000 if (mlen && has_dot && match[mlen - 1] == '.') 1001 --mlen; 1002 1003 if (mlen || add_slash) { 1004 XawTextPosition pos; 1005 1006 block.firstPos = 0; 1007 block.format = FMT8BIT; 1008 if (mlen) { 1009 pos = length; 1010 block.length = mlen; 1011 block.ptr = match; 1012 XawTextReplace(filenamewindow, pos, pos, &block); 1013 XawTextSetInsertionPoint(filenamewindow, pos + block.length); 1014 } 1015 if (add_slash) { 1016 XawTextPosition actual = XawTextGetInsertionPoint(w); 1017 1018 pos = XawTextSourceScan(XawTextGetSource(w), 0, XawstAll, 1019 XawsdRight, 1, True); 1020 block.length = 1; 1021 block.ptr = "/"; 1022 XawTextReplace(filenamewindow, pos, pos, &block); 1023 if (actual == pos) 1024 XawTextSetInsertionPoint(filenamewindow, pos + 1); 1025 } 1026 } 1027 else if (n_matches != 1 || isdir) { 1028 if (show_matches == SM_NEVER) 1029 Feep(); 1030 } 1031 1032 if (show_matches != SM_NEVER) { 1033 if (show_matches == SM_ALWAYS || (!changed && n_matches != 1)) { 1034 char **list = NULL, *label; 1035 int n_list; 1036 Arg args[2]; 1037 1038 XtSetArg(args[0], XtNlist, &list); 1039 XtSetArg(args[1], XtNnumberStrings, &n_list); 1040 XtGetValues(dirwindow, args, 2); 1041 1042 matches = (char **)XtRealloc((char*)matches, sizeof(char**) 1043 * (n_matches + 2)); 1044 matches[n_matches++] = XtNewString("./"); 1045 matches[n_matches++] = XtNewString("../"); 1046 qsort(matches, n_matches, sizeof(char*), compar); 1047 XtSetArg(args[0], XtNlist, matches); 1048 XtSetArg(args[1], XtNnumberStrings, n_matches); 1049 XtSetValues(dirwindow, args, 2); 1050 if (n_list > 0 1051 && (n_list != 1 || list[0] != XtName(dirwindow))) { 1052 while (--n_list > -1) 1053 XtFree(list[n_list]); 1054 XtFree((char*)list); 1055 } 1056 1057 label = ResolveName(dir_name); 1058 XtSetArg(args[0], XtNlabel, label); 1059 XtSetValues(dirlabel, args, 1); 1060 SwitchDirWindow(True); 1061 free_matches = False; 1062 } 1063 } 1064 if (free_matches && matches) { 1065 while (--n_matches > -1) 1066 XtFree(matches[n_matches]); 1067 XtFree((char*)matches); 1068 } 1069 } 1070 else 1071 Feep(); 1072 } 1073 else 1074 Feep(); 1075 1076 XtFree(save); 1077} 1078 1079/*ARGSUSED*/ 1080void 1081DirWindowCB(Widget w, XtPointer user_data, XtPointer call_data) 1082{ 1083 XawListReturnStruct *file_info = (XawListReturnStruct *)call_data; 1084 char *dir_name, *string, path[BUFSIZ]; 1085 Arg args[2]; 1086 1087 if (file_info == NULL) 1088 string = (char *)user_data; 1089 else 1090 string = file_info->string; 1091 1092 XtSetArg(args[0], XtNlabel, &dir_name); 1093 XtGetValues(dirlabel, args, 1); 1094 if (*dir_name == '\0') { 1095 strncpy(path, string, sizeof(path) - 1); 1096 path[sizeof(path) - 1] = '\0'; 1097 } 1098 else if (strcmp(dir_name, "/") == 0) 1099 XmuSnprintf(path, sizeof(path), "/%s", string); 1100 else 1101 XmuSnprintf(path, sizeof(path), "%s/%s", dir_name, string); 1102 1103 if (*string && string[strlen(string) - 1] == '/') { 1104 DIR *dir; 1105 1106 if ((dir = opendir(path)) != NULL) { 1107 struct dirent *ent; 1108 struct stat st; 1109 unsigned d_namlen; 1110 Bool isdir; 1111 char **entries = NULL, **list = NULL; 1112 int n_entries = 0, n_list = 0; 1113 char *label, *pptr = path + strlen(path); 1114 int bytes = sizeof(path) - (pptr - path) - 1; 1115 1116 while ((ent = readdir(dir)) != NULL) { 1117 d_namlen = strlen(ent->d_name); 1118 strncpy(pptr, ent->d_name, bytes); 1119 pptr[bytes] = '\0'; 1120 if (stat(path, &st) != 0) 1121 /* Should check errno, may be a broken symbolic link 1122 * a directory with r-- permission, etc */ 1123 continue; 1124 else 1125 isdir = S_ISDIR(st.st_mode); 1126 1127 entries = (char **)XtRealloc((char*)entries, sizeof(char*) 1128 * (n_entries + 1)); 1129 if (isdir) { 1130 entries[n_entries] = XtMalloc(d_namlen + 2); 1131 strcpy(entries[n_entries], ent->d_name); 1132 strcat(entries[n_entries], "/"); 1133 } 1134 else 1135 entries[n_entries] = XtNewString(ent->d_name); 1136 ++n_entries; 1137 } 1138 closedir(dir); 1139 1140 XtSetArg(args[0], XtNlist, &list); 1141 XtSetArg(args[1], XtNnumberStrings, &n_list); 1142 XtGetValues(dirwindow, args, 2); 1143 1144 if (n_entries == 0) { 1145 entries = (char**)XtMalloc(sizeof(char*) * 2); 1146 /* Directory has read but not execute permission? */ 1147 entries[n_entries++] = XtNewString("./"); 1148 entries[n_entries++] = XtNewString("../"); 1149 } 1150 qsort(entries, n_entries, sizeof(char*), compar); 1151 XtSetArg(args[0], XtNlist, entries); 1152 XtSetArg(args[1], XtNnumberStrings, n_entries); 1153 XtSetValues(dirwindow, args, 2); 1154 if (n_list > 0 1155 && (n_list != 1 || list[0] != XtName(dirwindow))) { 1156 while (--n_list > -1) 1157 XtFree(list[n_list]); 1158 XtFree((char*)list); 1159 } 1160 1161 *pptr = '\0'; 1162 if ((label = ResolveName(path)) == NULL) { 1163 Feep(); 1164 label = path; 1165 } 1166 XtSetArg(args[0], XtNlabel, label); 1167 XtSetValues(dirlabel, args, 1); 1168 1169 strncpy(path, label, sizeof(path) - 2); 1170 if (*path && path[strlen(path) - 1] != '/') 1171 strcat(path, "/"); 1172 XtSetArg(args[0], XtNstring, path); 1173 XtSetValues(filenamewindow, args, 1); 1174 XtSetKeyboardFocus(topwindow, filenamewindow); 1175 XawTextSetInsertionPoint(filenamewindow, strlen(path)); 1176 } 1177 else 1178 Feep(); 1179 } 1180 else { 1181 (void)ReallyDoLoad(path, path); 1182 SwitchDirWindow(False); 1183 XtSetKeyboardFocus(topwindow, textwindow); 1184 } 1185} 1186