1" 2" Filename: cream-replacemulti.vim 3" Updated: 2005-01-20 22:32:47-0400 4" 5" Cream -- An easy-to-use configuration of the famous Vim text editor 6" [ http://cream.sourceforge.net ] Copyright (C) 2001-2011 Steve Hall 7" 8" License: {{{1 9" This program is free software; you can redistribute it and/or modify 10" it under the terms of the GNU General Public License as published by 11" the Free Software Foundation; either version 3 of the License, or 12" (at your option) any later version. 13" [ http://www.gnu.org/licenses/gpl.html ] 14" 15" This program is distributed in the hope that it will be useful, but 16" WITHOUT ANY WARRANTY; without even the implied warranty of 17" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18" General Public License for more details. 19" 20" You should have received a copy of the GNU General Public License 21" along with this program; if not, write to the Free Software 22" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 23" 02111-1307, USA. 24" 25" Description: {{{1 26" 27" This script replaces text across multiple files. It is a pure Vim 28" implementation and does not depend on external utilties that do 29" similar things (like sed, grep, cat, or Find). As such, it is 30" cross-platform and has been tested on both Windows95/XP and Linux. 31" 32" Replace Multi is intended to be used in the GUI version of Vim, 33" gVim. It presents a rather hackish dialog to take user input for the 34" Find, Replace, and Path components and present options for Case 35" Sensitivity and the use of Vim's Regular Expressions. (Some day in 36" the future it might be adapted to work from the command line without 37" a dialog.) 38" 39" In testing, Replace Multi performed a replacment operation on 1000 40" small files in 25 seconds. (2.80Ghz Windows XP machine with 1Gb of 41" RAM.) Obviously various Vim and system settings could vary greatly, 42" but as a benchmark this is about 40 files/second. 43" 44" ToDos: {{{1 45" 46" Other (potential) bells and whistles to be added: 47" * Actual testing of non-alpha/numeric characters. ;) 48" * Search subdirectories option 49" * Provide a commandline version 50" * Prevent files from showing up on Recent File or Buffers menu 51" 52" 1}}} 53" 54" Dependencies: 55" ******************************************************************** 56" This script depends on Multivals, available from: 57" http://www.vim.org/script.php?script_id=171. 58" ******************************************************************** 59" 60" Cream_replacemulti() {{{1 61function! Cream_replacemulti() 62" Main function 63 64 " save guioptions environment 65 let myguioptions = &guioptions 66 " do not use a vertical layout for dialog buttons 67 set guioptions-=v 68 69 " initialize variables if don't exist (however, remember if they do!) 70 if !exists("g:CREAM_RMFIND") 71 let g:CREAM_RMFIND = 'Find Me!' 72 endif 73 if !exists("g:CREAM_RMREPL") 74 let g:CREAM_RMREPL = 'Replace Me!' 75 endif 76 " let's always get the current directory and don't remember previous(good idea?) 77 " * Would be good if we could somehow have another button on the 78 " inputdialog() box for path that reads "Get Current", but this function 79 " doesn't have this feature (yet). 80 if !exists("g:CREAM_RMPATH") 81 " hmmm... buggy 82 "let g:CREAM_RMPATH = Cream_path_fullsystem(getcwd(), "unix") 83 " try this... 84 if exists("$CREAM") 85 let g:CREAM_RMPATH = Cream_path_fullsystem(expand("%:p:h"), "unix") 86 else 87 let g:CREAM_RMPATH = s:Cream_path_fullsystem(expand("%:p:h"), "unix") 88 endif 89 90 endif 91 92 " option: case sensitive? (1=yes, 0=no) 93 if !exists("g:CREAM_RMCASE") 94 " initialize on 95 let g:CREAM_RMCASE = 0 96 endif 97 98 " option: regular expressions? (1=yes, 0=no) 99 if !exists("g:CREAM_RMREGEXP") 100 " initialize off 101 let g:CREAM_RMREGEXP = 0 102 endif 103 104 "*** No more warning 105 "" beta warning 106 "call confirm( 107 " \ "NOTICE:\n" . 108 " \ "\n" . 109 " \ "This tool is still beta quality. Use at your own risk!\n" . 110 " \ "\n" . 111 " \ "(But please forward us any bugs you find!)\n", 112 " \ "&Ok", 1, "Warning") 113 "*** 114 115 " get find, replace and path/filename info (verifying that Find and Path 116 " aren't empty, too) 117 let valid = 0 118 while valid == 0 119 " get find, replace and path/filename info 120 let myreturn = s:Cream_replacemulti_request(g:CREAM_RMFIND, g:CREAM_RMREPL, g:CREAM_RMPATH) 121 " if quit code returned 122 if myreturn == 2 123 break 124 " verify criticals aren't empty (user pressed 'ok') 125 elseif myreturn == 1 126 let valid = s:Cream_replacemulti_verify() 127 endif 128 endwhile 129 130 " if not quit code, continue 131 if myreturn != 2 132 " warning 133 let myproceed = s:Cream_replacemulti_warning() 134""*** DEBUG: 135"let n = confirm( 136" \ "DEBUG:\n" . 137" \ " g:CREAM_RMFIND = " . g:CREAM_RMFIND . "\n" . 138" \ " g:CREAM_RMREPL = " . g:CREAM_RMREPL . "\n" . 139" \ " g:CREAM_RMPATH = " . g:CREAM_RMPATH . "\n" . 140" \ " myreturn = " . myreturn . "\n" . 141" \ " myproceed = " . myproceed . "\n" . 142" \ "\n", "&Ok\n&Cancel", 1, "Info") 143"if n != 1 144" return 145"endif 146""*** 147 if myproceed != 0 148 " DO FIND/REPLACE! (Critical file states are verified within.) 149 if valid == 1 150 call s:Cream_replacemulti_doit() 151 else 152 return 153 endif 154 else 155 " do nothing 156 return 157 endif 158 endif 159 160 " restore guioptions 161 execute "set guioptions=" . myguioptions 162 163endfunction 164 165 166" s:Cream_replacemulti_warning() {{{1 167function! s:Cream_replacemulti_warning() 168" simple death/destruction warning, last chance to bail 169 170 let msg = "Warning! This action is about modify the contents of\n" . 171 \ "multiple files based on the previous dialog's settings.\n" 172 let msg = msg . "\n" 173 let msg = msg . " Continue?" 174 let mychoice = confirm(msg, "&Ok\n&Cancel", 1, "Info") 175 if mychoice == 0 176 return 177 elseif mychoice == 1 178 return 1 179 elseif mychoice == 2 180 return 181 else 182 " error 183 call confirm("Error in s:Cream_replacemulti_warning(). Unexpected result.", "&Ok", 1, "Error") 184 endif 185endfunction 186 187 188" s:Cream_replacemulti_request() {{{1 189function! s:Cream_replacemulti_request(myfind, myrepl, mypath) 190" Dialog to obtain user information (find, replace, path/filename) 191" * returns 0 for not finished (so can be recalled) 192" * returns 1 for finished 193" * returns 2 for quit 194 195 " escape ampersand with second ampersand in dialog box 196 " * JUST for display 197 " * Just for Windows 198 if has("win32") 199 \|| has("win16") 200 \|| has("win95") 201 \|| has("dos16") 202 \|| has("dos32") 203 let myfind_fixed = substitute(a:myfind, "&", "&&", "g") 204 let myrepl_fixed = substitute(a:myrepl, "&", "&&", "g") 205 else 206 let myfind_fixed = a:myfind 207 let myrepl_fixed = a:myrepl 208 endif 209 210 let mychoice = 0 211 let msg = "INSTRUCTIONS:\n" 212 let msg = msg . "* Enter the literal find and replace text below.\n" 213 let msg = msg . "* Use \"\\n\" to represent new lines and \"\\t\" for tabs.\n" 214 let msg = msg . "* Use wildcards as needed when entering path and filename.\n" 215 let msg = msg . "* Please read the Vim help to understand regular expressions (:help regexp)\n" 216 let msg = msg . "\n" 217 let msg = msg . "\n" 218 let msg = msg . "FIND: " . myfind_fixed . " \n" 219 let msg = msg . "\n" 220 let msg = msg . "REPLACE: " . myrepl_fixed . " \n" 221 let msg = msg . "\n" 222 let msg = msg . "PATH and FILENAME(S): " . a:mypath . " \n" 223 let msg = msg . "\n" 224 let msg = msg . "\n" 225 let msg = msg . "OPTIONS:\n" 226 if g:CREAM_RMCASE == 1 227 let msg = msg . " [X] Yes [_] No Case Sensitive\n" 228 else 229 let msg = msg . " [_] Yes [X] No Case Sensitive\n" 230 endif 231 if g:CREAM_RMREGEXP == 1 232 let msg = msg . " [X] Yes [_] No Regular Expressions\n" 233 else 234 let msg = msg . " [_] Yes [X] No Regular Expressions\n" 235 endif 236 let msg = msg . "\n" 237 let mychoice = confirm(msg, "&Find\n&Replace\n&Path/Filename\nOp&tions\n&Ok\n&Cancel", 1, "Info") 238 if mychoice == 0 239 " quit via Esc, window close, etc. (OS specific) 240 return 2 241 elseif mychoice == 1 242 " call dialog to get find string 243 call s:Cream_replacemulti_find(g:CREAM_RMFIND) 244 return 245 elseif mychoice == 2 246 " call dialog to get replace string 247 call s:Cream_replacemulti_repl(g:CREAM_RMREPL) 248 return 249 elseif mychoice == 3 250 " call dialog to get path string 251 call s:Cream_replacemulti_path(g:CREAM_RMPATH) 252 return 253 elseif mychoice == 4 254 " call dialog to set options. Continue to show until Ok(1) or Cancel(2) 255 let valid = '0' 256 while valid == '0' 257 " let user set options 258 let myreturn = s:Cream_replacemulti_options() 259 " if Ok or Cancel, go back to main dialog 260 if myreturn == 1 || myreturn == 2 261 let valid = 1 262 endif 263 endwhile 264 return 265 elseif mychoice == 5 266 " user thinks ok, return positive for actual verification 267 return 1 268 elseif mychoice == 6 269 " quit 270 return 2 271 endif 272 273 call confirm("Error in s:Cream_replacemulti_request(). Unexpected result.", "&Ok", "Error") 274 return 2 275 276endfunction 277 278 279" Get input through dialog 280" * Would be nice to detect Ok or Cancel here. (Cancel returns an empty string.) 281" * These stupid spaces are to work around a Windows GUI problem: Input is only 282" allowed to be as long as the actual input box. Therefore, we're widening the 283" dialog box so the input box is wider. ;) 284" s:Cream_replacemulti_find() {{{1 285function! s:Cream_replacemulti_find(myfind) 286 let myfind = inputdialog("Please enter a string to find... " . 287 \" " . 288 \" " . 289 \" " . 290 \" ", a:myfind) 291 " if user cancels, returns empty. Don't allow. 292 if myfind != "" 293 let g:CREAM_RMFIND = myfind 294 endif 295 return 296endfunction 297 298" s:Cream_replacemulti_repl() {{{1 299function! s:Cream_replacemulti_repl(myrepl) 300 let myrepl = inputdialog("Please enter a string to replace..." . 301 \" " . 302 \" " . 303 \" " . 304 \" ", a:myrepl) 305 " allow empty return, but verify not a cancel 306 if myrepl == "" 307 let mychoice = confirm( 308 \ "Replace was found empty.\n" . 309 \ "\n" . 310 \ "(This is legal, but was it your intention?)\n", 311 \ "&Leave\ empty\n&No,\ put\ back\ what\ I\ had", 2, "Question") 312 if mychoice == 2 313 " leave global as is 314 else 315 let g:CREAM_RMREPL = "" 316 endif 317 else 318 let g:CREAM_RMREPL = myrepl 319 endif 320 return 321endfunction 322 323" s:Cream_replacemulti_path() {{{1 324function! s:Cream_replacemulti_path(mypath) 325 let mypath = inputdialog("Please enter a path and filename... (Wildcards allowed.)" . 326 \" " . 327 \" " . 328 \" " . 329 \" ", a:mypath) 330 " if user cancels, returns empty. Don't allow. 331 if mypath != "" 332 let g:CREAM_RMPATH = mypath 333 endif 334 return 335endfunction 336 337" s:Cream_replacemulti_options() {{{1 338function! s:Cream_replacemulti_options() 339 340 let mychoice = 0 341 let msg = "Options:\n" 342 let msg = msg . "\n" 343 let msg = msg . "\n" 344 if g:CREAM_RMCASE == 1 345 let strcase = "X" 346 else 347 let strcase = "_" 348 endif 349 if g:CREAM_RMREGEXP == 1 350 let strregexp = "X" 351 else 352 let strregexp = "_" 353 endif 354 let msg = msg . " [" . strcase . "] Case Sensitive\n" 355 let msg = msg . " [" . strregexp . "] Regular Expressions\n" 356 let msg = msg . "\n" 357 let mychoice = confirm(msg, "Case\ Sensitive\nRegular\ Expressions\n&Ok", 1, "Info") 358 if mychoice == 0 359 " quit via Esc, window close, etc. (OS specific) 360 return 2 361 elseif mychoice == 1 362 " case sensitive 363 if g:CREAM_RMCASE == 1 364 let g:CREAM_RMCASE = 0 365 else 366 let g:CREAM_RMCASE = 1 367 endif 368 return 369 elseif mychoice == 2 370 " regular expressions 371 if g:CREAM_RMREGEXP == 1 372 let g:CREAM_RMREGEXP = 0 373 else 374 let g:CREAM_RMREGEXP = 1 375 endif 376 return 377 elseif mychoice == 3 378 " ok 379 return 1 380 endif 381 return 382endfunction 383 384 385" s:Cream_replacemulti_verify() {{{1 386function! s:Cream_replacemulti_verify() 387" Verify that Find and Path not empty (although Replace can be) 388" * Returns '1' when valid 389 390 if g:CREAM_RMFIND == '' 391 call confirm("Find may not be empty.", "&Ok", "Warning") 392 return 393 elseif g:CREAM_RMPATH == '' 394 call confirm("Path/Filename may not be empty.", "&Ok", "Warning") 395 return 396 else 397 return 1 398 endif 399endfunction 400 401 402" s:Cream_replacemulti_doit() {{{1 403function! s:Cream_replacemulti_doit() 404" Main find/replace function. Also validates files and state 405 406 " get files {{{2 407 408 let myfiles = "" 409 410 " get file list 411 " Note: Wildcard "*" for filename won't return files beginning with dot. 412 " (Must use ".*" to obtain.) 413 if exists("$CREAM") 414 let myfiles = Cream_path_fullsystem(glob(g:CREAM_RMPATH), "unix") 415 else 416 let myfiles = s:Cream_path_fullsystem(glob(g:CREAM_RMPATH), "unix") 417 endif 418 419 " (from explorer.vim) 420 " Add the dot files now, making sure "." is not included! 421 "let myfiles = substitute(Cream_path_fullsystem(glob(g:rmpath), "unix"), "[^\n]*/./\\=\n", '' , '') 422 423 " add a blank line at the end 424 if myfiles != "" && myfiles !~ '\n$' 425 let myfiles = myfiles . "\n" 426 endif 427 428 " 429 " create file list {{{2 430 431""*** DEBUG: 432"let n = confirm( 433" \ "DEBUG:\n" . 434" \ " myfiles:\n" . 435" \ myfiles . "\n" . 436" \ "\n", "&Ok\n&Cancel", 1, "Info") 437"if n != 1 438" return 439"endif 440""*** 441 442 " count files (big assumption: number of newlines == number of files!) 443 let filecount = MvNumberOfElements(myfiles, "\n") 444 " initialize variables 445 let newline = "\n" 446 let mypos = 0 447 let myposnew = 0 448 let i = 0 449 " iterate through files (do while newline still found in listing) 450 while myposnew != -1 451 let myposnew = match(myfiles, newline, mypos) 452 453"*** DEBUG: 454"let temp = confirm("DEBUG:\n" . 455" \" filecount = " . filecount . "\n" . 456" \" strlen(newline) = " . strlen(newline) . "\n" . 457" \" mypos = " . mypos . "\n" . 458" \" myposnew = " . myposnew . "\n" . 459" \" i = " . i 460" \, "&Ok\n&Cancel") 461"if temp == 2 462" break 463"endif 464"*** 465 if myposnew != -1 466 let i = i + 1 467 " get string 468 let myfile{i} = strpart(myfiles, mypos, myposnew - mypos) 469 " advance position to just after recently found newline 470 let mypos = myposnew + strlen(newline) 471 472"*** DEBUG: 473"call confirm("DEBUG:\n File " . i . ": " . "|" . myfile{i} . "|", "&Ok") 474"*** 475 else 476 break 477 endif 478 endwhile 479 480 " list file count and file names if less than 25 481 if filecount > 25 482 let mychoice = confirm( 483 \" Total number of files: " . filecount . "\n" . 484 \"\n", "&Ok\n&Cancel", 1, "Info") 485 else 486 let mychoice = confirm( 487 \" Total number of files: " . filecount . "\n" . 488 \"\n" . 489 \" Files to be modified... \n" . myfiles . "\n" . 490 \"\n", "&Ok\n&Cancel", 1, "Info") 491 endif 492 " Prompt: continue? 493 if mychoice != 1 494 let filecount = 0 495 let myabort = 1 496 endif 497 498 " 499 " find/replace in files (validating files prior to operation) {{{2 500 501 if filecount == 0 502 if exists("myabort") 503 call confirm("Operation aborted.", "&Ok", "Info") 504 else 505 call confirm("No files were found to act upon!", "&Ok", "Info") 506 endif 507 return 508 endif 509 510 " strings 511 " * use local variable to maintain global strings 512 " * work around ridiculous differences between {pattern} and {string} 513 let myfind = g:CREAM_RMFIND 514 let myrepl = g:CREAM_RMREPL 515 516 " capture current magic state 517 let mymagic = &magic 518 " turn off 519 if mymagic == "1" 520 set nomagic 521 endif 522 523 " case-sensitive 524 if g:CREAM_RMCASE == 1 525 let mycase = "I" 526 else 527 let mycase = "i" 528 endif 529 530 " regular expressions 531 if g:CREAM_RMREGEXP == 1 532 let myregexp = "" 533 set magic 534 else 535 let myregexp = "\\V" 536 set nomagic 537 538 " escape strings 539 " escape all backslashes 540 " * This effectively eliminates ALL magical special pattern 541 " meanings! Only those patterns "unescaped" at the next step 542 " become effective. (Diehards are gonna kill us for this.) 543 let myfind = substitute(myfind, '\\', '\\\\', 'g') 544 let myrepl = substitute(myrepl, '\\', '\\\\', 'g') 545 546 " un-escape desired escapes 547 " * Anything not recovered here is escaped forever ;) 548 let myfind = substitute(myfind, '\\\\n', '\\n', 'g') 549 let myfind = substitute(myfind, '\\\\t', '\\t', 'g') 550 let myrepl = substitute(myrepl, '\\\\n', '\\n', 'g') 551 let myrepl = substitute(myrepl, '\\\\t', '\\t', 'g') 552 553 " escape slashes so as not to thwart the :s command below! 554 let myfind = substitute(myfind, '/', '\\/', 'g') 555 let myrepl = substitute(myrepl, '/', '\\/', 'g') 556 557 " replace string requires returns instead of newlines 558 let myrepl = substitute(myrepl, '\\n', '\\r', 'g') 559 560 endif 561 562 " save options {{{2 563 " turn off redraw 564 let mylazyredraw = &lazyredraw 565 set lazyredraw 566 " turn off swap 567 let myswapfile = &swapfile 568 set noswapfile 569 " turn off undo 570 let myundolevels = &undolevels 571 set undolevels=-1 572 " ignore autocmds 573 let myeventignore = &eventignore 574 set eventignore=all 575 " unload buffers 576 let myhidden = &hidden 577 set nohidden 578 579 " Edit, Replace, Write, close {{{2 580 " (the main point!) 581 " 582 " * Force edit/replace/write/close here (we should have already 583 " picked up errors in validation) 584 " * We do NOT allow regexp at the moment. Thus, 585 " - magic is turned off 586 " - substitution uses \V (see :help \V) 587 " - ALL escaped characters are disallowed, save 2 ("\n" and 588 " "\t") This is accomplished by escaping all the backslashes 589 " and then un-escaping just those two. 590 591 " initialize iterations 592 let i = 0 593 " initialize errorlog 594 let myerrorlog = "" 595 " iterate while less than the total file count 596 while i < filecount 597 let i = i + 1 598""*** Don't validate, too slow 599" " validate 600" "let myerror = s:Cream_replacemulti_validate(myfile{i}) 601" let myerror = "" 602" "*** 603" " proceed if empty (valid) 604" if myerror == "" 605 606 " cmdheight for echo 607 let mycmdheight = &cmdheight 608 set cmdheight=2 609 echo " Progress: " . i . " of " . filecount . " (" . ((i*100) / filecount) . "%)" 610 let &cmdheight = mycmdheight 611 612 " open file 613 execute "silent! edit! " . myfile{i} 614 615 " find/replace ( :s/{pattern}/{string}/{options} ) 616 " * {command} 617 " % -- globally across the file 618 " * {pattern} 619 " \V -- eliminates magic (ALL specials must be escaped, most 620 " of which we thwarted above, MWUHAHAHA!!) 621 " * {string} 622 " * {options} 623 " g -- global 624 " e -- skip over minor errors (like "not found") 625 " I -- don't ignore case 626 " i -- ignore case 627 628 " condition based on options 629 let mycmd = ':silent! %s/' . myregexp . myfind . '/' . myrepl . '/ge' . mycase 630 631"*** DEBUG: 632"let mychoice = confirm("DEBUG:\n mycmd = " . mycmd, "&Ok\n&Cancel", 1) 633"if mychoice == 1 634"endif 635 " do it! 636 execute mycmd 637"*** 638 " save only if modified 639 if &modified == "1" 640 " save file 641 execute "silent! write! " . myfile{i} 642 endif 643 644 " close file (delete from buffer as is our preference ;) 645 execute "silent! bwipeout!" 646 647" else 648 649" "*** DEBUG: 650" "call confirm("DEBUG:\n Bypassing operation on file:\n\n " . myfile{i}, "&Ok") 651" "*** 652" " log error if not valid 653" let myerrorlog = myerrorlog . myerror 654" endif 655"*** end validation 656 endwhile 657 658 " restore options {{{2 659 660 " restore 661 let &lazyredraw = mylazyredraw 662 let &swapfile = myswapfile 663 let &undolevels = myundolevels 664 let &eventignore = myeventignore 665 let &hidden = myhidden 666 667 " return magic state 668 if mymagic == "1" 669 set magic 670 else 671 set nomagic 672 endif 673 unlet mymagic 674 675""*** DEBUG 676"" iterate only the first few files 677"let g:bob = 1 678"if g:bob < 3 679" let msg = "DEBUG: (operation completed)\n" 680" let msg = msg . " Find: " . myfind . "\n" 681" let msg = msg . " \n" 682" let msg = msg . " Replace: " . myrepl . "\n" 683" let msg = msg . " \n" 684" let msg = msg . " Filename: " . myfile{i} . " \n" 685" let msg = msg . " \n" 686" let msg = msg . " \n" 687"call confirm(msg, "&Ok", 1) 688"endif 689"let g:bob = g:bob + 1 690""*** 691 692 if myerrorlog != "" 693 call confirm("Difficulties were encountered during the operation:\n\n" . myerrorlog, "&Ok", "Error") 694 endif 695 " 2}}} 696 697endfunction 698 699" s:Cream_replacemulti_validate() {{{1 700function! s:Cream_replacemulti_validate(filename) 701" Validate a file for editing 702" * Returns empty if valid 703" * Returns string describing error if not valid 704" * Argument must include full path 705 706 " log error in this string (always returned; so if empty, valid) 707 let errorlog = "" 708 709 "...................................................................... 710 " do tests 711 712 " is writable? 713 let test1 = filewritable(a:filename) 714 715 " has DOS binary extension? 716 if has("dos16") || 717 \ has("dos32") || 718 \ has("win16") || 719 \ has("win32") 720 721 let test2 = match(a:filename, "exe", strlen(a:filename) - 3) 722 " if return no match 723 if test2 == '-1' 724 " try 'com' 725 let test2 = match(a:filename, "com", strlen(a:filename) - 3) 726 " if yes, make return code different to distinguish 727 if test2 != '-1' 728 " found, return error code 729 let test2 = '1' 730 else 731 let test2 = '0' 732 endif 733 else 734 " assign error code 735 let test2 = '-1' 736 endif 737 else 738 let test2 = '0' 739 endif 740 741 " file is currently a buffer [distinguish buflisted() and bufloaded()] 742 " * bufexists() returns 0 if doesn't exist, buffer number if does 743 let test3 = bufexists(a:filename) 744 745 746 "...................................................................... 747 " log errors (if any) 748 if test1 == '0' || test1 == '2' || 749 \ test2 == '1' || test2 == '-1' || 750 \ test3 != '0' 751 752 " if an error, start with the file name 753 let errorlog = errorlog . "\n" . a:filename . ":\n" 754 755 " filewritable 756 if test1 == '0' || test1 == '2' 757 let errorlog = errorlog . " * Not writable -- " 758 if test1 == '0' 759 let errorlog = errorlog . "Unable to write file (perhaps read-only or all ready open?)\n" 760 elseif test1 == '2' 761 let errorlog = errorlog . "Filename is a directory\n" 762 endif 763 endif 764 765 " has DOS binary extension 766 if test2 == '1' || test2 == '-1' 767 let errorlog = errorlog . " * Window program file (binary) -- " 768 if test2 == '-1' 769 let errorlog = errorlog . ".exe file\n" 770 elseif test2 == '1' 771 let errorlog = errorlog . ".com file\n" 772 endif 773 endif 774 775 " is an existing buffer 776 if test3 != '0' 777 let errorlog = errorlog . " * Buffer currently open " 778 endif 779 780 endif 781 782"*** DEBUG: 783"if errorlog != "" 784" call confirm("DEBUG:\n Cream_multireplace_validate(), errorlog:\n" . errorlog, "&Ok") 785"endif 786"*** 787 788 return errorlog 789 790endfunction 791 792" 1}}} 793 794" ******************************************************************** 795" Localised duplicate Cream functions 796" 797" Functions below are localised versions of global library functions 798" duplicated here for portability of this script outside of the Cream 799" environment. 800" ******************************************************************** 801" s:Cream_path_fullsystem() {{{1 802function! s:Cream_path_fullsystem(path, ...) 803" Return a completely expanded and valid path for the current system 804" from string {path}. 805" o Path does not have to exist, although generally invalid formats 806" will not usually be properly expanded (e.g., backslash path separators 807" on Unix). 808" o Both files and directories may be processed. (Paths will not be 809" returned with a trailing path separator.) 810" o Preserves Windows UNC server name preceding "\\". 811" o {optional} argument can be used to override system settings: 812" * "win" forces return in Windows format as possible: 813" - No drive letter is added (none can be assumed) 814" * "unix" forces return to Unix format as possible: 815" - Windows drive letter is not removed 816" 817 818 " format type 819 " forced with argument 820 if a:0 == 1 821 if a:1 == "win" 822 let format = "win" 823 elseif a:1 == "unix" 824 let format = "unix" 825 endif 826 endif 827 " detected if not forced 828 if !exists("format") 829 if Cream_has("ms") 830 let format = "win" 831 else 832 let format = "unix" 833 endif 834 endif 835 836 " expand to full path 837 let path = fnamemodify(a:path, ":p") 838 839 " make Windows format (assume is Unix) 840 if format == "win" 841 842 " remove escaping of spaces 843 let path = substitute(path, '\\ ', ' ', "g") 844 845 " convert forward slashes to backslashes 846 let path = substitute(path, '/', '\', "g") 847 848 " make Unix format (assume is Windows) 849 else 850 851 "" strip drive letter 852 "let path = substitute(path, '^\a:', '', '') 853 854 " convert backslashes to forward slashes 855 let path = substitute(path, '\', '/', "g") 856 857 " escape spaces (but not twice) 858 let path = substitute(path, '[/\\]* ', '\\ ', "g") 859 860 endif 861 862 " remove duplicate separators 863 let path = substitute(path, '\\\+', '\', "g") 864 let path = substitute(path, '/\+', '/', "g") 865 866 " remove trailing separators 867 let path = substitute(path, '[/\\]*$', '', "") 868 869 " maintain Windows UNC servername 870 if s:Cream_path_isunc(a:path) 871 let path = substitute(path, '^\', '\\\\', "") 872 let path = substitute(path, '^/', '\\\\', "") 873 endif 874 875 return path 876 877endfunction 878 879" s:Cream_path_isunc() {{{1 880function! s:Cream_path_isunc(path) 881" Returns 1 if {path} is in Windows UNC format, 0 if not. 882 if match(a:path, '^\\\\') != -1 || match(a:path, '^//') != -1 883 return 1 884 endif 885endfunction 886 887" 1}}} 888" vim:foldmethod=marker 889