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