diff options
| author | Stefan Monnier | 2008-05-13 05:16:43 +0000 |
|---|---|---|
| committer | Stefan Monnier | 2008-05-13 05:16:43 +0000 |
| commit | 3271a8f56cca6dc132ee3af157e87ab8faf42ab8 (patch) | |
| tree | 7273e7da207cfa9816c7aa38a3b81edcf44fd4a9 /src | |
| parent | f836b98e3139ff39834ceb2dcc4852c1c3e068c9 (diff) | |
| download | emacs-3271a8f56cca6dc132ee3af157e87ab8faf42ab8.tar.gz emacs-3271a8f56cca6dc132ee3af157e87ab8faf42ab8.zip | |
(file_name_completion): Tweak the code so as to always do it
in a single pass. Tighten the scope of some variables.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 3 | ||||
| -rw-r--r-- | src/dired.c | 315 |
2 files changed, 164 insertions, 154 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 86bc2e171ae..31bf04624b0 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | 2008-05-13 Stefan Monnier <monnier@iro.umontreal.ca> | 1 | 2008-05-13 Stefan Monnier <monnier@iro.umontreal.ca> |
| 2 | 2 | ||
| 3 | * dired.c (file_name_completion): Tweak the code so as to always do it | ||
| 4 | in a single pass. Tighten the scope of some variables. | ||
| 5 | |||
| 3 | * dired.c (Qdefault_directory): New var. | 6 | * dired.c (Qdefault_directory): New var. |
| 4 | (file_name_completion): Use it instead of Fexpand_file_name. | 7 | (file_name_completion): Use it instead of Fexpand_file_name. |
| 5 | (syms_of_dired): Initialize it. | 8 | (syms_of_dired): Initialize it. |
diff --git a/src/dired.c b/src/dired.c index f25f24e8e47..12e7d54ebd4 100644 --- a/src/dired.c +++ b/src/dired.c | |||
| @@ -466,8 +466,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate) | |||
| 466 | Lisp_Object predicate; | 466 | Lisp_Object predicate; |
| 467 | { | 467 | { |
| 468 | DIR *d; | 468 | DIR *d; |
| 469 | int bestmatchsize = 0, skip; | 469 | int bestmatchsize = 0; |
| 470 | register int compare, matchsize; | ||
| 471 | int matchcount = 0; | 470 | int matchcount = 0; |
| 472 | /* If ALL_FLAG is 1, BESTMATCH is the list of all matches, decoded. | 471 | /* If ALL_FLAG is 1, BESTMATCH is the list of all matches, decoded. |
| 473 | If ALL_FLAG is 0, BESTMATCH is either nil | 472 | If ALL_FLAG is 0, BESTMATCH is either nil |
| @@ -477,7 +476,10 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate) | |||
| 477 | Lisp_Object encoded_dir; | 476 | Lisp_Object encoded_dir; |
| 478 | struct stat st; | 477 | struct stat st; |
| 479 | int directoryp; | 478 | int directoryp; |
| 480 | int passcount; | 479 | /* If includeall is zero, exclude files in completion-ignored-extensions as |
| 480 | well as "." and "..". Until shown otherwise, assume we can't exclude | ||
| 481 | anything. */ | ||
| 482 | int includeall = 1; | ||
| 481 | int count = SPECPDL_INDEX (); | 483 | int count = SPECPDL_INDEX (); |
| 482 | struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; | 484 | struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; |
| 483 | 485 | ||
| @@ -518,76 +520,71 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate) | |||
| 518 | 520 | ||
| 519 | encoded_dir = ENCODE_FILE (dirname); | 521 | encoded_dir = ENCODE_FILE (dirname); |
| 520 | 522 | ||
| 521 | /* With passcount = 0, ignore files that end in an ignored extension. | 523 | BLOCK_INPUT; |
| 522 | If nothing found then try again with passcount = 1, don't ignore them. | 524 | d = opendir (SDATA (Fdirectory_file_name (encoded_dir))); |
| 523 | If looking for all completions, start with passcount = 1, | 525 | UNBLOCK_INPUT; |
| 524 | so always take even the ignored ones. | 526 | if (!d) |
| 527 | report_file_error ("Opening directory", Fcons (dirname, Qnil)); | ||
| 525 | 528 | ||
| 526 | ** It would not actually be helpful to the user to ignore any possible | 529 | record_unwind_protect (directory_files_internal_unwind, |
| 527 | completions when making a list of them.** */ | 530 | make_save_value (d, 0)); |
| 528 | 531 | ||
| 529 | for (passcount = !!all_flag; NILP (bestmatch) && passcount < 2; passcount++) | 532 | /* Loop reading blocks */ |
| 533 | /* (att3b compiler bug requires do a null comparison this way) */ | ||
| 534 | while (1) | ||
| 530 | { | 535 | { |
| 531 | int inner_count = SPECPDL_INDEX (); | 536 | DIRENTRY *dp; |
| 532 | 537 | int len; | |
| 533 | BLOCK_INPUT; | 538 | int canexclude = 0; |
| 534 | d = opendir (SDATA (Fdirectory_file_name (encoded_dir))); | ||
| 535 | UNBLOCK_INPUT; | ||
| 536 | if (!d) | ||
| 537 | report_file_error ("Opening directory", Fcons (dirname, Qnil)); | ||
| 538 | |||
| 539 | record_unwind_protect (directory_files_internal_unwind, | ||
| 540 | make_save_value (d, 0)); | ||
| 541 | |||
| 542 | /* Loop reading blocks */ | ||
| 543 | /* (att3b compiler bug requires do a null comparison this way) */ | ||
| 544 | while (1) | ||
| 545 | { | ||
| 546 | DIRENTRY *dp; | ||
| 547 | int len; | ||
| 548 | 539 | ||
| 549 | #ifdef VMS | 540 | #ifdef VMS |
| 550 | dp = (*readfunc) (d); | 541 | dp = (*readfunc) (d); |
| 551 | #else | 542 | #else |
| 552 | errno = 0; | 543 | errno = 0; |
| 553 | dp = readdir (d); | 544 | dp = readdir (d); |
| 554 | if (dp == NULL && (0 | 545 | if (dp == NULL && (0 |
| 555 | # ifdef EAGAIN | 546 | # ifdef EAGAIN |
| 556 | || errno == EAGAIN | 547 | || errno == EAGAIN |
| 557 | # endif | 548 | # endif |
| 558 | # ifdef EINTR | 549 | # ifdef EINTR |
| 559 | || errno == EINTR | 550 | || errno == EINTR |
| 560 | # endif | 551 | # endif |
| 561 | )) | 552 | )) |
| 562 | { QUIT; continue; } | 553 | { QUIT; continue; } |
| 563 | #endif | 554 | #endif |
| 564 | 555 | ||
| 565 | if (!dp) break; | 556 | if (!dp) break; |
| 566 | 557 | ||
| 567 | len = NAMLEN (dp); | 558 | len = NAMLEN (dp); |
| 568 | 559 | ||
| 569 | QUIT; | 560 | QUIT; |
| 570 | if (! DIRENTRY_NONEMPTY (dp) | 561 | if (! DIRENTRY_NONEMPTY (dp) |
| 571 | || len < SCHARS (encoded_file) | 562 | || len < SCHARS (encoded_file) |
| 572 | || 0 <= scmp (dp->d_name, SDATA (encoded_file), | 563 | || 0 <= scmp (dp->d_name, SDATA (encoded_file), |
| 573 | SCHARS (encoded_file))) | 564 | SCHARS (encoded_file))) |
| 574 | continue; | 565 | continue; |
| 575 | 566 | ||
| 576 | if (file_name_completion_stat (encoded_dir, dp, &st) < 0) | 567 | if (file_name_completion_stat (encoded_dir, dp, &st) < 0) |
| 577 | continue; | 568 | continue; |
| 578 | 569 | ||
| 579 | directoryp = ((st.st_mode & S_IFMT) == S_IFDIR); | 570 | directoryp = ((st.st_mode & S_IFMT) == S_IFDIR); |
| 580 | tem = Qnil; | 571 | tem = Qnil; |
| 581 | if (directoryp) | 572 | /* If all_flag is set, always include all. |
| 573 | It would not actually be helpful to the user to ignore any possible | ||
| 574 | completions when making a list of them. */ | ||
| 575 | if (!all_flag) | ||
| 576 | { | ||
| 577 | int skip; | ||
| 578 | if (directoryp) | ||
| 582 | { | 579 | { |
| 583 | #ifndef TRIVIAL_DIRECTORY_ENTRY | 580 | #ifndef TRIVIAL_DIRECTORY_ENTRY |
| 584 | #define TRIVIAL_DIRECTORY_ENTRY(n) (!strcmp (n, ".") || !strcmp (n, "..")) | 581 | #define TRIVIAL_DIRECTORY_ENTRY(n) (!strcmp (n, ".") || !strcmp (n, "..")) |
| 585 | #endif | 582 | #endif |
| 586 | /* "." and ".." are never interesting as completions, and are | 583 | /* "." and ".." are never interesting as completions, and are |
| 587 | actually in the way in a directory with only one file. */ | 584 | actually in the way in a directory with only one file. */ |
| 588 | if (!passcount && TRIVIAL_DIRECTORY_ENTRY (dp->d_name)) | 585 | if (TRIVIAL_DIRECTORY_ENTRY (dp->d_name)) |
| 589 | continue; | 586 | canexclude = 1; |
| 590 | if (!passcount && len > SCHARS (encoded_file)) | 587 | else if (len > SCHARS (encoded_file)) |
| 591 | /* Ignore directories if they match an element of | 588 | /* Ignore directories if they match an element of |
| 592 | completion-ignored-extensions which ends in a slash. */ | 589 | completion-ignored-extensions which ends in a slash. */ |
| 593 | for (tem = Vcompletion_ignored_extensions; | 590 | for (tem = Vcompletion_ignored_extensions; |
| @@ -618,10 +615,10 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate) | |||
| 618 | } | 615 | } |
| 619 | } | 616 | } |
| 620 | else | 617 | else |
| 621 | { | 618 | { |
| 622 | /* Compare extensions-to-be-ignored against end of this file name */ | 619 | /* Compare extensions-to-be-ignored against end of this file name */ |
| 623 | /* if name is not an exact match against specified string */ | 620 | /* if name is not an exact match against specified string */ |
| 624 | if (!passcount && len > SCHARS (encoded_file)) | 621 | if (len > SCHARS (encoded_file)) |
| 625 | /* and exit this for loop if a match is found */ | 622 | /* and exit this for loop if a match is found */ |
| 626 | for (tem = Vcompletion_ignored_extensions; | 623 | for (tem = Vcompletion_ignored_extensions; |
| 627 | CONSP (tem); tem = XCDR (tem)) | 624 | CONSP (tem); tem = XCDR (tem)) |
| @@ -644,121 +641,131 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate) | |||
| 644 | 641 | ||
| 645 | /* If an ignored-extensions match was found, | 642 | /* If an ignored-extensions match was found, |
| 646 | don't process this name as a completion. */ | 643 | don't process this name as a completion. */ |
| 647 | if (!passcount && CONSP (tem)) | 644 | if (CONSP (tem)) |
| 648 | continue; | 645 | canexclude = 1; |
| 649 | 646 | ||
| 650 | /* FIXME: If we move this `decode' earlier we can eliminate | 647 | if (!includeall && canexclude) |
| 651 | the repeated ENCODE_FILE on Vcompletion_ignored_extensions. */ | 648 | /* We're not including all files and this file can be excluded. */ |
| 652 | name = make_unibyte_string (dp->d_name, len); | 649 | continue; |
| 653 | name = DECODE_FILE (name); | ||
| 654 | 650 | ||
| 655 | if (!passcount) | 651 | if (includeall && !canexclude) |
| 656 | { | 652 | { /* If we have one non-excludable file, we want to exclude the |
| 657 | Lisp_Object regexps; | 653 | excudable files. */ |
| 658 | Lisp_Object zero; | 654 | includeall = 0; |
| 659 | XSETFASTINT (zero, 0); | 655 | /* Throw away any previous excludable match found. */ |
| 660 | 656 | bestmatch = Qnil; | |
| 661 | /* Ignore this element if it fails to match all the regexps. */ | 657 | bestmatchsize = 0; |
| 662 | for (regexps = Vcompletion_regexp_list; CONSP (regexps); | 658 | matchcount = 0; |
| 663 | regexps = XCDR (regexps)) | ||
| 664 | if (fast_string_match (XCAR (regexps), name) < 0) | ||
| 665 | break; | ||
| 666 | if (CONSP (regexps)) | ||
| 667 | continue; | ||
| 668 | } | 659 | } |
| 660 | } | ||
| 661 | /* FIXME: If we move this `decode' earlier we can eliminate | ||
| 662 | the repeated ENCODE_FILE on Vcompletion_ignored_extensions. */ | ||
| 663 | name = make_unibyte_string (dp->d_name, len); | ||
| 664 | name = DECODE_FILE (name); | ||
| 665 | |||
| 666 | { | ||
| 667 | Lisp_Object regexps; | ||
| 668 | Lisp_Object zero; | ||
| 669 | XSETFASTINT (zero, 0); | ||
| 670 | |||
| 671 | /* Ignore this element if it fails to match all the regexps. */ | ||
| 672 | for (regexps = Vcompletion_regexp_list; CONSP (regexps); | ||
| 673 | regexps = XCDR (regexps)) | ||
| 674 | if (fast_string_match (XCAR (regexps), name) < 0) | ||
| 675 | break; | ||
| 676 | if (CONSP (regexps)) | ||
| 677 | continue; | ||
| 678 | } | ||
| 679 | |||
| 680 | /* This is a possible completion */ | ||
| 681 | if (directoryp) | ||
| 682 | /* This completion is a directory; make it end with '/'. */ | ||
| 683 | name = Ffile_name_as_directory (name); | ||
| 684 | |||
| 685 | /* Test the predicate, if any. */ | ||
| 686 | if (!NILP (predicate)) | ||
| 687 | { | ||
| 688 | Lisp_Object val; | ||
| 689 | struct gcpro gcpro1; | ||
| 669 | 690 | ||
| 670 | /* This is a possible completion */ | 691 | GCPRO1 (name); |
| 671 | if (directoryp) | 692 | val = call1 (predicate, name); |
| 672 | /* This completion is a directory; make it end with '/'. */ | 693 | UNGCPRO; |
| 673 | name = Ffile_name_as_directory (name); | ||
| 674 | |||
| 675 | /* Test the predicate, if any. */ | ||
| 676 | if (!NILP (predicate)) | ||
| 677 | { | ||
| 678 | Lisp_Object val; | ||
| 679 | struct gcpro gcpro1; | ||
| 680 | |||
| 681 | GCPRO1 (name); | ||
| 682 | val = call1 (predicate, name); | ||
| 683 | UNGCPRO; | ||
| 684 | 694 | ||
| 685 | if (NILP (val)) | 695 | if (NILP (val)) |
| 686 | continue; | 696 | continue; |
| 687 | } | 697 | } |
| 688 | 698 | ||
| 689 | /* Suitably record this match. */ | 699 | /* Suitably record this match. */ |
| 690 | 700 | ||
| 691 | matchcount++; | 701 | matchcount++; |
| 692 | 702 | ||
| 693 | if (all_flag) | 703 | if (all_flag) |
| 694 | bestmatch = Fcons (name, bestmatch); | 704 | bestmatch = Fcons (name, bestmatch); |
| 695 | else if (NILP (bestmatch)) | 705 | else if (NILP (bestmatch)) |
| 696 | { | 706 | { |
| 697 | bestmatch = name; | 707 | bestmatch = name; |
| 698 | bestmatchsize = SCHARS (name); | 708 | bestmatchsize = SCHARS (name); |
| 699 | } | 709 | } |
| 700 | else | 710 | else |
| 711 | { | ||
| 712 | Lisp_Object zero = make_number (0); | ||
| 713 | /* FIXME: This is a copy of the code in Ftry_completion. */ | ||
| 714 | int compare = min (bestmatchsize, SCHARS (name)); | ||
| 715 | Lisp_Object tem | ||
| 716 | = Fcompare_strings (bestmatch, zero, | ||
| 717 | make_number (compare), | ||
| 718 | name, zero, | ||
| 719 | make_number (compare), | ||
| 720 | completion_ignore_case ? Qt : Qnil); | ||
| 721 | int matchsize | ||
| 722 | = (EQ (tem, Qt) ? compare | ||
| 723 | : XINT (tem) < 0 ? - XINT (tem) - 1 | ||
| 724 | : XINT (tem) - 1); | ||
| 725 | |||
| 726 | if (completion_ignore_case) | ||
| 701 | { | 727 | { |
| 702 | Lisp_Object zero = make_number (0); | 728 | /* If this is an exact match except for case, |
| 703 | /* FIXME: This is a copy of the code in Ftry_completion. */ | 729 | use it as the best match rather than one that is not |
| 704 | compare = min (bestmatchsize, SCHARS (name)); | 730 | an exact match. This way, we get the case pattern |
| 705 | tem = Fcompare_strings (bestmatch, zero, | 731 | of the actual match. */ |
| 706 | make_number (compare), | 732 | /* This tests that the current file is an exact match |
| 707 | name, zero, | 733 | but BESTMATCH is not (it is too long). */ |
| 708 | make_number (compare), | 734 | if ((matchsize == SCHARS (name) |
| 709 | completion_ignore_case ? Qt : Qnil); | 735 | && matchsize + !!directoryp |
| 710 | if (EQ (tem, Qt)) | 736 | < SCHARS (bestmatch)) |
| 711 | matchsize = compare; | 737 | || |
| 712 | else if (XINT (tem) < 0) | 738 | /* If there is no exact match ignoring case, |
| 713 | matchsize = - XINT (tem) - 1; | 739 | prefer a match that does not change the case |
| 714 | else | 740 | of the input. */ |
| 715 | matchsize = XINT (tem) - 1; | 741 | /* If there is more than one exact match aside from |
| 716 | 742 | case, and one of them is exact including case, | |
| 717 | if (completion_ignore_case) | 743 | prefer that one. */ |
| 718 | { | 744 | /* This == checks that, of current file and BESTMATCH, |
| 719 | /* If this is an exact match except for case, | 745 | either both or neither are exact. */ |
| 720 | use it as the best match rather than one that is not | 746 | (((matchsize == SCHARS (name)) |
| 721 | an exact match. This way, we get the case pattern | 747 | == |
| 722 | of the actual match. */ | 748 | (matchsize + !!directoryp == SCHARS (bestmatch))) |
| 723 | /* This tests that the current file is an exact match | 749 | && (tem = Fcompare_strings (name, zero, |
| 724 | but BESTMATCH is not (it is too long). */ | 750 | make_number (SCHARS (file)), |
| 725 | if ((matchsize == SCHARS (name) | 751 | file, zero, |
| 726 | && matchsize + !!directoryp | 752 | Qnil, |
| 727 | < SCHARS (bestmatch)) | 753 | Qnil), |
| 728 | || | 754 | EQ (Qt, tem)) |
| 729 | /* If there is no exact match ignoring case, | 755 | && (tem = Fcompare_strings (bestmatch, zero, |
| 730 | prefer a match that does not change the case | 756 | make_number (SCHARS (file)), |
| 731 | of the input. */ | 757 | file, zero, |
| 732 | /* If there is more than one exact match aside from | 758 | Qnil, |
| 733 | case, and one of them is exact including case, | 759 | Qnil), |
| 734 | prefer that one. */ | 760 | ! EQ (Qt, tem)))) |
| 735 | /* This == checks that, of current file and BESTMATCH, | 761 | bestmatch = name; |
| 736 | either both or neither are exact. */ | ||
| 737 | (((matchsize == SCHARS (name)) | ||
| 738 | == | ||
| 739 | (matchsize + !!directoryp == SCHARS (bestmatch))) | ||
| 740 | && (tem = Fcompare_strings (name, zero, | ||
| 741 | make_number (SCHARS (file)), | ||
| 742 | file, zero, | ||
| 743 | Qnil, | ||
| 744 | Qnil), | ||
| 745 | EQ (Qt, tem)) | ||
| 746 | && (tem = Fcompare_strings (bestmatch, zero, | ||
| 747 | make_number (SCHARS (file)), | ||
| 748 | file, zero, | ||
| 749 | Qnil, | ||
| 750 | Qnil), | ||
| 751 | ! EQ (Qt, tem)))) | ||
| 752 | bestmatch = name; | ||
| 753 | } | ||
| 754 | bestmatchsize = matchsize; | ||
| 755 | } | 762 | } |
| 763 | bestmatchsize = matchsize; | ||
| 756 | } | 764 | } |
| 757 | /* This closes the directory. */ | ||
| 758 | bestmatch = unbind_to (inner_count, bestmatch); | ||
| 759 | } | 765 | } |
| 760 | 766 | ||
| 761 | UNGCPRO; | 767 | UNGCPRO; |
| 768 | /* This closes the directory. */ | ||
| 762 | bestmatch = unbind_to (count, bestmatch); | 769 | bestmatch = unbind_to (count, bestmatch); |
| 763 | 770 | ||
| 764 | if (all_flag || NILP (bestmatch)) | 771 | if (all_flag || NILP (bestmatch)) |