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#include <stdlib.h> 33#include <stdio.h> 34#include <limits.h> 35#include <string.h> 36#include <dirent.h> 37#include <pwd.h> 38#include <sys/stat.h> 39#include <X11/Xmu/SysUtil.h> 40#include <X11/IntrinsicP.h> 41#include <X11/Xaw/TextSrcP.h> 42 43/* Turn a NULL pointer string into an empty string */ 44#define NULLSTR(x) (((x)!=NULL)?(x):("")) 45 46#define Error(x) { printf x ; exit(EXIT_FAILURE); } 47#define Assertion(expr, msg) { if (!(expr)) { Error msg } } 48#define Log(x) { if (True) printf x; } 49 50void ResetSourceChanged(xedit_flist_item*); 51static void ResetDC(Widget, XtPointer, XtPointer); 52 53static void AddDoubleClickCallback(Widget, Bool); 54static Bool ReallyDoLoad(char*, char*); 55static char *makeBackupName(String, String, unsigned); 56 57/* 58 * External 59 */ 60extern void _XawTextShowPosition(TextWidget); 61 62extern Widget scratch, texts[3], labels[3]; 63 64#define DC_UNSAVED (1 << 0) 65#define DC_LOADED (1 << 1) 66#define DC_CLOBBER (1 << 2) 67#define DC_KILL (1 << 3) 68#define DC_SAVE (1 << 4) 69#define DC_NEWER (1 << 5) 70static int dc_state; 71 72static void 73AddDoubleClickCallback(Widget w, Bool state) 74{ 75 if (state) 76 XtAddCallback(w, XtNcallback, ResetDC, NULL); 77 else 78 XtRemoveCallback(w, XtNcallback, ResetDC, NULL); 79} 80 81/* Function Name: ResetDC 82 * Description: Resets the double click flag. 83 * Arguments: w - the text widget. 84 * junk, garbage - *** NOT USED *** 85 * Returns: none. 86 */ 87 88/* ARGSUSED */ 89static void 90ResetDC(Widget w, XtPointer junk, XtPointer garbage) 91{ 92 AddDoubleClickCallback(w, FALSE); 93} 94 95/*ARGSUSED*/ 96void 97QuitAction(Widget w, XEvent *event, String *params, Cardinal *num_params) 98{ 99 DoQuit(w, NULL, NULL); 100} 101 102/*ARGSUSED*/ 103void 104DoQuit(Widget w, XtPointer client_data, XtPointer call_data) 105{ 106 unsigned i; 107 Bool source_changed = False; 108 109 if (!(dc_state & DC_UNSAVED)) { 110 for (i = 0; i < flist.num_itens; i++) 111 if (flist.itens[i]->flags & CHANGED_BIT) { 112 source_changed = True; 113 break; 114 } 115 } 116 if (!source_changed) { 117 XeditLispCleanUp(); 118 exit(0); 119 } 120 121 XeditPrintf("Unsaved changes. Save them, or Quit again.\n"); 122 Feep(); 123 dc_state |= DC_UNSAVED; 124 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 125} 126 127static char * 128makeBackupName(String buf, String filename, unsigned len) 129{ 130 if (app_resources.backupNamePrefix 131 && strlen(app_resources.backupNamePrefix)) { 132 if (strchr(app_resources.backupNamePrefix, '/')) 133 XmuSnprintf(buf, len, "%s%s%s", app_resources.backupNamePrefix, 134 filename, app_resources.backupNameSuffix); 135 else { 136 char fname[BUFSIZ]; 137 char *name, ch; 138 139 strncpy(fname, filename, sizeof(fname) - 1); 140 fname[sizeof(fname) - 1] = '\0'; 141 if ((name = strrchr(fname, '/')) != NULL) 142 ++name; 143 else 144 name = filename; 145 ch = *name; 146 *name = '\0'; 147 ++name; 148 XmuSnprintf(buf, len, "%s%s%c%s%s", 149 fname, app_resources.backupNamePrefix, ch, name, 150 app_resources.backupNameSuffix); 151 } 152 } 153 else 154 XmuSnprintf(buf, len, "%s%s", 155 filename, app_resources.backupNameSuffix); 156 157 return (strcmp(filename, buf) ? buf : NULL); 158} 159 160/*ARGSUSED*/ 161void 162SaveFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 163{ 164 if (line_edit) { 165 /* Don't try to save buffer with regex string. 166 * Call CancelFindFile() to leave line_edit mode. 167 */ 168 XeditPrintf("Save: Leaving line edit mode -- nothing saved.\n"); 169 CancelFindFile(w, event, params, num_params); 170 Feep(); 171 } 172 else 173 DoSave(w, NULL, NULL); 174} 175 176/*ARGSUSED*/ 177void 178DoSave(Widget w, XtPointer client_data, XtPointer call_data) 179{ 180 String name = GetString(filenamewindow); 181 String filename = ResolveName(name); 182 FileAccess file_access; 183 xedit_flist_item *item; 184 Boolean exists; 185 Widget source = XawTextGetSource(textwindow); 186 char buffer[BUFSIZ]; 187 struct stat st; 188 static const char *nothing_saved = " -- nothing saved.\n"; 189 190 if (!filename) { 191 XmuSnprintf(buffer, sizeof(buffer), "%s%s", 192 "Save: Can't resolve pathname", nothing_saved); 193 goto error; 194 } 195 else if (*name == '\0') { 196 XmuSnprintf(buffer, sizeof(buffer), "%s%s", 197 "Save: No filename specified", nothing_saved); 198 goto error; 199 } 200 201 item = FindTextSource(NULL, filename); 202 if (item != NULL && item->source != source) { 203 if (!(dc_state & DC_LOADED)) { 204 XmuSnprintf(buffer, sizeof(buffer), "%s%s%s%s", 205 "Save: file ", name, " is already loaded, " 206 "Save again to unload it", nothing_saved); 207 Feep(); 208 dc_state |= DC_LOADED; 209 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 210 goto error; 211 } 212 else { 213 KillTextSource(item); 214 item = FindTextSource(source = XawTextGetSource(textwindow), NULL); 215 dc_state &= ~DC_LOADED; 216 } 217 } 218 else if (item && !(item->flags & CHANGED_BIT)) { 219 if (!(dc_state & DC_SAVE)) { 220 XmuSnprintf(buffer, sizeof(buffer), "%s%s", 221 "Save: No changes need to be saved, " 222 "save again to override", nothing_saved); 223 Feep(); 224 dc_state |= DC_SAVE; 225 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 226 goto error; 227 } 228 else 229 dc_state &= ~DC_SAVE; 230 } 231 232 file_access = CheckFilePermissions(filename, &exists); 233 if (exists) { 234 if (stat(filename, &st) != 0) { 235 XmuSnprintf(buffer, sizeof(buffer), "%s%s%s", 236 "Save: cannot stat ", name, nothing_saved); 237 goto error; 238 } 239 else if (!S_ISREG(st.st_mode)) { 240 XmuSnprintf(buffer, sizeof(buffer), "%s%s%s%s", 241 "Save: file ", name, "is not a regular file", 242 nothing_saved); 243 goto error; 244 } 245 } 246 247 if (!item || strcmp(item->filename, filename)) { 248 if (file_access == WRITE_OK && exists) { 249 if (!(dc_state & DC_CLOBBER)) { 250 XmuSnprintf(buffer, sizeof(buffer), "%s%s%s%s", 251 "Save: file ", name, " already exists, " 252 "save again to override", nothing_saved); 253 Feep(); 254 dc_state |= DC_CLOBBER; 255 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 256 goto error; 257 } 258 else 259 dc_state &= ~DC_CLOBBER; 260 } 261 if (!item) 262 item = FindTextSource(source, NULL); 263 } 264 265 if (item && item->mtime && exists && item->mtime < st.st_mtime) { 266 if (!(dc_state & DC_NEWER)) { 267 XmuSnprintf(buffer, sizeof(buffer), "%s%s", 268 "Save: Newer file exists, " 269 "save again to override", nothing_saved); 270 Feep(); 271 dc_state |= DC_NEWER; 272 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 273 goto error; 274 } 275 else 276 dc_state &= DC_NEWER; 277 } 278 279 if (app_resources.enableBackups && exists) { 280 char backup_file[BUFSIZ]; 281 282 if (makeBackupName(backup_file, filename, sizeof(backup_file)) == NULL 283 || rename(filename, backup_file) != 0) { 284 XeditPrintf("Error backing up file: %s\n", filename); 285 } 286 } 287 288 switch (file_access = MaybeCreateFile(filename)) { 289 case NO_READ: 290 case READ_OK: 291 XeditPrintf("File %s could not be opened for writing.\n", name); 292 Feep(); 293 break; 294 case WRITE_OK: 295 if (XawAsciiSaveAsFile(source, filename)) { 296 int i; 297 Arg args[1]; 298 299 XmuSnprintf(buffer, sizeof(buffer), 300 "%s Read - Write", name); 301 XtSetArg(args[0], XtNlabel, buffer); 302 for (i = 0; i < 3; i++) 303 if (XawTextGetSource(texts[i]) == source) 304 XtSetValues(labels[i], args, 1); 305 306 XeditPrintf("Saved file: %s\n", name); 307 308 if (item && item->source != scratch) { 309 XtSetArg(args[0], XtNlabel, filename); 310 XtSetValues(item->sme, args, 1); 311 312 XtSetArg(args[0], XtNeditType, XawtextEdit); 313 XtSetValues(item->source, args, 1); 314 315 XtFree(item->name); 316 XtFree(item->filename); 317 item->name = XtNewString(name); 318 item->filename = XtNewString(filename); 319 item->flags = EXISTS_BIT; 320 } 321 else { 322 item = flist.itens[0]; 323 XtRemoveCallback(scratch, XtNcallback, SourceChanged, 324 (XtPointer)item); 325 item->source = scratch = 326 XtVaCreateWidget("textSource", international ? 327 multiSrcObjectClass : 328 asciiSrcObjectClass, 329 topwindow, 330 XtNtype, XawAsciiFile, 331 XtNeditType, XawtextEdit, 332 NULL, NULL); 333 ResetSourceChanged(item); 334 XtAddCallback(scratch, XtNcallback, SourceChanged, 335 (XtPointer)item); 336 337 item = AddTextSource(source, name, filename, EXISTS_BIT, 338 file_access); 339 XtAddCallback(item->source, XtNcallback, SourceChanged, 340 (XtPointer)item); 341 } 342 343 /* Keep file protection mode */ 344 if (item->mode) 345 chmod(filename, item->mode); 346 347 /* Remember time of last modification */ 348 if (stat(filename, &st) == 0) 349 item->mtime = st.st_mtime; 350 351 item->flags |= EXISTS_BIT; 352 ResetSourceChanged(item); 353 } 354 else { 355 XeditPrintf("Error saving file: %s\n", name); 356 Feep(); 357 } 358 break; 359 default: 360 Feep(); 361 break; 362 } 363 364 return; 365error: 366 XeditPrintf("%s", buffer); 367 Feep(); 368} 369 370/*ARGSUSED*/ 371void 372DoLoad(Widget w, XtPointer client_data, XtPointer call_data) 373{ 374 if (ReallyDoLoad(GetString(filenamewindow), ResolveName(NULL))) { 375 SwitchDirWindow(False); 376 XtSetKeyboardFocus(topwindow, textwindow); 377 } 378} 379 380Bool 381LoadFileInTextwindow(char *name, char *resolved_name) 382{ 383 return (ReallyDoLoad(name, resolved_name)); 384} 385 386static Bool 387ReallyDoLoad(char *name, char *filename) 388{ 389 Arg args[5]; 390 Cardinal num_args = 0; 391 xedit_flist_item *item; 392 Widget source = XawTextGetSource(textwindow); 393 394 if (!filename) { 395 XeditPrintf("Load: Can't resolve pathname.\n"); 396 Feep(); 397 return (False); 398 } 399 else if (*name == '\0') { 400 XeditPrintf("Load: No file specified.\n"); 401 Feep(); 402 } 403 if ((item = FindTextSource(NULL, filename)) != NULL) { 404 SwitchTextSource(item); 405 return (True); 406 } 407 else { 408 struct stat st; 409 410 if (stat(filename, &st) == 0 && !S_ISREG(st.st_mode)) { 411 if (S_ISDIR(st.st_mode)) { 412 char path[BUFSIZ + 1]; 413 414 strncpy(path, filename, sizeof(path) - 2); 415 path[sizeof(path) - 2] = '\0'; 416 if (*path) { 417 if (path[strlen(path) - 1] != '/') 418 strcat(path, "/"); 419 } 420 else 421 strcpy(path, "./"); 422 XtSetArg(args[0], XtNlabel, ""); 423 XtSetValues(dirlabel, args, 1); 424 SwitchDirWindow(True); 425 DirWindowCB(dirwindow, path, NULL); 426 return (False); 427 } 428 } 429 } 430 431 { 432 Boolean exists; 433 int flags; 434 FileAccess file_access; 435 436 switch( file_access = CheckFilePermissions(filename, &exists) ) { 437 case NO_READ: 438 if (exists) 439 XeditPrintf("File %s, %s", name, 440 "exists, and could not be opened for reading.\n"); 441 else 442 XeditPrintf("File %s %s %s", name, 443 "does not exist, and", 444 "the directory could not be opened for writing.\n"); 445 446 Feep(); 447 return (False); 448 case READ_OK: 449 XtSetArg(args[num_args], XtNeditType, XawtextRead); num_args++; 450 XeditPrintf("File %s opened READ ONLY.\n", name); 451 break; 452 case WRITE_OK: 453 XtSetArg(args[num_args], XtNeditType, XawtextEdit); num_args++; 454 XeditPrintf("File %s opened read - write.\n", name); 455 break; 456 default: 457 Feep(); 458 return (False); 459 } 460 461 if (exists) { 462 flags = EXISTS_BIT; 463 XtSetArg(args[num_args], XtNstring, filename); num_args++; 464 } 465 else { 466 flags = 0; 467 XtSetArg(args[num_args], XtNstring, NULL); num_args++; 468 } 469 470 source = XtVaCreateWidget("textSource", international ? 471 multiSrcObjectClass : 472 asciiSrcObjectClass, 473 topwindow, 474 XtNtype, XawAsciiFile, 475 XtNeditType, XawtextEdit, 476 NULL, NULL); 477 XtSetValues(source, args, num_args); 478 479 item = AddTextSource(source, name, filename, flags, file_access); 480 XtAddCallback(item->source, XtNcallback, SourceChanged, 481 (XtPointer)item); 482 if (exists && file_access == WRITE_OK) { 483 struct stat st; 484 485 if (stat(item->filename, &st) == 0) { 486 item->mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); 487 item->mtime = st.st_mtime; 488 } 489 } 490 SwitchTextSource(item); 491 ResetSourceChanged(item); 492 } 493 494 return (True); 495} 496 497/* Function Name: SourceChanged 498 * Description: A callback routine called when the source has changed. 499 * Arguments: w - the text source that has changed. 500 * client_data - xedit_flist_item associated with text buffer. 501 * call_data - NULL is unchanged 502 * Returns: none. 503 */ 504/*ARGSUSED*/ 505void 506SourceChanged(Widget w, XtPointer client_data, XtPointer call_data) 507{ 508 xedit_flist_item *item = (xedit_flist_item*)client_data; 509 Bool changed = (Bool)(long)call_data; 510 511 if (changed) { 512 if (item->flags & CHANGED_BIT) 513 return; 514 item->flags |= CHANGED_BIT; 515 } 516 else { 517 if (item->flags & CHANGED_BIT) 518 ResetSourceChanged(item); 519 return; 520 } 521 522 if (flist.pixmap) { 523 Arg args[1]; 524 Cardinal num_args; 525 int i; 526 527 num_args = 0; 528 XtSetArg(args[num_args], XtNleftBitmap, flist.pixmap); ++num_args; 529 XtSetValues(item->sme, args, num_args); 530 531 for (i = 0; i < 3; i++) 532 if (XawTextGetSource(texts[i]) == item->source) 533 XtSetValues(labels[i], args, num_args); 534 } 535} 536 537/* Function Name: ResetSourceChanged. 538 * Description: Sets the source changed to FALSE, and 539 * registers a callback to set it to TRUE when 540 * the source has changed. 541 * Arguments: item - item with widget to register the callback on. 542 * Returns: none. 543 */ 544 545void 546ResetSourceChanged(xedit_flist_item *item) 547{ 548 Arg args[1]; 549 Cardinal num_args; 550 int i; 551 552 num_args = 0; 553 XtSetArg(args[num_args], XtNleftBitmap, None); ++num_args; 554 XtSetValues(item->sme, args, num_args); 555 556 dc_state = 0; 557 for (i = 0; i < 3; i++) { 558 if (XawTextGetSource(texts[i]) == item->source) 559 XtSetValues(labels[i], args, num_args); 560 AddDoubleClickCallback(XawTextGetSource(texts[i]), False); 561 } 562 563 num_args = 0; 564 XtSetArg(args[num_args], XtNsourceChanged, False); ++num_args; 565 XtSetValues(item->source, args, num_args); 566 567 item->flags &= ~CHANGED_BIT; 568} 569 570/*ARGSUSED*/ 571void 572KillFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 573{ 574 xedit_flist_item *item = FindTextSource(XawTextGetSource(textwindow), NULL); 575 576 if (item->source == scratch) { 577 Feep(); 578 return; 579 } 580 581 if (item->flags & CHANGED_BIT) { 582 if (!(dc_state & DC_KILL)) { 583 XeditPrintf("Kill: Unsaved changes. Kill again to override.\n"); 584 Feep(); 585 dc_state |= DC_KILL; 586 AddDoubleClickCallback(XawTextGetSource(textwindow), True); 587 return; 588 } 589 dc_state &= ~DC_KILL; 590 } 591 KillTextSource(item); 592} 593 594/*ARGSUSED*/ 595void 596FindFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 597{ 598 char *string; 599 char *slash; 600 XawTextBlock block; 601 XawTextPosition end = XawTextSourceScan(XawTextGetSource(filenamewindow), 602 0, XawstAll, XawsdRight, 1, True); 603 604 slash = NULL; 605 if (!line_edit) { 606 string = GetString(filenamewindow); 607 if (string) 608 slash = strrchr(string, '/'); 609 } 610 else { 611 string = ""; 612 line_edit = False; 613 } 614 615 block.firstPos = 0; 616 block.format = FMT8BIT; 617 block.ptr = string; 618 block.length = slash ? slash - string + 1 : 0; 619 620 if (block.length != end) 621 XawTextReplace(filenamewindow, 0, end, &block); 622 XawTextSetInsertionPoint(filenamewindow, end); 623 XtSetKeyboardFocus(topwindow, filenamewindow); 624} 625 626/*ARGSUSED*/ 627void 628LoadFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 629{ 630 if (line_edit) 631 LineEdit(textwindow); 632 else if (ReallyDoLoad(GetString(filenamewindow), ResolveName(NULL))) { 633 SwitchDirWindow(False); 634 XtSetKeyboardFocus(topwindow, textwindow); 635 } 636} 637 638/*ARGSUSED*/ 639void 640CancelFindFile(Widget w, XEvent *event, String *params, Cardinal *num_params) 641{ 642 Arg args[1]; 643 xedit_flist_item *item; 644 645 XtSetKeyboardFocus(topwindow, textwindow); 646 647 item = FindTextSource(XawTextGetSource(textwindow), NULL); 648 649 if (item->source != scratch) 650 XtSetArg(args[0], XtNstring, item->name); 651 else 652 XtSetArg(args[0], XtNstring, NULL); 653 654 XtSetValues(filenamewindow, args, 1); 655 /* XXX This probably should be done by the TextWidget, i.e. notice 656 * if the cursor became inivisible due to an horizontal scroll */ 657 _XawTextShowPosition((TextWidget)filenamewindow); 658 659 if (XtIsManaged(XtParent(dirwindow))) 660 SwitchDirWindow(False); 661 662 line_edit = False; 663} 664 665static int 666compar(_Xconst void *a, _Xconst void *b) 667{ 668 return (strcmp(*(char **)a, *(char **)b)); 669} 670 671/*ARGSUSED*/ 672void 673FileCompletion(Widget w, XEvent *event, String *params, Cardinal *num_params) 674{ 675 XawTextBlock block; 676 String text; 677 int length; 678 char **matches, *save, *dir_name, *file_name, match[257]; 679 unsigned n_matches, len, mlen, buflen; 680 DIR *dir; 681 Bool changed, slash = False, has_dot = False; 682#define SM_NEVER 0 683#define SM_HINT 1 684#define SM_ALWAYS 2 685 int show_matches; 686 687 text = GetString(filenamewindow); 688 689 if (!text) { 690 Feep(); 691 return; 692 } 693 else if (line_edit) { 694 Feep(); 695 line_edit = 0; 696 } 697 698 { 699 XawTextPosition pos = XawTextGetInsertionPoint(w); 700 char *cslash = strchr(&text[pos], '/'), *cdot = strchr(&text[pos], '.'); 701 702 if (cslash != NULL || cdot != NULL) { 703 if (cslash != NULL && (cdot == NULL || cdot > cslash)) { 704 length = cslash - text; 705 slash = True; 706 } 707 else { 708 length = cdot - text; 709 has_dot = True; 710 } 711 } 712 else 713 length = strlen(text); 714 } 715 716 if (*num_params == 1 && length == strlen(text)) { 717 switch (params[0][0]) { 718 case 'n': /* Never */ 719 case 'N': 720 show_matches = SM_NEVER; 721 break; 722 case 'h': /* Hint */ 723 case 'H': 724 show_matches = SM_HINT; 725 break; 726 case 'a': /* Always */ 727 case 'A': 728 show_matches = SM_ALWAYS; 729 break; 730 default: 731 show_matches = SM_NEVER; 732 XtAppWarning(XtWidgetToApplicationContext(w), 733 "Bad argument to file-completion, " 734 "must be Never, Hint or Always"); 735 break; 736 } 737 } 738 else 739 show_matches = SM_NEVER; 740 741 matches = NULL; 742 n_matches = buflen = 0; 743 save = XtMalloc(length + 1); 744 memmove(save, text, length); 745 save[length] = '\0'; 746 747 if (save[0] == '~' && save[1]) { 748 char *slash2 = strchr(save, '/'); 749 750 if (slash2) { 751 struct passwd *pw; 752 char home[BUFSIZ]; 753 char *name; 754 int slen = strlen(save), diff = slash2 - save; 755 756 *slash2 = '\0'; 757 name = save + 1; 758 if (strlen(name) != 0) 759 pw = getpwnam(name); 760 else 761 pw = getpwuid(getuid()); 762 763 if (pw) { 764 char fname[BUFSIZ]; 765 int hlen; 766 767 strncpy(home, pw->pw_dir, sizeof(home) - 1); 768 home[sizeof(home) - 1] = '\0'; 769 hlen = strlen(home); 770 strncpy(fname, slash2 + 1, sizeof(fname) - 1); 771 fname[sizeof(fname) - 1] = '\0'; 772 save = XtRealloc(save, slen - diff + hlen + 2); 773 (void)memmove(save, home, hlen); 774 save[hlen] = '/'; 775 strcpy(&save[hlen + 1], fname); 776 777 /* expand directory */ 778 block.length = strlen(save); 779 block.ptr = save; 780 block.firstPos = 0; 781 block.format = FMT8BIT; 782 XawTextReplace(filenamewindow, 0, length, &block); 783 XawTextSetInsertionPoint(filenamewindow, length = block.length); 784 } 785 else 786 *slash2 = '/'; 787 } 788 } 789 790 if ((file_name = strrchr(save, '/')) != NULL) { 791 *file_name = '\0'; 792 ++file_name; 793 dir_name = save; 794 if (!file_name[0]) 795 slash = True; 796 if (!dir_name[0]) 797 dir_name = "/"; 798 } 799 else { 800 dir_name = "."; 801 file_name = save; 802 } 803 len = strlen(file_name); 804 805 if ((dir = opendir(dir_name)) != NULL) { 806 char path[BUFSIZ], *pptr; 807 struct dirent *ent; 808 int isdir = 0, first = 1, bytes; 809 810 XmuSnprintf(path, sizeof(path), "%s/", dir_name); 811 pptr = path + strlen(path); 812 bytes = sizeof(path) - (pptr - path) - 1; 813 814 mlen = 0; 815 match[0] = '\0'; 816 while ((ent = readdir(dir)) != NULL) { 817 unsigned d_namlen = strlen(ent->d_name); 818 819 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) 820 continue; 821 if (d_namlen >= len && strncmp(ent->d_name, file_name, len) == 0) { 822 char *tmp = &(ent->d_name[len]), *mat = match; 823 struct stat st; 824 Bool is_dir = FALSE; 825 826 strncpy(pptr, ent->d_name, bytes); 827 pptr[bytes] = '\0'; 828 if (stat(path, &st) != 0) 829 /* Should check errno, may be a broken symbolic link 830 * a directory with r-- permission, etc */ 831 continue; 832 else if (first || show_matches != SM_NEVER) { 833 is_dir = S_ISDIR(st.st_mode); 834 } 835 836 if (first) { 837 strncpy(match, tmp, sizeof(match) - 1); 838 match[sizeof(match) - 2] = '\0'; 839 mlen = strlen(match); 840 first = 0; 841 isdir = is_dir; 842 } 843 else { 844 while (*tmp && *mat && *tmp++ == *mat) 845 ++mat; 846 if (mlen > mat - match) { 847 mlen = mat - match; 848 match[mlen] = '\0'; 849 } 850 } 851 if (show_matches != SM_NEVER) { 852 matches = (char **)XtRealloc((char*)matches, sizeof(char**) 853 * (n_matches + 1)); 854 buflen += d_namlen + 1; 855 if (is_dir) { 856 matches[n_matches] = XtMalloc(d_namlen + 2); 857 strcpy(matches[n_matches], ent->d_name); 858 strcat(matches[n_matches], "/"); 859 ++buflen; 860 } 861 else 862 matches[n_matches] = XtNewString(ent->d_name); 863 } 864 else if (mlen == 0 && n_matches >= 1) { 865 ++n_matches; 866 break; 867 } 868 ++n_matches; 869 } 870 } 871 872 closedir(dir); 873 changed = mlen != 0; 874 875 if (first || n_matches) { 876 Bool free_matches = True, add_slash = n_matches == 1 && isdir && !slash; 877 878 if (mlen && has_dot && match[mlen - 1] == '.') 879 --mlen; 880 881 if (mlen || add_slash) { 882 XawTextPosition pos; 883 884 block.firstPos = 0; 885 block.format = FMT8BIT; 886 if (mlen) { 887 pos = length; 888 block.length = mlen; 889 block.ptr = match; 890 XawTextReplace(filenamewindow, pos, pos, &block); 891 XawTextSetInsertionPoint(filenamewindow, pos + block.length); 892 } 893 if (add_slash) { 894 XawTextPosition actual = XawTextGetInsertionPoint(w); 895 896 pos = XawTextSourceScan(XawTextGetSource(w), 0, XawstAll, 897 XawsdRight, 1, True); 898 block.length = 1; 899 block.ptr = "/"; 900 XawTextReplace(filenamewindow, pos, pos, &block); 901 if (actual == pos) 902 XawTextSetInsertionPoint(filenamewindow, pos + 1); 903 } 904 } 905 else if (n_matches != 1 || isdir) { 906 if (show_matches == SM_NEVER) 907 Feep(); 908 } 909 910 if (show_matches != SM_NEVER) { 911 if (show_matches == SM_ALWAYS || (!changed && n_matches != 1)) { 912 char **list = NULL, *label; 913 int n_list; 914 Arg args[2]; 915 916 XtSetArg(args[0], XtNlist, &list); 917 XtSetArg(args[1], XtNnumberStrings, &n_list); 918 XtGetValues(dirwindow, args, 2); 919 920 matches = (char **)XtRealloc((char*)matches, sizeof(char**) 921 * (n_matches + 2)); 922 matches[n_matches++] = XtNewString("./"); 923 matches[n_matches++] = XtNewString("../"); 924 qsort(matches, n_matches, sizeof(char*), compar); 925 XtSetArg(args[0], XtNlist, matches); 926 XtSetArg(args[1], XtNnumberStrings, n_matches); 927 XtSetValues(dirwindow, args, 2); 928 if (n_list > 0 929 && (n_list != 1 || list[0] != XtName(dirwindow))) { 930 while (--n_list > -1) 931 XtFree(list[n_list]); 932 XtFree((char*)list); 933 } 934 935 label = ResolveName(dir_name); 936 XtSetArg(args[0], XtNlabel, label); 937 XtSetValues(dirlabel, args, 1); 938 SwitchDirWindow(True); 939 free_matches = False; 940 } 941 } 942 if (free_matches && matches) { 943 while (--n_matches > -1) 944 XtFree(matches[n_matches]); 945 XtFree((char*)matches); 946 } 947 } 948 else 949 Feep(); 950 } 951 else 952 Feep(); 953 954 XtFree(save); 955} 956 957/*ARGSUSED*/ 958void 959DirWindowCB(Widget w, XtPointer user_data, XtPointer call_data) 960{ 961 XawListReturnStruct *file_info = (XawListReturnStruct *)call_data; 962 char *dir_name, *string, path[BUFSIZ]; 963 Arg args[2]; 964 965 if (file_info == NULL) 966 string = (char *)user_data; 967 else 968 string = file_info->string; 969 970 XtSetArg(args[0], XtNlabel, &dir_name); 971 XtGetValues(dirlabel, args, 1); 972 if (*dir_name == '\0') { 973 strncpy(path, string, sizeof(path) - 1); 974 path[sizeof(path) - 1] = '\0'; 975 } 976 else if (strcmp(dir_name, "/") == 0) 977 XmuSnprintf(path, sizeof(path), "/%s", string); 978 else 979 XmuSnprintf(path, sizeof(path), "%s/%s", dir_name, string); 980 981 if (*string && string[strlen(string) - 1] == '/') { 982 DIR *dir; 983 984 if ((dir = opendir(path)) != NULL) { 985 struct dirent *ent; 986 struct stat st; 987 unsigned d_namlen; 988 Bool isdir; 989 char **entries = NULL, **list = NULL; 990 int n_entries = 0, n_list = 0; 991 char *label, *pptr = path + strlen(path); 992 int bytes = sizeof(path) - (pptr - path) - 1; 993 994 while ((ent = readdir(dir)) != NULL) { 995 d_namlen = strlen(ent->d_name); 996 strncpy(pptr, ent->d_name, bytes); 997 pptr[bytes] = '\0'; 998 if (stat(path, &st) != 0) 999 /* Should check errno, may be a broken symbolic link 1000 * a directory with r-- permission, etc */ 1001 continue; 1002 else 1003 isdir = S_ISDIR(st.st_mode); 1004 1005 entries = (char **)XtRealloc((char*)entries, sizeof(char*) 1006 * (n_entries + 1)); 1007 if (isdir) { 1008 entries[n_entries] = XtMalloc(d_namlen + 2); 1009 strcpy(entries[n_entries], ent->d_name); 1010 strcat(entries[n_entries], "/"); 1011 } 1012 else 1013 entries[n_entries] = XtNewString(ent->d_name); 1014 ++n_entries; 1015 } 1016 closedir(dir); 1017 1018 XtSetArg(args[0], XtNlist, &list); 1019 XtSetArg(args[1], XtNnumberStrings, &n_list); 1020 XtGetValues(dirwindow, args, 2); 1021 1022 if (n_entries == 0) { 1023 entries = (char**)XtMalloc(sizeof(char*) * 2); 1024 /* Directory has read but not execute permission? */ 1025 entries[n_entries++] = XtNewString("./"); 1026 entries[n_entries++] = XtNewString("../"); 1027 } 1028 qsort(entries, n_entries, sizeof(char*), compar); 1029 XtSetArg(args[0], XtNlist, entries); 1030 XtSetArg(args[1], XtNnumberStrings, n_entries); 1031 XtSetValues(dirwindow, args, 2); 1032 if (n_list > 0 1033 && (n_list != 1 || list[0] != XtName(dirwindow))) { 1034 while (--n_list > -1) 1035 XtFree(list[n_list]); 1036 XtFree((char*)list); 1037 } 1038 1039 *pptr = '\0'; 1040 if ((label = ResolveName(path)) == NULL) { 1041 Feep(); 1042 label = path; 1043 } 1044 XtSetArg(args[0], XtNlabel, label); 1045 XtSetValues(dirlabel, args, 1); 1046 1047 strncpy(path, label, sizeof(path) - 2); 1048 if (*path && path[strlen(path) - 1] != '/') 1049 strcat(path, "/"); 1050 XtSetArg(args[0], XtNstring, path); 1051 XtSetValues(filenamewindow, args, 1); 1052 XtSetKeyboardFocus(topwindow, filenamewindow); 1053 XawTextSetInsertionPoint(filenamewindow, strlen(path)); 1054 } 1055 else 1056 Feep(); 1057 } 1058 else { 1059 (void)ReallyDoLoad(path, ResolveName(path)); 1060 SwitchDirWindow(False); 1061 XtSetKeyboardFocus(topwindow, textwindow); 1062 } 1063} 1064