" CVSmenu.vim : Vim menu for using CVS                  vim:tw=0
" Author : Thorsten Maerz <info@netztorte.de>           vim600:fdm=marker
" $Revision: 1.1.1.1 $
" $Date: 2002/05/02 06:46:33 $
" License : LGPL
"
" Tested with Vim 6.0
" Primary site : http://ezytools.sourceforge.net/
" Located in the "VimTools" section
" Thanks to Max Ischenko and Michael Sternberg 
"   for testing and useful tips
"
" TODO: Better support for additional params
" TODO: gettag()/getmodule()

"#############################################################################
" Settings
"#############################################################################
" global variables : may be set in ~/.vimrc     {{{1

" this *may* interfere with :help (inhibits highlighting)
" disable autocheck, if this happens to you.
if ($CVSOPT == '')
  "let $CVSOPT='-z9'
  let $CVSOPT=''
endif

if ($CVSCMD == '')
  let $CVSCMD='cvs'
endif

if !exists("g:CVSforcedirectory")
  let g:CVSforcedirectory = 0           " 0:off 1:once 2:forever
endif
if !exists("g:CVSqueryrevision")
  let g:CVSqueryrevision = 0            " 0:fast update 1:query for revisions
endif
if !exists("g:CVSdumpandclose")
  let g:CVSdumpandclose = 2             " 0:new buffer 1:statusline 2:autoswitch
endif
if !exists("g:CVSsortoutput")
  let g:CVSsortoutput = 1               " sort cvs output (group conflicts,updates,...)
endif
if !exists("g:CVScompressoutput")
  let g:CVScompressoutput = 1           " show extended output only if error
endif
if !exists("g:CVSstatusline")
  let g:CVSstatusline = 1               " Notification output to statusline
endif
if !exists("g:CVStitlebar")
  let g:CVStitlebar = 1                 " Notification output to titlebar
endif
if !exists("g:CVSofferrevision")
  let g:CVSofferrevision = 1            " Offer current revision on queries
endif
if !exists("g:CVSsavediff")
  let g:CVSsavediff = 1                 " save settings when using :diff
endif
if !exists("g:CVSdontswitch")
  let g:CVSdontswitch = 1               " dont switch to diffed file
endif
if !exists("g:CVSautocheck")
  let g:CVSautocheck = 1                " do local status on every read file
endif
if !exists("g:CVSdefaultmsg")
  let g:CVSdefaultmsg = ''              " message to use for commands below
endif
if !exists("g:CVSusedefaultmsg")
  let g:CVSusedefaultmsg = 'aj'         " a:Add, i:Commit, j:Join in, p:Import
endif
if !exists("g:CVSfullstatus")
  let g:CVSfullstatus = 0               " display all fields for fullstatus
endif

" problems with :help on console
if !(has("gui_running"))
  let g:CVSautocheck = 0
endif


" script variables      {{{1
if has('unix')                          " path separator
  let s:sep='/'
else
  let s:sep='\'
endif
let s:CVSentries='CVS'.s:sep.'Entries'  " location of 'CVS/Entries' file
let s:cvsmenuhttp="http://cvs.sf.net/cgi-bin/viewcvs.cgi/~checkout~/ezytools/VimTools/"
let s:cvsmenucvs=":pserver:anonymous@cvs.ezytools.sf.net:/cvsroot/ezytools"
let s:CVSupdatequeryonly = 0            " update -n (internal!)
let s:CVSorgtitle = &titlestring        " backup of original title
let g:orgpath = getcwd()
let g:CVSleavediff = 0
let g:CVSdifforgbuf = 0

if exists("loaded_cvsmenu")
  aunmenu CVS
endif

"-----------------------------------------------------------------------------
" Menu entries          {{{1
"-----------------------------------------------------------------------------
" Space before each command to inhibit translation (no one wants a 'cvs Differenz':)
" <esc> in Keyword menus to avoid expansion
" use only TAB between menu item and command (used for MakeLeaderMapping)

amenu &CVS.\ In&fo                                              :call CVSShowInfo()<cr>
amenu &CVS.\ Settin&gs.\ In&fo\ (buffer)                        :call CVSShowInfo(1)<cr>
amenu &CVS.\ Settin&gs.\ Show\ &mappings                        :call CVSShowMapping()<cr>
amenu &CVS.\ Settin&gs.-SEP1-                                   :
amenu &CVS.\ Settin&gs.\ &Autocheck.&Enable                     :call CVSSetAutocheck(1)<cr>
amenu &CVS.\ Settin&gs.\ &Autocheck.&Disable                    :call CVSSetAutocheck(0)<cr>
amenu &CVS.\ Settin&gs.\ &Target.File\ in\ &buffer              :call CVSSetForceDir(0)<cr>
amenu &CVS.\ Settin&gs.\ &Target.&Directory                     :call CVSSetForceDir(2)<cr>
amenu &CVS.\ Settin&gs.\ &Diff.Stay\ in\ &original              :call CVSSetDontSwitch(1)<cr>
amenu &CVS.\ Settin&gs.\ &Diff.Switch\ to\ &diffed              :call CVSSetDontSwitch(0)<cr>
amenu &CVS.\ Settin&gs.\ &Diff.-SEP1-                           :
amenu &CVS.\ Settin&gs.\ &Diff.&Autorestore\ prev\.\ mode       :call CVSSetSaveDiff(1)<cr>
amenu &CVS.\ Settin&gs.\ &Diff.&No\ autorestore                 :call CVSSetSaveDiff(0)<cr>
amenu &CVS.\ Settin&gs.\ &Diff.-SEP2-                           :
amenu &CVS.\ Settin&gs.\ &Diff.Re&store\ pre-diff\ mode         :call CVSRestoreDiffMode()<cr>
amenu &CVS.\ Settin&gs.\ Revision\ &queries.&Enable             :call CVSSetQueryRevision(1)<cr>
amenu &CVS.\ Settin&gs.\ Revision\ &queries.&Disable            :call CVSSetQueryRevision(0)<cr>
amenu &CVS.\ Settin&gs.\ Revision\ &queries.-SEP1-              :
amenu &CVS.\ Settin&gs.\ Revision\ &queries.&Offer\ current\ rev        :call CVSSetOfferRevision(1)<cr>
amenu &CVS.\ Settin&gs.\ Revision\ &queries.&Hide\ current\ rev         :call CVSSetOfferRevision(0)<cr>
amenu &CVS.\ Settin&gs.\ &Output.N&otifcation.Enable\ &statusline       :call CVSSetStatusline(1)<cr>
amenu &CVS.\ Settin&gs.\ &Output.N&otifcation.Disable\ status&line      :call CVSSetStatusline(0)<cr>
amenu &CVS.\ Settin&gs.\ &Output.N&otifcation.-SEP1-                    :
amenu &CVS.\ Settin&gs.\ &Output.N&otifcation.Enable\ &titlebar         :call CVSSetTitlebar(1)<cr>
amenu &CVS.\ Settin&gs.\ &Output.N&otifcation.Disable\ title&bar        :call CVSSetTitlebar(0)<cr>
amenu &CVS.\ Settin&gs.\ &Output.-SEP1-                                 :
amenu &CVS.\ Settin&gs.\ &Output.To\ new\ &buffer               :call CVSSetDumpAndClose(0)<cr>
amenu &CVS.\ Settin&gs.\ &Output.&Notify\ only                  :call CVSSetDumpAndClose(1)<cr>
amenu &CVS.\ Settin&gs.\ &Output.&Autoswitch                    :call CVSSetDumpAndClose(2)<cr>
amenu &CVS.\ Settin&gs.\ &Output.-SEP2-                         :
amenu &CVS.\ Settin&gs.\ &Output.&Compressed                    :call CVSSetCompressOutput(1)<cr>
amenu &CVS.\ Settin&gs.\ &Output.&Full                          :call CVSSetCompressOutput(0)<cr>
amenu &CVS.\ Settin&gs.\ &Output.-SEP3-                         :
amenu &CVS.\ Settin&gs.\ &Output.&Sorted                        :call CVSSetSortOutput(1)<cr>
amenu &CVS.\ Settin&gs.\ &Output.&Unsorted                      :call CVSSetSortOutput(0)<cr>
amenu &CVS.\ Settin&gs.-SEP2-                                   :
amenu &CVS.\ Settin&gs.\ &Install.&Install\ updates             :call CVSInstallUpdates()<cr>
amenu &CVS.\ Settin&gs.\ &Install.&Download\ updates            :call CVSDownloadUpdates()<cr>
amenu &CVS.\ Settin&gs.\ &Install.Install\ buffer\ as\ &help    :call CVSInstallAsHelp()<cr>
amenu &CVS.\ Settin&gs.\ &Install.Install\ buffer\ as\ &plugin  :call CVSInstallAsPlugin()<cr>
amenu &CVS.\ &Keyword.\ &Author                                 a$Author<esc>a$<esc>
amenu &CVS.\ &Keyword.\ &Date                                   a$Date<esc>a$<esc>
amenu &CVS.\ &Keyword.\ &Header                                 a$Header<esc>a$<esc>
amenu &CVS.\ &Keyword.\ &Id                                     a$Id<esc>a$<esc>
amenu &CVS.\ &Keyword.\ &Name                                   a$Name<esc>a$<esc>
amenu &CVS.\ &Keyword.\ Loc&ker                                 a$Locker<esc>a$<esc>
amenu &CVS.\ &Keyword.\ &Log                                    a$Log<esc>a$<esc>
amenu &CVS.\ &Keyword.\ RCS&file                                a$RCSfile<esc>a$<esc>
amenu &CVS.\ &Keyword.\ &Revision                               a$Revision<esc>a$<esc>
amenu &CVS.\ &Keyword.\ &Source                                 a$Source<esc>a$<esc>
amenu &CVS.\ &Keyword.\ S&tate                                  a$State<esc>a$<esc>
amenu &CVS.\ Director&y.\ &Log                                  :call CVSSetForceDir(1)<cr>:call CVSlog()<cr>
amenu &CVS.\ Director&y.\ &Status                               :call CVSSetForceDir(1)<cr>:call CVSstatus()<cr>
amenu &CVS.\ Director&y.\ S&hort\ status                        :call CVSSetForceDir(1)<cr>:call CVSshortstatus()<cr>
amenu &CVS.\ Director&y.\ Lo&cal\ status                        :call CVSSetForceDir(1)<cr>:call CVSLocalStatus()<cr>
amenu &CVS.\ Director&y.-SEP1-                                  :
amenu &CVS.\ Director&y.\ &Query\ update                        :call CVSSetForceDir(1)<cr>:call CVSqueryupdate()<cr>
amenu &CVS.\ Director&y.\ &Update                               :call CVSSetForceDir(1)<cr>:call CVSupdate()<cr>
amenu &CVS.\ Director&y.-SEP2-                                  :
amenu &CVS.\ Director&y.\ &Add                                  :call CVSSetForceDir(1)<cr>:call CVSadd()<cr>
amenu &CVS.\ Director&y.\ Comm&it                               :call CVSSetForceDir(1)<cr>:call CVScommit()<cr>
amenu &CVS.\ Director&y.-SEP3-                                  :
amenu &CVS.\ Director&y.\ Re&move\ from\ repositoy                      :call CVSSetForceDir(1)<cr>:call CVSremove()<cr>
amenu &CVS.\ E&xtra.\ &Create\ patchfile.\ &Context             :call CVSdiffcontext()<cr>
amenu &CVS.\ E&xtra.\ &Create\ patchfile.\ &Standard            :call CVSdiffstandard()<cr>
amenu &CVS.\ E&xtra.\ &Create\ patchfile.\ &Uni                 :call CVSdiffuni()<cr>
amenu &CVS.\ E&xtra.\ &Diff\ to\ revision                       :call CVSdifftorev()<cr>
amenu &CVS.\ E&xtra.\ &Log\ to\ revision                        :call CVSlogtorev()<cr>
amenu &CVS.\ E&xtra.-SEP1-                                      :
amenu &CVS.\ E&xtra.\ Check&out\ revision                       :call CVScheckoutrevision()<cr>
amenu &CVS.\ E&xtra.\ &Update\ to\ revision                     :call CVSupdatetorev()<cr>
amenu &CVS.\ E&xtra.\ &Merge\ in\ revision                      :call CVSupdatemergerev()<cr>
amenu &CVS.\ E&xtra.\ Merge\ in\ revision\ di&ffs               :call CVSupdatemergediff()<cr>
amenu &CVS.\ E&xtra.-SEP2-                                      :
amenu &CVS.\ E&xtra.\ Comm&it\ to\ revision                     :call CVScommitrevision()<cr>
amenu &CVS.\ E&xtra.\ Im&port\ to\ revision                     :call CVSimportrevision()<cr>
amenu &CVS.\ E&xtra.\ &Join\ in\ to\ revision                   :call CVSjoininrevision()<cr>
amenu &CVS.\ E&xtra.-SEP3-                                      :
amenu &CVS.\ E&xtra.\ CVS\ lin&ks                               :call CVSOpenLinks()<cr>
amenu &CVS.\ E&xtra.\ &Get\ file                                :call CVSGet()<cr>
amenu &CVS.\ E&xtra.\ Get\ file\ (pass&word)                    :call CVSGet('','','io')<cr>
amenu &CVS.-SEP1-                                               :
amenu &CVS.\ Ad&min.\ Log&in                                    :call CVSlogin()<cr>
amenu &CVS.\ Ad&min.\ Log&out                                   :call CVSlogout()<cr>
amenu &CVS.\ D&elete.\ Re&move\ from\ repository                :call CVSremove()<cr>
amenu &CVS.\ D&elete.\ Re&lease\ workdir                        :call CVSrelease()<cr>
amenu &CVS.\ &Tag.\ &Create\ tag                                :call CVStag()<cr>
amenu &CVS.\ &Tag.\ &Remove\ tag                                :call CVStagremove()<cr>
amenu &CVS.\ &Tag.\ Create\ &branch                             :call CVSbranch()<cr>
amenu &CVS.\ &Tag.-SEP1-                                        :
amenu &CVS.\ &Tag.\ Cre&ate\ tag\ by\ module                    :call CVSrtag()<cr>
amenu &CVS.\ &Tag.\ Rem&ove\ tag\ by\ module                    :call CVSrtagremove()<cr>
amenu &CVS.\ &Tag.\ Create\ branc&h\ by\ module                 :call CVSrbranch()<cr>
amenu &CVS.\ &Watch/Edit.\ &Watchers                            :call CVSwatchwatchers()<cr>
amenu &CVS.\ &Watch/Edit.\ Watch\ &add                          :call CVSwatchadd()<cr>
amenu &CVS.\ &Watch/Edit.\ Watch\ &remove                       :call CVSwatchremove()<cr>
amenu &CVS.\ &Watch/Edit.\ Watch\ o&n                           :call CVSwatchon()<cr>
amenu &CVS.\ &Watch/Edit.\ Watch\ o&ff                          :call CVSwatchoff()<cr>
amenu &CVS.\ &Watch/Edit.-SEP1-                                 :
amenu &CVS.\ &Watch/Edit.\ &Editors                             :call CVSwatcheditors()<cr>
amenu &CVS.\ &Watch/Edit.\ Edi&t                                :call CVSwatchedit()<cr>
amenu &CVS.\ &Watch/Edit.\ &Unedit                              :call CVSwatchunedit()<cr>
amenu &CVS.-SEP2-                                               :
amenu &CVS.\ &Diff                                              :call CVSdiff()<cr>
amenu &CVS.\ A&nnotate                                          :call CVSannotate()<cr>
amenu &CVS.\ Histo&ry                                           :call CVShistory()<cr>
amenu &CVS.\ &Log                                               :call CVSlog()<cr>
amenu &CVS.\ &Status                                            :call CVSstatus()<cr>
amenu &CVS.\ S&hort\ status                                     :call CVSshortstatus()<cr>
amenu &CVS.\ Lo&cal\ status                                     :call CVSLocalStatus()<cr>
amenu &CVS.-SEP3-                                               :
amenu &CVS.\ Check&out                                          :call CVScheckout()<cr>
amenu &CVS.\ &Query\ update                                     :call CVSqueryupdate()<cr>
amenu &CVS.\ &Update                                            :call CVSupdate()<cr>
amenu &CVS.\ Re&vert\ changes                                   :call CVSrevertchanges()<cr>
amenu &CVS.-SEP4-                                               :
amenu &CVS.\ &Add                                               :call CVSadd()<cr>
amenu &CVS.\ Comm&it                                            :call CVScommit()<cr>
amenu &CVS.\ Im&port                                            :call CVSimport()<cr>
amenu &CVS.\ &Join\ in                                          :call CVSjoinin()<cr>

" create key mappings from this script          {{{1
" key mappings : <Leader> (mostly '\' ?), then same as menu hotkeys
" e.g. <ALT>ci = \ci = CVS.Commit
function! CVSMakeLeaderMapping()
  let cvsmenu=expand("$VIM").s:sep.'plugin'.s:sep.'cvsmenu.vim'
  silent! call CVSMappingFromMenu(cvsmenu,',')
  unlet cvsmenu
endfunction

function! CVSMappingFromMenu(filename,...)
  if !filereadable(a:filename)
    return
  endif
  if a:0 == 0
    let leader = '<Leader>'
  else
    let leader = a:1
  endif
  " create mappings from &-chars
  new
  exec 'read '.a:filename
  " leave only amenu defs
  exec 'g!/^\s*amenu/d'
  " delete separators and blank lines
  exec 'g/\.-SEP/d'
  exec 'g/^$/d'
  " count entries
  let entries=line("$")
  " extract menu entries, put in @m
  exec '%s/^\s*amenu\s\([^'."\t".']*\).\+/\1/eg' 
  exec '%y m'
  " extract mappings from '&'
  exec '%s/&\(\w\)[^&]*/\l\1/eg'
  " create cmd, delete to @k
  exec '%s/^\(.*\)$/nmap '.leader.'\1 :em /eg' 
  exec '%d k'
  " restore menu, delete '&'
  normal "mP
  exec '%s/&//eg'
  " visual block inserts failed, when called from script (vim60at)
  " append keymappings
  normal G"kP
  " merge keys/commands, execute
  let curlin=0
  while curlin < entries
    let curlin = curlin + 1
    call setline(curlin,getline(curlin + entries).getline(curlin).'<cr>')
    exec getline(curlin)
  endwhile
  set nomodified
  bwipeout
endfunction

"-----------------------------------------------------------------------------
" show cvs info         {{{1
"-----------------------------------------------------------------------------
" Param : ToBuffer (bool)
function! CVSShowInfo(...)
  if a:0 == 0
    let tobuf = 0
  else
    let tobuf = a:1
  endif
  "exec 'cd '.expand('%:p:h')
  call CVSChDir(expand('%:p:h'))
  " show CVS info from directory
  let cvsroot='CVS'.s:sep.'Root'
  let cvsrepository='CVS'.s:sep.'Repository'
  silent! exec 'split '.cvsroot
  let root=getline(1)
  bwipeout
  silent! exec 'split '.cvsrepository
  let repository=getline(1)
  bwipeout
  unlet cvsroot cvsrepository
  " show settings
  new
  let zbak=@z
  let @z = ''
    \."\n\"CVSmenu $Revision: 1.1.1.1 $"
    \."\n\"Current directory : ".expand('%:p:h')
    \."\n\"Current Root : ".root
    \."\n\"Current Repository : ".repository
    \."\n\"\t\t\t\t  set environment var to cvsroot"
    \."\nlet $CVSROOT\t\t= \'"                  .$CVSROOT."\'"
    \."\nlet $CVS_RSH\t\t= \'"                  .$CVS_RSH."\'"                  ."\t\" set environment var to rsh/ssh"
    \."\nlet $CVSOPT\t\t= \'"                   .$CVSOPT."\'"                   ."\t\" set cvs options (see cvs --help-options)"
    \."\nlet $CVSCMDOPT\t\t= \'"                .$CVSCMDOPT."\'"                ."\t\" set cvs command options"
    \."\nlet $CVSCMD\t\t\= '"                   .$CVSCMD."\'"                   ."\t\" set cvs command"
    \."\nlet g:CVSforcedirectory\t= "           .g:CVSforcedirectory            ."\t\" refer to directory instead of current file"
    \."\nlet g:CVSqueryrevision\t= "            .g:CVSqueryrevision             ."\t\" Query for revisions (0:no 1:yes)"
    \."\nlet g:CVSdumpandclose\t= "             .g:CVSdumpandclose              ."\t\" Output to: 0=buffer 1=notify 2=autoswitch"
    \."\nlet g:CVSsortoutput\t= "               .g:CVSsortoutput                ."\t\" Toggle sorting output (0:no sorting)"
    \."\nlet g:CVScompressoutput\t= "           .g:CVScompressoutput            ."\t\" Show extended output only if error"
    \."\nlet g:CVStitlebar\t= "                 .g:CVStitlebar                  ."\t\" Notification on titlebar"
    \."\nlet g:CVSstatusline\t= "               .g:CVSstatusline                ."\t\" Notification on statusline"
    \."\nlet g:CVSautocheck\t= "                .g:CVSautocheck                 ."\t\" Get local status when file is read"
    \."\nlet g:CVSofferrevision\t= "            .g:CVSofferrevision             ."\t\" Offer current revision on queries"
    \."\nlet g:CVSsavediff\t= "                 .g:CVSsavediff                  ."\t\" Save settings when using :diff"
    \."\nlet g:CVSdontswitch\t= "               .g:CVSdontswitch                ."\t\" Dont switch to diffed file"
    \."\nlet g:CVSdefaultmsg\t= \'"             .g:CVSdefaultmsg."\'"           ."\t\" message to use for commands below"
    \."\nlet g:CVSusedefaultmsg\t= \'"          .g:CVSusedefaultmsg."\'"        ."\t\" a:Add, i:Commit, j:Join in, p:Import"
    \."\nlet g:CVSfullstatus\t= "               .g:CVSfullstatus                ."\t\" display all fields for fullstatus"
    \."\n\"----------------------------------------"
    \."\n\" Change above values to your needs."
    \."\n\" To execute a line, put the cursor on it and press <shift-cr> or doubleclick."
    \."\n\" Site: http://ezytools.sf.net/VimTools"
  normal "zP
  let @z=zbak
  normal dd
  if tobuf == 0
    silent! exec '5,$g/^"/d'
    normal dddd
    " dont dump this to titlebar
    let titlebak = g:CVStitlebar
    let g:CVStitlebar = 0
    call CVSDumpAndClose()
    let g:CVStitlebar = titlebak 
    unlet titlebak
  else
    map <buffer> q :bd!<cr>
    map <buffer> <s-cr> :exec getline('.')<cr>:set nomod<cr>:echo getline('.')<cr>
    map <buffer> <2-LeftMouse> <s-cr>
    set syntax=vim
    set nomodified
  endif
  call CVSRestoreDir()
  unlet root repository tobuf 
endfunction

"-----------------------------------------------------------------------------
" syntax, MakeRO/RW             {{{1
"-----------------------------------------------------------------------------

function! CVSUpdateSyntax()
  syn match cvsupdateMerge      '^M .*$'
  syn match cvsupdatePatch      '^P .*$'
  syn match cvsupdateConflict   '^C .*$'
  syn match cvsupdateDelete     '^D .*$'
  syn match cvsupdateUnknown    '^? .*$'
  syn match cvscheckoutUpdate   '^U .*$'
  syn match cvsimportNew        '^N .*$'
  syn match cvstagNew           '^T .*$'
  hi link cvstagNew             Special
  hi link cvsimportNew          Special
  hi link cvscheckoutUpdate     Special
  hi link cvsupdateMerge        Special
  hi link cvsupdatePatch        Constant
  hi link cvsupdateConflict     WarningMsg
  hi link cvsupdateDelete       Statement
  hi link cvsupdateUnknown      Comment

  syn match cvsstatusUpToDate   '^File:\s.*\sStatus: Up-to-date$'
  syn match cvsstatusLocal      '^File:\s.*\sStatus: Locally.*$'
  syn match cvsstatusNeed       '^File:\s.*\sStatus: Need.*$'
  syn match cvsstatusConflict   '^File:\s.*\sStatus: File had conflict.*$'
  syn match cvsstatusUnknown    '^File:\s.*\sStatus: Unknown$'
  hi link cvsstatusUpToDate     Type
  hi link cvsstatusLocal        Constant
  hi link cvsstatusNeed         Identifier
  hi link cvsstatusConflict     Warningmsg
  hi link cvsstatusUnknown      Comment

  syn match cvslocalstatusUnknown       '^unknown:.*'
  syn match cvslocalstatusUnchanged     '^unchanged:.*'
  syn match cvslocalstatusMissing       '^missing:.*'
  syn match cvslocalstatusModified      '^modified:.*'
  hi link cvslocalstatusUnknown         Comment
  hi link cvslocalstatusUnchanged       Type
  hi link cvslocalstatusMissing         Identifier
  hi link cvslocalstatusModified        Constant
  " colorize revision X.XX (folke)
  syn match cvslogRevNr         "[0-9.]\+" contained
  syn match cvslogRevision      "\(new\|previous\|Working\|Repository\|\) revision:\([ \t]*\)[0-9.]\+" contains=cvslogRevNr

  if !filereadable($VIM.s:sep.'syntax'.s:sep.'rcslog')
    syn match cvslogRevision    '^revision.*$'
    syn match cvslogFile        '^RCS file:.*'
    syn match cvslogDate        '^date: .*$'
    hi link cvslogFile          Type
    hi link cvslogRevision      Constant
    hi link cvslogRevNr         Statement
    hi link cvslogDate          Identifier
  endif
endfunction

function! CVSAddConflictSyntax()
  syn region CVSConflictOrg start="^<<<<<<<" end="^====" contained
  syn region CVSConflictNew start="===$" end="^>>>>>>>" contained
  syn region CVSConflict start="^<<<<<<<" end=">>>>>>>.*" contains=CVSConflictOrg,CVSConflictNew keepend
"  hi link CVSConflict Special
  hi link CVSConflictOrg DiffChange
  hi link CVSConflictNew DiffAdd
endfunction

function! CVSMakeRO()
  set nomodified
  set readonly
  setlocal nomodifiable
endfunction

function! CVSMakeRW()
  set noreadonly
  setlocal modifiable
endfunction

" output window: open file under cursor by <doubleclick> or <shift-cr>
function! CVSUpdateMapping()
  nmap <buffer> <2-LeftMouse> :call CVSFindFile()<cr>
  nmap <buffer> <S-CR> :call CVSFindFile()<cr>
  nmap <buffer> q :bd!<cr>zv
  nmap <buffer> ? :call CVSShowMapping()<cr>
  nmap <buffer> <Leader>a :call CVSFindFile()<cr>:call CVSadd()<cr>
  nmap <buffer> <Leader>d :call CVSFindFile()<cr>:call CVSdiff()<cr>
  nmap <buffer> <Leader>i :call CVSFindFile()<cr>:call CVScommit()<cr>
  nmap <buffer> <Leader>u :call CVSFindFile()<cr>:call CVSupdate()<cr>
  nmap <buffer> <Leader>s :call CVSFindFile()<cr>:call CVSstatus()<cr>
  nmap <buffer> <Leader>h :call CVSFindFile()<cr>:call CVSshortstatus()<cr>
  nmap <buffer> <Leader>c :call CVSFindFile()<cr>:call CVSlocalstatus()<cr>
  nmap <buffer> <Leader>v :call CVSFindFile()<cr>:call CVSrevertchanges()<cr>
endfunction

function! CVSShowMapping()
  echo 'Mappings in output buffer :'
  echo '<2-LeftMouse> , <SHIFT-CR>      : open file in new buffer'
  echo 'q                               : close output buffer'
  echo '?                               : close output buffer'
  echo '<Leader>a                       : Show this help'
  echo '<Leader>d                       : open file and CVSdiff'
  echo '<Leader>i                       : open file and CVScommit'
  echo '<Leader>u                       : open file and CVSupdate'
  echo '<Leader>s                       : open file and CVSstatus'
  echo '<Leader>h                       : open file and CVSshortstatus'
  echo '<Leader>c                       : open file and CVSlocalstatus'
  echo '<Leader>v                       : open file and CVSrevertchanges'
  echo 'Hint: Leader is mostly \ '
endfunction

function! CVSFindFile()
  let curdir = getcwd()
  exec 'cd '.g:workdir
  normal 0W^W^F
  exec 'cd '.curdir
  unlet curdir
endfunction

"-----------------------------------------------------------------------------
" sort output           {{{1
"-----------------------------------------------------------------------------

" move all lines matching "searchstr" to top
function! CVSMoveToTop(searchstr)
  silent exec 'g/'.a:searchstr.'/m0'
endfunction

" only called by CVSShortStatus
function! CVSSortStatusOutput()
  " allow changes
  call CVSMakeRW()
  call CVSMoveToTop('Status: Unknown$')
  call CVSMoveToTop('Status: Needs Checkout$')
  call CVSMoveToTop('Status: Needs Merge$')
  call CVSMoveToTop('Status: Needs Patch$')
  call CVSMoveToTop('Status: Locally Removed$')
  call CVSMoveToTop('Status: Locally Added$')
  call CVSMoveToTop('Status: Locally Modified$')
  call CVSMoveToTop('Status: File had conflicts on merge$')
endfunction

" called by CVSDoCommand
function! CVSSortOutput()
  " allow changes
  call CVSMakeRW()
  " localstatus
  call CVSMoveToTop('^unknown:')
  call CVSMoveToTop('^unchanged:')
  call CVSMoveToTop('^missing:')
  call CVSMoveToTop('^modified:')
  " org cvs
  call CVSMoveToTop('^? ')      " unknown
  call CVSMoveToTop('^T ')      " tag
  call CVSMoveToTop('^D ')      " delete
  call CVSMoveToTop('^N ')      " new
  call CVSMoveToTop('^U ')      " update
  call CVSMoveToTop('^M ')      " merge
  call CVSMoveToTop('^P ')      " patch
  call CVSMoveToTop('^C ')      " conflict
endfunction

"-----------------------------------------------------------------------------
" status variables              {{{1
"-----------------------------------------------------------------------------

function! CVSSaveOpts()
  let s:CVSROOTbak            = $CVSROOT
  let s:CVS_RSHbak            = $CVS_RSH
  let s:CVSOPTbak             = $CVSOPT
  let s:CVSCMDOPTbak          = $CVSCMDOPT
  let s:CVSCMDbak             = $CVSCMD
  let s:CVSforcedirectorybak  = g:CVSforcedirectory
  let s:CVSqueryrevisionbak   = g:CVSqueryrevision
  let s:CVSdumpandclosebak    = g:CVSdumpandclose
  let g:CVSsortoutputbak        = g:CVSsortoutput
  let g:CVScompressoutputbak    = g:CVScompressoutput
  let g:CVStitlebarbak          = g:CVStitlebar
  let g:CVSstatuslinebak        = g:CVSstatusline
  let g:CVSautocheckbak         = g:CVSautocheck
  let g:CVSofferrevisionbak     = g:CVSofferrevision
  let g:CVSsavediffbak          = g:CVSsavediff
  let g:CVSdontswitchbak        = g:CVSdontswitch
endfunction

function! CVSRestoreOpts()
  let $CVSROOT                = s:CVSROOTbak          
  let $CVS_RSH                = s:CVS_RSHbak          
  let $CVSOPT                 = s:CVSOPTbak           
  let $CVSCMDOPT              = s:CVSCMDOPTbak        
  let $CVSCMD                 = s:CVSCMDbak           
  let g:CVSforcedirectory     = s:CVSforcedirectorybak
  let g:CVSqueryrevision      = s:CVSqueryrevisionbak 
  let g:CVSdumpandclose       = s:CVSdumpandclosebak  
  let g:CVSsortoutput           = g:CVSsortoutputbak
  let g:CVScompressoutput       = g:CVScompressoutputbak
  let g:CVStitlebar             = g:CVStitlebarbak
  let g:CVSstatusline           = g:CVSstatuslinebak
  let g:CVSautocheck            = g:CVSautocheckbak
  let g:CVSofferrevision        = g:CVSofferrevisionbak
  let g:CVSsavediff             = g:CVSsavediffbak
  let g:CVSdontswitch           = g:CVSdontswitchbak
  unlet g:CVSsortoutputbak g:CVScompressoutputbak g:CVStitlebarbak
  unlet g:CVSstatuslinebak g:CVSautocheckbak g:CVSofferrevisionbak
  unlet g:CVSsavediffbak g:CVSdontswitchbak
  unlet s:CVSROOTbak s:CVS_RSHbak s:CVSOPTbak s:CVSCMDOPTbak s:CVSCMDbak
  unlet s:CVSforcedirectorybak s:CVSqueryrevisionbak s:CVSdumpandclosebak
endfunction

" set scope : file or directory, inform user
function! CVSSetForceDir(value)
  let g:CVSforcedirectory=a:value
  if g:CVSforcedirectory==1
    echo 'CVS:Using current DIRECTORY once'
  elseif g:CVSforcedirectory==2
    echo 'CVS:Using current DIRECTORY'
  else
    echo 'CVS:Using current buffer'
  endif
endfunction

" Set output to statusline, close output buffer
function! CVSSetDumpAndClose(value)
  if a:value > 1
    echo 'CVS:output to status(file) and buffer(dir)'
  elseif a:value > 0
    echo 'CVS:output to statusline'
  else
    echo 'CVS:output to buffer'
  endif
  let g:CVSdumpandclose = a:value
endfunction

" enable/disable revs/branchs queries
function! CVSSetQueryRevision(value)
  if a:value > 0
    echo 'CVS:Enabled revision queries'
  else
    echo 'CVS:Not asking for revisions'
  endif
  let g:CVSqueryrevision = a:value
endfunction

" Sort output (group conflicts,updates,...)
function! CVSSetSortOutput(value)
  if a:value > 0
    echo 'CVS:sorting output'
  else
    echo 'CVS:unsorted output'
  endif
  let g:CVSsortoutput = a:value
endfunction

" compress output to one line
function! CVSSetCompressOutput(value)
  if a:value > 0
    echo 'CVS:compressed output'
  else
    echo 'CVS:full output'
  endif
  let g:CVScompressoutput = a:value
endfunction

" output to statusline
function! CVSSetStatusline(value)
  if a:value > 0
    echo 'CVS:output to statusline'
  else
    echo 'CVS:no output to statusline'
  endif
  let g:CVSstatusline = a:value
endfunction

" output to titlebar
function! CVSSetTitlebar(value)
  if a:value > 0
    echo 'CVS:output to titlebar'
  else
    echo 'CVS:no output to titlebar'
  endif
  let g:CVStitlebar = a:value
endfunction

" show local status (autocheck)
function! CVSSetAutocheck(value)
  if a:value > 0
    echo 'CVS:autochecking each file'
  else
    echo 'CVS:autocheck disabled'
  endif
  let g:CVSautocheck = a:value
endfunction

" show current revision as default, when asking for it
function! CVSSetOfferRevision(value)
  if a:value > 0
    echo 'CVS:offering current revision'
  else
    echo 'CVS:not offering current revision'
  endif
  let g:CVSofferrevision = a:value
endfunction

" CVSDiff : activate original or checked-out
function! CVSSetDontSwitch(value)
  if a:value > 0
    echo 'CVS:switching to compared file'
  else
    echo 'CVS:stay in original when diffing'
  endif
  let g:CVSdontswitch = a:value
endfunction

" save settings when using :diff
function! CVSSetSaveDiff(value)
  if a:value > 0
    echo 'CVS:saving settings for :diff'
  else
    echo 'CVS:not saving settings for :diff'
  endif
  let g:CVSsavediff = a:value
endfunction

function! CVSChDir(path)
  let g:orgpath = getcwd()
  let g:workdir = expand("%:p:h")
  exec 'cd '.a:path
endfunction

function! CVSRestoreDir()
  if isdirectory(g:orgpath)
    exec 'cd '.g:orgpath
  endif
endfunction

"}}}
"#############################################################################
" CVS commands
"#############################################################################
"-----------------------------------------------------------------------------
" CVS call              {{{1
"-----------------------------------------------------------------------------

" return > 0 if is win 95-me
function! CVSIsW9x()
  return (has("win32") && (match($COMSPEC,"command\.com") > -1))
endfunction

function! CVSDoCommand(cmd,...)
  " needs to be called from orgbuffer
  let isfile = CVSUsesFile()
  " change to buffers directory
  "exec 'cd '.expand('%:p:h')
  call CVSChDir(expand('%:p:h'))
  " get file/directory to work on (if not given)
  if a:0 < 1
    if g:CVSforcedirectory>0
      let filename=''
    else
      let filename=expand('%:p:t')
    endif
  else
    let filename = a:1
  endif
  " problem with win98se : system() gives an error
  " cannot open 'F:\WIN98\TEMP\VIo9134.TMP'
  " piping the password also seems to fail (maybe caused by cvs.exe)
  " Using 'exec' creates a confirm prompt - only use this s**t on w9x
  if CVSIsW9x()
    let tmp=tempname()
    exec '!'.$CVSCMD.' '.$CVSOPT.' '.a:cmd.' '.$CVSCMDOPT.' '.filename.'>'.tmp
    exec 'split '.tmp
    let dummy=delete(tmp)
    unlet tmp dummy
  else
    let regbak=@z
    let @z=system($CVSCMD.' '.$CVSOPT.' '.a:cmd.' '.$CVSCMDOPT.' '.filename)
    new
    silent normal "zP
    let @z=regbak
  endif
  call CVSProcessOutput(isfile, filename, a:cmd)
  call CVSRestoreDir()
  unlet filename
endfunction

" also jumped in by CVSLocalStatus
function! CVSProcessOutput(isfile,filename,cmd)
  " delete leading and trainling blank lines
  while (getline(1) == '') && (line("$")>1)
    silent exec '0d'
  endwhile
  while (getline("$") == '') && (line("$")>1)
    silent exec '$d'
  endwhile
  " group conflicts, updates, ....
  if g:CVSsortoutput > 0
    silent call CVSSortOutput()
  endif
  " compress output ?
  if g:CVScompressoutput > 0
    if (g:CVSdumpandclose > 0) && a:isfile
      silent call CVSCompressOutput(a:cmd)
    endif
  endif
  " move to top
  normal gg
  set nowrap
  " reset single shot flag
  if g:CVSforcedirectory==1
    let g:CVSforcedirectory = 0
  endif
  call CVSMakeRO()
  if (g:CVSdumpandclose == 1) || ((g:CVSdumpandclose == 2) && a:isfile)
    call CVSDumpAndClose()
  else
    call CVSUpdateMapping()
    call CVSUpdateSyntax()
  endif
endfunction

" return: 1=file 0=dir
function! CVSUsesFile()
  let filename=expand("%:p:t")
  if    ((g:CVSforcedirectory == 0) && (filename != ''))
   \ || ((g:CVSforcedirectory > 0) && (filename == ''))
    return 1
  else
    return 0
  endif
  unlet filename
endfunction

" compress output
function! CVSCompressOutput(cmd)
    " commit
    if match(a:cmd,"commit") > -1
      let v:errmsg = ''
      silent! exec '/^cvs \[commit aborted]:'
      " only compress, if no error found
      if v:errmsg != ''
        silent! exec 'g!/^new revision:/d'
      endif
    " skip localstatus
    elseif (match(a:cmd,"localstatus") > -1)
    " status
    elseif (match(a:cmd,"status") > -1)
      silent! exec 'g/^=\+$/d'
      silent! exec 'g/^$/d'
      silent! exec '%s/.*Status: //'
      silent! exec '%s/^\s\+Working revision:\s\+\([0-9.]*\).*/W:\1/'
      silent! exec '%s/^\s\+Repository revision:\s\+\([0-9.]*\).*/R:\1/'
      silent! exec '%s/^\s\+Sticky Tag:\s\+/T:/'
      silent! exec '%s/^\s\+Sticky Date:\s\+/D:/'
      silent! exec '%s/^\s\+Sticky Options:\s\+/O:/'
      silent! normal ggJJJJJJ
    endif
endfunction

"#############################################################################
" following commands read from STDIN. Call CVS directly
"#############################################################################

"-----------------------------------------------------------------------------
" CVS login / logout (password prompt)          {{{1
"-----------------------------------------------------------------------------

function! CVSlogin(...)
  if a:0 == 0
    let pwpipe = ''
  else
    let pwpipe = 'echo '
    if !has("unix") 
      if a:1 == ''
        let pwpipe = pwpipe . '.'
      endif
    endif
    let pwpipe = pwpipe . a:1 . '|'
  endif
  if has("unix")
    " show password prompt 
    exec '!'.pwpipe.$CVSCMD.' '.$CVSOPT.' login '.$CVSCMDOPT
  else
    " shell is opened in win32 (dos?)
    silent! exec '!'.pwpipe.$CVSCMD.' '.$CVSOPT.' login '.$CVSCMDOPT
  endif
endfunction

function! CVSlogout()
  silent! exec '!'.$CVSCMD.' '.$CVSOPT.' logout '.$CVSCMDOPT
endfunction

"-----------------------------------------------------------------------------
" CVS release (confirmation prompt)             {{{1
"-----------------------------------------------------------------------------

function! CVSrelease()
  let localtoo=input('Release:Also delete local file [y]:')
  if (localtoo=='y') || (localtoo=='')
    let localtoo='-d '
  else
    let localtoo=''
  endif
  let releasedir=expand('%:p:h')
  "exec ':cd ..'
  call CVSChDir(releasedir.s:sep.'..')
  " confirmation prompt -> dont use CVSDoCommand
  if has("unix")
    " show confirmation prompt
    exec '!'.$CVSCMD.' '.$CVSOPT.' release '.localtoo.releasedir.' '.$CVSCMDOPT
  else
    silent! exec '!'.$CVSCMD.' '.$CVSOPT.' release '.localtoo.releasedir.' '.$CVSCMDOPT
  endif
  call CVSRestoreDir()
  unlet localtoo releasedir
endfunction

"#############################################################################
" from here : use CVSDoCommand wrapper
"#############################################################################

"-----------------------------------------------------------------------------
" CVS diff (diffsplit)          {{{1
"-----------------------------------------------------------------------------

" parm : revision
function! CVSdiff(...)
  call SavePos()
  if g:CVSsavediff > 0
    call CVSDiffEnter()
  endif
  let orgcwd = getcwd()
  exec 'cd '.expand('%:p:h')
  let outputbak = g:CVSdumpandclose
  let autocheckbak = g:CVSautocheck
  let g:CVSautocheck = 0
  let g:CVSdumpandclose = 0
  let g:CVSautocheck = 0
  let g:CVSdumpandclose = 0
  let orgfiletype = &filetype
  " query revision (if wanted)
  if a:0 != ''
    let rev = a:1
  elseif g:CVSqueryrevision > 0
    let rev=CVSInputRev('Revision (optional):')
  else 
    let rev=''
  endif
  " tempname() would be deleted before diff (linux)!
  " it is also better to show selected revision
  let tmpnam=expand("%").'.'.rev.'_'.localtime().'.dif'
  if rev!=''
    let rev='-r '.rev.' '
  endif
  call CVSDoCommand('update -p '.rev)
  redraw
  " delete stderr ('checking out...')
  call CVSStripHeader()
  call CVSMakeRO()
  " x! did not write before diffing (linux)!
  exec "w! ".tmpnam
  bwipeout
  wincmd _
  " jump to next diff (current buffer)
  silent! nmap <unique> <buffer> <tab> ]c
  silent! nmap <unique> <buffer> <s-tab> [c
  redraw
  silent exec 'vertical diffsplit '.tmpnam
  let &filetype = orgfiletype
  " jump to next diff (diffed buffer)
  silent! nmap <unique> <buffer> <tab> ]c
  silent! nmap <unique> <buffer> <s-tab> [c
  silent! nmap <unique> <buffer> q :bwipeout<cr>
  let dummy=delete(tmpnam)
  if g:CVSdontswitch > 0
    redraw
    wincmd ^W
    call RestorePos()
  endif
  exec 'cd '.orgcwd
  let g:CVSdumpandclose = outputbak
  let g:CVSautocheck = autocheckbak 
  unlet outputbak autocheckbak
  unlet tmpnam rev orgfiletype
endfunction

" diff to a specific revision
function! CVSdifftorev()
  " Force revision input
  let rev=CVSInputRev('Revision:')
  if rev==''
    echo "CVS diff to revision: aborted"
    return
  endif
  call CVSdiff(rev)
  unlet rev
endfunction

"-----------------------------------------------------------------------------
" CVS diff / patchfile          {{{1
"-----------------------------------------------------------------------------
function! CVSgetdiff(parm)
  call CVSSaveOpts()
  let g:CVSdumpandclose = 0
  let g:CVStitlebar = 1                 " Notification output to titlebar
  " query revision
  let rev=CVSInputRev('Revision (optional):')
  if rev!=''
    let rev=' -r '.rev.' '
  endif
  call CVSDoCommand('diff '.a:parm.rev)
  set syntax=diff
  call CVSRestoreOpts()
  unlet rev
endfunction

function! CVSdiffcontext()
  call CVSgetdiff('-c')
endfunction

function! CVSdiffstandard()
  call CVSgetdiff('')
endfunction

function! CVSdiffuni()
  call CVSgetdiff('-u')
endfunction


"-----------------------------------------------------------------------------
" CVS annotate / log / status / history         {{{1
"-----------------------------------------------------------------------------

function! CVSannotate()
  call CVSDoCommand('annotate',expand('%:p:t'))
  wincmd _
endfunction

function! CVSstatus()
  call CVSDoCommand('status')
endfunction

function! CVShistory()
  call CVSSaveOpts()
  let g:CVSdumpandclose = 0
  call CVSDoCommand('history')
  call CVSRestoreOpts()
endfunction

function! CVSlog()
  call CVSSaveOpts()
  let g:CVSdumpandclose = 0
  if g:CVSqueryrevision > 0
    if g:CVSofferrevision > 0
      let default = '1.1:'.CVSSubstr(b:CVSentry,'/',2)
    else
      let default = ''
    endif
    let rev=input('Revisions (optional): ',default)
  else 
    let rev=''
  endif
  if rev!=''
    let rev=' -r'.rev.' '
  endif
  call CVSDoCommand('log'.rev)
  call CVSRestoreOpts()
endfunction

" log between specific revisions
function! CVSlogtorev()
  let querybak=g:CVSqueryrevision
  let g:CVSqueryrevision = 1
  call CVSlog()
  let g:CVSqueryrevision=querybak
  unlet querybak
endfunction

"-----------------------------------------------------------------------------
" CVS watch / edit : common             {{{1
"-----------------------------------------------------------------------------

function! CVSQueryAction()
  let action=input('Action (e)dit, (u)nedit, (c)ommit, (a)ll, [n]one:')
  if action == 'e'
    let action = '-a edit '
  elseif action == 'u'
    let action = '-a unedit '
  elseif action == 'a'
    let action = '-a all '
  else
    let action = ''
  endif
  return action
endfunction

"-----------------------------------------------------------------------------
" CVS edit              {{{1
"-----------------------------------------------------------------------------

function! CVSwatcheditors()
  call CVSDoCommand('editors')
endfunction

function! CVSwatchedit()
  let action=CVSQueryAction()
  call CVSDoCommand('edit '.action)
  unlet action
endfunction

function! CVSwatchunedit()
  call CVSDoCommand('unedit')
endfunction

"-----------------------------------------------------------------------------
" CVS watch             {{{1
"-----------------------------------------------------------------------------

function! CVSwatchwatchers()
  call CVSDoCommand('watchers')
endfunction

function! CVSwatchadd()
  let action=CVSQueryAction()
  call CVSDoCommand('watch add '.action)
  unlet action
endfunction

function! CVSwatchremove()
  call CVSDoCommand('watch remove')
endfunction

function! CVSwatchon()
  call CVSDoCommand('watch on')
endfunction

function! CVSwatchoff()
  call CVSDoCommand('watch off')
endfunction

"-----------------------------------------------------------------------------
" CVS tag               {{{1
"-----------------------------------------------------------------------------

function! CVSDoTag(usertag,tagopt)
  " force tagname input
  let tagname=escape(input('tagname:'),'"<>|&')
  if tagname==''
    echo 'CVS tag: aborted'
    return
  endif
  " if rtag, force module instead local copy
  if a:usertag > 0
    let tagcmd = 'rtag '
    let module = input('Enter module name:')
    if module == ''
      echo 'CVS rtag: aborted'
      return
    endif
    let target = module
    unlet module
  else
    let tagcmd = 'tag '
    let target = ''
  endif
  " g:CVSqueryrevision ?
  " tag by date, revision or local
  let tagby=input('Tag by (d)ate, (r)evision (default:none):')
  if (tagby == 'd')
    let tagby='-D '
    let tagwhat=input('Enter date:')
  elseif (tagby == 'r')
    let tagby='-r '
    let tagwhat=CVSInputRev('Revision (optional):')
  else 
    let tagby = ''
  endif
  " input date / revision
  if tagby != ''
    if tagwhat == ''
      echo 'CVS tag: aborted'
      return
    else
      let tagwhat = tagby.tagwhat.' '
    endif
  else
    let tagwhat = ''
  endif
  " check if working file is unchanged (if not rtag)
  if a:usertag == 0
    let checksync=input('Override sync check [n]:')
    if (checksync == 'n') || (checksync == '')
      let checksync='-c '
    else
      let checksync=''
    endif
  else
    let checksync=''
  endif
  call CVSDoCommand(tagcmd.' '.a:tagopt.checksync.tagwhat.tagname,target)
  unlet checksync tagname tagcmd tagby tagwhat target
endfunction

" tag local copy (usertag=0)
function! CVStag()
  call CVSDoTag(0,'')
endfunction

function! CVStagremove()
  call CVSDoTag(0,'-d ')
endfunction

function! CVSbranch()
  call CVSDoTag(0,'-b ')
endfunction

" tag module (usertag=1)
function! CVSrtag()
  call CVSDoTag(1,'')
endfunction

function! CVSrtagremove()
  call CVSDoTag(1,'-d ')
endfunction

function! CVSrbranch()
  call CVSDoTag(1,'-b ',)
endfunction

"-----------------------------------------------------------------------------
" CVS update / query update             {{{1
"-----------------------------------------------------------------------------

function! CVSupdate()
  " ask for revisions to merge/join (if wanted)
  if g:CVSqueryrevision > 0
    let rev=CVSInputRev('Revision (optional):')
    if rev!=''
      let rev='-r '.rev.' '
    endif
    let mergerevstart=CVSInputRev('Merge from 1st Revision (optional):')
    if mergerevstart!=''
      let mergerevstart='-j '.mergerevstart.' '
    endif
    let mergerevend=CVSInputRev('Merge from 2nd Revision (optional):')
    if mergerevend!=''
      let mergerevend='-j '.mergerevend.' '
    endif
  else
    let rev = ''
    let mergerevstart = ''
    let mergerevend = ''
  endif
  " update or query
  if s:CVSupdatequeryonly > 0
    call CVSDoCommand('-n update -P '.rev.mergerevstart.mergerevend)
  else
    call CVSDoCommand('update '.rev.mergerevstart.mergerevend)
     if CVSUsesFile() 
         ex %
     endif
  endif
  unlet rev mergerevstart mergerevend
endfunction

function! CVSqueryupdate()
  let s:CVSupdatequeryonly = 1
  call CVSupdate()
  let s:CVSupdatequeryonly = 0
endfunction

function! CVSupdatetorev()
  " Force revision input
  let rev=CVSInputRev('Revision:')
  if rev==''
    echo "CVS Update to revision: aborted"
    return
  endif
  let rev='-r '.rev.' '
  " save old state
  call CVSSaveOpts()
  let $CVSCMDOPT=rev
  " call update
  call CVSupdate()
  " restore options
  call CVSRestoreOpts()
  unlet rev
endfunction

function! CVSupdatemergerev()
  " Force revision input
  let mergerevstart=CVSInputRev('Merge from 1st Revision:')
  if mergerevstart==''
    echo "CVS merge revision: aborted"
    return
  endif
  let mergerevstart='-j '.mergerevstart.' '
  " save old state
  call CVSSaveOpts()
  let $CVSCMDOPT=mergerevstart
  " call update
  call CVSupdate()
  " restore options
  call CVSRestoreOpts()
  unlet mergerevstart
endfunction

function! CVSupdatemergediff()
  " Force revision input
  let mergerevstart=CVSInputRev('Merge from 1st Revision:')
  if mergerevstart==''
    echo "CVS merge revision diffs: aborted"
    return
  endif
  let mergerevend=CVSInputRev('Merge from 2nd Revision:')
  if mergerevend==''
    echo "CVS merge revision diffs: aborted"
    return
  endif
  let mergerevstart='-j '.mergerevstart.' '
  let mergerevend='-j '.mergerevend.' '
  " save old state
  call CVSSaveOpts()
  let $CVSCMDOPT=mergerevstart.mergerevend
  " call update
  call CVSupdate()
  " restore options
  call CVSRestoreOpts()
endfunction

"-----------------------------------------------------------------------------
" CVS remove (request confirmation)             {{{1
"-----------------------------------------------------------------------------

function! CVSremove()
  " remove from rep. also local ?
  if g:CVSforcedirectory>0
    let localtoo=input('Remove:Also delete local DIRECTORY [y]:')
  else
    let localtoo=input('Remove:Also delete local file [y]:')
  endif
  if (localtoo=='y') || (localtoo=='')
    let localtoo='-f '
  else
    let localtoo=''
  endif
  " force confirmation
  let confrm=input('Remove:confirm with "y":')
  if confrm!='y'
    echo 'CVS remove: aborted'
    return
  endif
  call CVSDoCommand('remove '.localtoo)
  unlet localtoo
endfunction

"-----------------------------------------------------------------------------
" CVS add               {{{1
"-----------------------------------------------------------------------------

function! CVSadd()
  if (g:CVSusedefaultmsg =~ 'a') && (g:CVSdefaultmsg != '')
    let message = g:CVSdefaultmsg
  else
    " force message input
    let message=escape(input('Message:'),'"<>|&')
  endif
  if message==""
    echo 'CVS add: aborted'
    return
  endif
  call CVSDoCommand('add -m "'.message.'"')
  unlet message
endfunction

"-----------------------------------------------------------------------------
" CVS commit            {{{1
"-----------------------------------------------------------------------------

function! CVScommit()
  if (g:CVSusedefaultmsg =~ 'i') && (g:CVSdefaultmsg != '')
    let message = g:CVSdefaultmsg
  else
    " force message input
    let message=escape(input('Message:'),'"<>|&')
  endif
  if message==''
    echo 'CVS commit: aborted'
    return
  endif
  " query revision (if wanted)
  if g:CVSqueryrevision > 0
    let rev=CVSInputRev('Revision (optional):')
  else 
    let rev=''
  endif
  if rev!=''
    let rev='-r '.rev.' '
  endif
  call CVSDoCommand('commit -m "'.message.'" '.rev)
  if CVSUsesFile() 
      ex %
  endif

  unlet message rev
endfunction

function! CVScommitrevision()
  let querybak=g:CVSqueryrevision
  let g:CVSqueryrevision=1
  call CVScommit()
  let g:CVSqueryrevision=querybak
endfunction

"-----------------------------------------------------------------------------
" CVS join in           {{{1
"-----------------------------------------------------------------------------

function! CVSjoinin(...)
  if a:0 == 1
    let message = a:1
  elseif (g:CVSusedefaultmsg =~ 'j') && (g:CVSdefaultmsg != '')
    let message = g:CVSdefaultmsg
  else
    " force message input
    let message=escape(input('Message:'),'"<>|&')
    if message==""
      echo 'CVS add/commit: aborted'
      return
    endif
  endif
  " query revision (if wanted)
  if g:CVSqueryrevision > 0
    let rev=CVSInputRev('Revision (optional):')
  else 
    let rev=''
  endif
  if rev!=''
    let rev='-r '.rev.' '
  endif
  call CVSDoCommand('add -m "'.message.'"')
  call CVSDoCommand('commit -m "'.message.'" '.rev)
  call CVSLocalStatus()
  unlet message rev
endfunction

function! CVSjoininrevision()
  let querybak=g:CVSqueryrevision
  let g:CVSqueryrevision=1
  call CVSjoinin()
  let g:CVSqueryrevision=querybak
endfunction

"-----------------------------------------------------------------------------
" CVS import            {{{1
"-----------------------------------------------------------------------------

function! CVSimport()
  if (g:CVSusedefaultmsg =~ 'p') && (g:CVSdefaultmsg != '')
    let message = g:CVSdefaultmsg
  else
    " force message input
    let message=escape(input('Message:'),'"<>|&')
  endif
  if message==''
    echo 'CVS import: aborted'
    return
  endif
  " query branch (if wanted)
  if g:CVSqueryrevision > 0
    let rev=input('Branch (optional):')
  else 
    let rev=''
  endif
  if rev!=''
    let rev='-b '.rev.' '
  endif
  " query vendor tag
  let vendor=input('Vendor tag:')
  if vendor==''
    echo 'CVS import: aborted'
    return
  endif
  " query release tag
  let release=input('Release tag:')
  if release==''
    echo 'CVS import: aborted'
    return
  endif
  " query module
  let module=input('Module:')
  if module==''
    echo 'CVS import: aborted'
    return
  endif
  " only works on directories
  call CVSDoCommand('import -m "'.message.'" '.rev.module.' '.vendor.' '.release)
  unlet message rev vendor release
endfunction

function! CVSimportrevision()
  let querybak=g:CVSqueryrevision
  let g:CVSqueryrevision=1
  call CVSimport()
  let g:CVSqueryrevision=querybak
endfunction

"-----------------------------------------------------------------------------
" CVS checkout          {{{1
"-----------------------------------------------------------------------------

function! CVScheckout()
  let destdir=expand('%:p:h')
  let destdir=input('Checkout to:',destdir)
  if destdir==''
    return
  endif
  let module=input('Module name:')
  if module==''
    echo 'CVS checkout: aborted'
    return
  endif
  " query revision (if wanted)
  if g:CVSqueryrevision > 0
    let rev=CVSInputRev('Revision (optional):')
  else 
    let rev=''
  endif
  if rev!=''
    let rev='-r '.rev.' '
  endif
  call CVSDoCommand('checkout '.rev.module)
  unlet destdir module rev
endfunction

function! CVScheckoutrevision()
  let querybak=g:CVSqueryrevision
  let g:CVSqueryrevision=1
  call CVScheckout()
  let g:CVSqueryrevision=querybak
endfunction
"}}}
"#############################################################################
" extended commands
"#############################################################################
"-----------------------------------------------------------------------------
" revert changes, shortstatus           {{{1
"-----------------------------------------------------------------------------

function! CVSrevertchanges()
  let filename=expand("%:p:t")
  call CVSChDir(expand("%:p:h"))
  if filename == ''
    echo 'Revert changes:only on files'
    return
  endif
  if delete(filename) != 0
    echo 'Revert changes:could not delete file'
    return
  endif
  call CVSSaveOpts()
  let $CVSCMDOPT='-A '
  call CVSupdate()
  call CVSRestoreOpts()
  call CVSRestoreDir()
endfunction

" get status info, compress it (one line/file), sort by status
function! CVSshortstatus()
  let isfile = CVSUsesFile()
  " save flags
  let filename = expand("%:p:t")
  let savedump = g:CVSdumpandclose
  let forcedirbak = g:CVSforcedirectory
  " output needed
  let g:CVSdumpandclose=0
  silent call CVSstatus()
  call CVSMakeRW()
  silent call CVSCompressStatus()
  if g:CVSsortoutput > 0
    silent call CVSSortStatusOutput()
  endif
  normal gg
  call CVSMakeRO()
  " restore flags
  let g:CVSdumpandclose = savedump
  if forcedirbak == 1
    let g:CVSforcedirectory = 0
  else
    let g:CVSforcedirectory = forcedirbak
  endif
  if   (g:CVSdumpandclose == 1) || ((g:CVSdumpandclose == 2) && isfile)
    call CVSDumpAndClose()
  endif
  unlet savedump forcedirbak filename isfile
endfunction

"-----------------------------------------------------------------------------
" tools: output processing, input query         {{{1
"-----------------------------------------------------------------------------

" Dump output to statusline, close output buffer
function! CVSDumpAndClose()
  " collect in reg. z first, otherwise func
  " will terminate, if user stops output with "q"
  let curlin=1
  let regbak=@z
  let @z = getline(curlin)
  while curlin < line("$")
    let curlin = curlin + 1
    let @z = @z . "\n" . getline(curlin)
  endwhile
  " appends \n on winnt
  "exec ":1,$y z"
  set nomodified
  bwipeout
  if g:CVSstatusline
    redraw
    " statusline may be cleared otherwise
    echo @z
  endif
  if g:CVStitlebar
    let cleantitle = substitute(@z,'\t\|\r\|\s\{2,\}',' ','g')
    let cleantitle = substitute(cleantitle,"\n",' ',"g")
    let &titlestring = '%F - '.cleantitle
    let b:CVSbuftitle = &titlestring
    unlet cleantitle
  endif
  let @z=regbak
endfunction

" leave only leading line with status info (for CVSShortStatus)
function! CVSCompressStatus()
  exec 'g!/^File:\|?/d'
endfunction

" delete stderr ('checking out...')
" CVS checkout ends with ***************(15)
function! CVSStripHeader()
  call CVSMakeRW()
  silent! exec '1,/^\*\{15}$/d'
endfunction

function! CVSInputRev(...)
  if !exists("b:CVSentry")
    let b:CVSentry = ''
  endif
  if a:0 == 1
    let query = a:1
  else
    let query = ''
  endif
  if g:CVSofferrevision > 0
    let default = CVSSubstr(b:CVSentry,'/',2)
  else
    let default = ''
  endif
  return input(query,default)
endfunction

"-----------------------------------------------------------------------------
" quick get file.               {{{1
"-----------------------------------------------------------------------------

function! CVSGet(...)
  " Params :
  " 0:ask file,rep
  " 1:filename
  " 2:repository
  " 3:string:i=login,o=logout
  " 4:string:login password
  " save flags, do not destroy CVSSaveOpts
  let cvsoptbak=$CVSCMDOPT
  let outputbak=g:CVSdumpandclose
    let rep=''
    let log=''
  " eval params
  if     a:0 > 2        " file,rep,logflag[,logpw]
    let fn  = a:1
    let rep = a:2
    let log = a:3
  elseif a:0 > 1        " file,rep
    let fn  = a:1
    let rep = a:2
  elseif a:0 > 0        " file: (use current rep) 
    let fn  = a:1
  endif
  if fn == ''           " no name:query file and rep
    let rep=input("CVSROOT:")
    let fn=input("Filename:")
  endif
  " still no filename : abort
  if fn == ''
    echo "CVS Get: aborted"
  else
    " prepare param
    if rep != ''
      let $CVSOPT = '-d'.rep
    endif
    " no output windows
    let g:CVSdumpandclose=0
    " login
    if match(log,'i') > -1
      if (a:0 == 4)     " login with pw (if given)
        call CVSlogin(a:4)
      else
        call CVSlogin()
      endif
    endif
    " get file
    call CVSDoCommand('checkout -p',fn)
    " delete stderr ('checking out...')
    if !CVSIsW9x()
      call CVSStripHeader()
    endif
    set nomodified
    " logout
    if match(log,'o') > -1
      call CVSlogout()
    endif
  endif
  " restore flags, cleanup
  let g:CVSdumpandclose=outputbak
  let $CVSOPT=cvsoptbak
  unlet fn rep outputbak cvsoptbak
endfunction

"-----------------------------------------------------------------------------
" Download help and install             {{{1
"-----------------------------------------------------------------------------

function! CVSInstallUpdates()
  if confirm("Install updates: Close all buffers, first !", 
           \ "&Cancel\n&Ok") < 2
    echo 'CVS Install updates: aborted'
    return
  endif
  call CVSDownloadUpdates()
  if match(getline(1),'^\*cvsmenu.txt\*') > -1
    call CVSInstallAsHelp('cvsmenu.txt')
    let helpres="Helpfile installed"
  else
    let helpres="Error: Helpfile not installed"
  endif
  bwipeout
  redraw
  if match(getline(1),'^" CVSmenu.vim :') > -1
    call CVSInstallAsPlugin('cvsmenu.vim')
    let plugres="Plugin installed"
  else
    let plugres="Error: Plugin not installed"
  endif
  bwipeout
  redraw
  echo helpres."\n".plugres
  echo "Changes take place in next vim session"
endfunction

function! CVSDownloadUpdates()
  call CVSGet('VimTools/cvsmenu.vim',s:cvsmenucvs,'i','')
  call CVSGet('VimTools/cvsmenu.txt',s:cvsmenucvs,'o','')
endfunction

function! CVSInstallAsHelp(...)
  " ask for name to save as (if not given)
  if (a:0 == 0) || (a:1 == '')
    let dest=input('Helpfilename (clear to abort):')
  else
    let dest=a:1
  endif
  " abort if still no filename
  if dest==''
    echo 'CVS Install help: aborted'
  else
    " create directories "~/.vim/doc" if needed
    call CVSAssureLocalDirs()
    " copy to local doc dir
    exec 'w! '.s:localvimdoc.'/'.dest
    " create tags
    exec 'helptags '.s:localvimdoc
  endif
  unlet dest
endfunction

function! CVSInstallAsPlugin(...)
  " ask for name to save as
  if (a:0 == 0) || (a:1 == '')
    let dest=input('Pluginfilename (clear to abort):',a:1)
  else
    let dest=a:1
  endif
  " abort if still no filename
  if dest==''
    echo 'CVS Install plugin: aborted'
  else
    " copy to plugin dir
    exec 'w! '.$VIMRUNTIME.'/plugin/'.dest
  endif
  unlet dest
endfunction

"-----------------------------------------------------------------------------
" user directories / CVSLinks           {{{1
"-----------------------------------------------------------------------------

function! CVSOpenLinks()
  let links=s:localvim.s:sep.'cvslinks.vim'
  call CVSAssureLocalDirs()
  if !filereadable(links)
    let @z = "\" ~/cvslinks.vim\n"
      \ . "\" move to a command and press <shift-cr> to execute it\n"
      \ . "\" (one-liners only).\n\n"
      \ . "nmap <buffer> <s-cr> :exec getline('.')<cr>\n"
      \ . "finish\n\n"
      \ . "\" add modifications below here\n\n"
      \ . "\" look for a new Vim\n"
      \ . "\" login, get latest Vim README.txt, logout\n"
      \ . "call CVSGet('vim/README.txt', ':pserver:anonymous@cvs.vim.org:/cvsroot/vim', 'io', '')\n\n"
      \ . "\" manual cvsmenu update (-> CVS.Settings.Install buffer as...)\n"
      \ . "\" login, get latest version of cvsmenu.vim\n"
      \ . "call CVSGet('VimTools/cvsmenu.vim',':pserver:anonymous@cvs.ezytools.sf.net:/cvsroot/ezytools','i','')\n"
      \ . "\" get latest cvsmenu.txt, logout\n"
      \ . "call CVSGet('VimTools/cvsmenu.vim',':pserver:anonymous@cvs.ezytools.sf.net:/cvsroot/ezytools','o','')\n\n"
      \ . "\" Get some help on this\n"
      \ . "help CVSFunctions"
    "exec ':cd '.s:localvim
    call CVSChDir(s:localvim)
    new
    normal "zP
    exec ':x '.links
    call CVSRestoreDir()
  endif
  if !filereadable(links)
    echo 'CVSLinks: cannot access '.links
    return
  endif
  exec ':sp '.links
  exec ':so %'
  unlet links
endfunction

function! CVSAssureLocalDirs()
  if !isdirectory(s:localvim)
    silent! exec '!mkdir '.s:localvim
  endif
  if !isdirectory(s:localvimdoc)
    silent! exec '!mkdir '.s:localvimdoc
  endif
endfunction

function! CVSGetFolderNames()
  if has("unix")
    " expands to /home/(user)
    let s:localvim=expand('~').s:sep.'.vim'
  else
    " expands to $HOME (must be set)
    if expand('~') == ''
      let s:localvim=$VIMRUNTIME
    else
      let s:localvim=expand('~').s:sep.'vimfiles'
    endif
  endif
  let s:localvimdoc=s:localvim.s:sep.'doc'
endfunction

"-----------------------------------------------------------------------------
" LocalStatus : read from CVS/Entries           {{{1
"-----------------------------------------------------------------------------

function! CVSLocalStatus()
  " needs to be called from orgbuffer
  let isfile = CVSUsesFile()
  " change to buffers directory
  "exec 'cd '.expand('%:p:h')
  call CVSChDir(expand('%:p:h'))
  if g:CVSforcedirectory>0
    let filename=expand('%:p:h')
  else
    let filename=expand('%:p:t')
  endif
  let regbak=@z
  let @z = CVSCompare(filename)
  new
  " seems to be a vim bug : when executed as autocommand when doing ':help', 
  " vim echoes 'not modidifiable'
  set modifiable
  normal "zP
  call CVSProcessOutput(isfile, filename, '*localstatus')
  let @z=regbak
  call CVSRestoreDir()
  unlet filename
endfunction

" get info from CVS/Entries about given/current buffer/dir
function! CVSCompare(...)
  " return, if no CVS dir
  if !filereadable(s:CVSentries)
    echo 'No '.s:CVSentries.' !'
    return
  endif
  " eval params
  if (a:0 == 1) && (a:1 != '')
    if filereadable(a:1)
      let filename = a:1
      let dirname  = ''
    else
      let filename = ''
      let dirname  = a:1
    endif
  else
    let filename = expand("%:p:t")
    let dirname  = expand("%:p:h")
  endif
  let result = ''
  if filename == ''
    let result = CVSGetLocalDirStatus(dirname)
  else
    let result = CVSGetLocalStatus(filename)
  endif  " filename given
  return result
endfunction

" get info from CVS/Entries about given file/current buffer
function! CVSGetLocalStatus(...)
  if a:0 == 0
    let filename = expand("%:p:t")
  else
    let filename = a:1
  endif
  if filename == ''
    return 'error:no filename'
  endif
  if a:0 > 1
    let entry=a:2
  else
    let entry=CVSGetEntry(filename)
  endif
  let b:CVSentry=entry
  let status = ''
  if entry == ''
    if isdirectory(filename)
      let status = "unknown:   <DIR>\t".filename"
    else
      let status = 'unknown:   '.filename
    endif
  else
    let entryver  = CVSSubstr(entry,'/',2)
    let entryopt  = CVSSubstr(entry,'/',4)
    let entrytag  = CVSSubstr(entry,'/',5)
    let entrytime = CVStimeToStr(entry)
    if (!CVSUsesFile()) || (g:CVSfullstatus > 0)
      let status = filename."\t".entryver." ".entrytime." ".entryopt." ".entrytag
    else
      let status = entryver." ".entrytag." ".entryopt
    endif
    if !filereadable(filename)
      if isdirectory(filename)
        let status = 'directory: '.filename
      else
        if entry[0] == 'D'
          let status = "missing:   <DIR>\t".filename
        else
          let status = 'missing:   '.status
        endif
      endif
    else
      if entrytime == CVSFiletimeToStr(filename)
        let status = 'unchanged: '.status
      else
        let status = 'modified:  '.status
      endif " time identical
    endif " file exists
    if CVSUsesFile()
      let status = substitute(status,':','','g')
      let status = substitute(status,'\s\{2,}',' ','g')
    endif
  endif  " entry found
  unlet entry
  return status
endfunction

" get info on all files from CVS/Entries and given/current directory
" opens CVS/Entries only once, passing each entryline to CVSGetLocalStatus
function! CVSGetLocalDirStatus(...)
  let zbak = @z
  let ybak = @y
  if a:0 == 0
    let dirname = expand("%:p:h")
  else
    let dirname = a:1
  endif
  "exec 'cd '.dirname
  call CVSChDir(dirname)
  if has("unix")
    let @z = glob("*")
  else
    let @z = glob("*.*")
  endif
  new
  silent! exec 'read '.s:CVSentries
  let entrycount = line("$") - 1
  normal k"zP
  if (line("$") == entrycount) && (getline(entrycount) == '')
    " empty directory
    set nomodified
    return
  endif
  let filecount = line("$") - entrycount
  " collect status of all found files in @y
  let @y = ''
  let curlin = 0
  while (curlin < filecount)
    let curlin = curlin + 1
    let fn=getline(curlin)
    if fn != 'CVS'
      let search=escape(fn,'.')
      let v:errmsg = ''
      " find CVSEntry
      silent! exec '/^D\?\/'.search.'\//'
      if v:errmsg == ''
        let entry = getline(".")
        " clear found file from CVS/Entries
        silent! exec 's/.*//eg'
      else
        let entry = ''
      endif
      " fetch info
      let @y = @y . CVSGetLocalStatus(fn,entry) . "\n"
    endif
  endwhile
  " process files from CVS/Entries
  let curlin = filecount
  while (curlin < line("$"))
    let curlin = curlin + 1
    let entry = getline(curlin)
    let fn=CVSSubstr(entry,'/',1)
    if fn != ''
      let @y = @y . CVSGetLocalStatus(fn,entry) . "\n"
    endif
  endwhile
  set nomodified
  let result = @y
  bwipeout
  call CVSRestoreDir()
  let @z = zbak
  let @y = ybak
  unlet zbak ybak
  return result
endfunction

" return info about filename from 'CVS/Entries'
function! CVSGetEntry(filename)
  let result = ''
  if a:filename != ''
    silent! exec 'split '.s:CVSentries
    let v:errmsg = ''
    let search=escape(a:filename,'.')
    silent! exec '/^D\?\/'.search.'\//'
    if v:errmsg == ''
      let result = getline(".")
    endif
    set nomodified
    silent! bwipeout
  endif
  return result
endfunction

" extract and convert timestamp from CVSEntryItem
function! CVStimeToStr(entry)
  return CVSAsctimeToStr(CVSSubstr(a:entry,'/',3))
endfunction
" get and convert filetime
" include local time zone info
function! CVSFiletimeToStr(filename)
  let time=getftime(a:filename)-(GMTOffset() * 60*60)
  return strftime('%Y-%m-%d %H:%M:%S',time)
endfunction

" entry format : ISO C asctime()
function! CVSAsctimeToStr(asctime)
  let mon=strpart(a:asctime, 4,3)
  let DD=CVSLeadZero(strpart(a:asctime, 8,2))
  let hh=CVSLeadZero(strpart(a:asctime, 11,2))
  let nn=CVSLeadZero(strpart(a:asctime, 14,2))
  let ss=CVSLeadZero(strpart(a:asctime, 17,2))
  let YY=strpart(a:asctime, 20,4)
  let MM=CVSMonthIdx(mon)
  " CVS/WinNT : no date given for merge-results
  if MM == ''
    let result = ''
  else
    let result = YY.'-'.MM.'-'.DD.' '.hh.':'.nn.':'.ss
  endif
  unlet YY MM DD hh nn ss mon
  return result
endfunction

" append a leading zero
function! CVSLeadZero(value)
  let nr=substitute(a:value,' ','','g') + 0
  if (nr < 10)
    let nr = '0' . nr
  endif
  return nr
endfunction

" return month (leading zero) from cleartext
function! CVSMonthIdx(month)
  if match(a:month,'Jan') > -1
    return '01'
  elseif match(a:month,'Feb') > -1
    return '02'
  elseif match(a:month,'Mar') > -1
    return '03'
  elseif match(a:month,'Apr') > -1
    return '04'
  elseif match(a:month,'May') > -1
    return '05'
  elseif match(a:month,'Jun') > -1
    return '06'
  elseif match(a:month,'Jul') > -1
    return '07'
  elseif match(a:month,'Aug') > -1
    return '08'
  elseif match(a:month,'Sep') > -1
    return '09'
  elseif match(a:month,'Oct') > -1
    return '10'
  elseif match(a:month,'Nov') > -1
    return '11'
  elseif match(a:month,'Dec') > -1
    return '12'
  else
    return
endfunction

" divide string by sep, return field[index] .start at 0.
function! CVSSubstr(string,separator,index)
  let sub = ''
  let idx = 0
  let bst = 0
  while (bst < strlen(a:string)) && (idx <= a:index)
    if a:string[bst] == a:separator
      let idx = idx + 1
    else
      if (idx == a:index)
        let sub = sub . a:string[bst]
      endif
    endif
    let bst = bst + 1
  endwhile
  unlet idx bst
  return sub
endfunction

"Get difference between local time and GMT
"strftime() returns the adjusted time
"->strftime(0) GMT=00:00:00, GMT+1=01:00:00
"->midyear=summertime: strftime(182*24*60*60)=02:00:00 (GMT+1)
"linux bug:wrong CEST information before 1980
"->use 331257600 = 01.07.80 00:00:00
function! GMTOffset()
  let winter1980 = (10*365+2)*24*60*60      " = 01.01.80 00:00:00
  let summer1980 = winter1980+182*24*60*60  " = 01.07.80 00:00:00
  let summerhour = strftime("%H",summer1980)
  let summerzone = strftime("%Z",summer1980)
  let winterhour = strftime("%H",winter1980)
  let winterday  = strftime("%d",winter1980)
  let curzone    = strftime("%Z",localtime())
  if curzone == summerzone
    let result = summerhour
  else
    let result = winterhour
  endif
  " GMT - x : invert sign
  if winterday == 31
    let result = -1 * result
  endif
  unlet curzone winterday winterhour summerzone summerhour summer1980 winter1980
  return result
endfunction

"-----------------------------------------------------------------------------
" Autocommand : set title, restore diffmode             {{{1
"-----------------------------------------------------------------------------

" restore title
function! CVSBufEnter()
  " set/reset title
  if g:CVStitlebar
    if !exists("b:CVSbuftitle")
      let &titlestring = s:CVSorgtitle
    else
      let &titlestring = b:CVSbuftitle
    endif
  endif
endfunction

" show status, add syntax
function! CVSBufRead(...)
  " query status if wanted, file and CVSdir exist
  if (g:CVSautocheck > 0)
   \ && (expand("%:p:t") != '')
   \ && filereadable(expand("%:p:h").s:sep.s:CVSentries)
    call CVSLocalStatus()
  endif
  " highlight conflicts on every file
  call CVSAddConflictSyntax()
endfunction

" save pre diff settings
function! CVSDiffEnter()
  let g:CVSdifforgbuf = bufnr('%')
  let g:CVSbakdiff              = &diff
  let g:CVSbakscrollbind        = &scrollbind
  let g:CVSbakwrap              = &wrap
  let g:CVSbakfoldcolumn        = &foldcolumn
  let g:CVSbakfoldenable        = &foldenable
  let g:CVSbakfoldlevel         = &foldlevel
  let g:CVSbakfoldmethod        = &foldmethod
endfunction

" restore pre diff settings
function! CVSDiffLeave()
  call setwinvar(bufwinnr(g:CVSdifforgbuf), '&diff'       , g:CVSbakdiff        )
  call setwinvar(bufwinnr(g:CVSdifforgbuf), '&scrollbind' , g:CVSbakscrollbind  )
  call setwinvar(bufwinnr(g:CVSdifforgbuf), '&wrap'       , g:CVSbakwrap        )
  call setwinvar(bufwinnr(g:CVSdifforgbuf), '&foldcolumn' , g:CVSbakfoldcolumn  )
  call setwinvar(bufwinnr(g:CVSdifforgbuf), '&foldenable' , g:CVSbakfoldenable  )
  call setwinvar(bufwinnr(g:CVSdifforgbuf), '&foldlevel'  , g:CVSbakfoldlevel   )
  call setwinvar(bufwinnr(g:CVSdifforgbuf), '&foldmethod' , g:CVSbakfoldmethod  )
endfunction

" save original settings
function! CVSBackupDiffMode()
  let g:CVSorgdiff              = &diff
  let g:CVSorgscrollbind        = &scrollbind
  let g:CVSorgwrap              = &wrap
  let g:CVSorgfoldcolumn        = &foldcolumn
  let g:CVSorgfoldenable        = &foldenable
  let g:CVSorgfoldlevel         = &foldlevel
  let g:CVSorgfoldmethod        = &foldmethod
endfunction

" restore original settings
function! CVSRestoreDiffMode()
  let &diff                     = g:CVSorgdiff
  let &scrollbind               = g:CVSorgscrollbind
  let &wrap                     = g:CVSorgwrap
  let &foldcolumn               = g:CVSorgfoldcolumn
  let &foldenable               = g:CVSorgfoldenable
  let &foldlevel                = g:CVSorgfoldlevel
  let &foldmethod               = g:CVSorgfoldmethod
endfunction

" this is useful for mapping
function! CVSSwitchDiffMode()
  if &diff
    call CVSRestoreDiffMode()
  else
    diffthis
  endif
endfunction

" remember restoring prediff mode
function! CVSDiffPrepareLeave()
  if match(expand("<afile>:e"),'dif','i') > -1
    " diffed buffer gets unloaded twice by :vert diffs
    " only react to the second unload
    let g:CVSleavediff = g:CVSleavediff + 1
    " restore prediff settings (see CVSPrepareLeave)
    if (g:CVSsavediff > 0) && (g:CVSleavediff > 1)
      call CVSDiffLeave()
      let g:CVSleavediff = 0
    endif
  endif
endfunction

"-----------------------------------------------------------------------------
" finalization          {{{1
"-----------------------------------------------------------------------------

call CVSGetFolderNames()                " vim user directories
call CVSMakeLeaderMapping()             " create keymappings from menu shortcuts
call CVSBackupDiffMode()                " save pre :diff settings

" provide direct access to CVS commands, using dumping and sorting from this script
command! -nargs=+ -complete=expression -complete=file -complete=function -complete=var CVS call CVSDoCommand(<q-args>)

" highlight conflicts on every file
au BufRead * call CVSBufRead()
" set title
au BufEnter * call CVSBufEnter()
" restore prediff settings
au BufWinLeave *.dif call CVSDiffPrepareLeave()

if !exists("loaded_cvsmenu")
  let loaded_cvsmenu=1
endif


"}}}
" ToolBar icons added by Folke {{{1
" .xpm's should be in runtimepath/bitmaps or ~/.vim/bitmaps
function! CVSwu()
    if &modified
        silent! w
    endif
    call CVSupdate()
endfun

function! CVSwc()
    if &modified 
        silent! w
    endif
    call CVScommit()
endfun

function! CVSwdrev()
    if &modified 
        silent! w
    endif
    call CVSdifftorev()
endfun

function! CVSwd()
    if &modified 
        silent! w
    endif
    call CVSdiff()
endfun


function! CVSwqf()
    if &modified 
        silent! w
    endif
    call CVSSetForceDir(1)
    call CVSqueryupdate()
endfun

amenu icon=cvs-update.xpm ToolBar.cvs-update    :call CVSwu()<cr>
tmenu ToolBar.cvs-update CVS-Update
amenu icon=cvs-commit.xpm ToolBar.cvs-commit    :call CVSwc()<cr>
tmenu ToolBar.cvs-commit CVS-Commit
amenu icon=cvs-diff.xpm ToolBar.cvs-diff        :call CVSwdrev()<cr>
tmenu ToolBar.cvs-diff CVS Diff to Revision
amenu icon=cvs-queryfolder.xpm ToolBar.cvs-queryfolder  :call CVSwqf()<cr>
tmenu ToolBar.cvs-queryfolder CVS Query folder

"end tool bar
"}}}
" Mappings {{{1
inoremap <F5>   <esc>:call CVSwu()<cr>a
noremap  <F5>   :call CVSwu()<cr>
inoremap <F6>   <esc>:call CVSwc()<cr>a
noremap  <F6>   :call CVSwc()<cr>
inoremap <F7>   <esc>:call CVSwfq()<cr>a
noremap  <F7>   :call CVSwd()<cr>
inoremap <S-F7> <esc>:call CVSwdrev()<cr>a
noremap  <S-F7> :call CVSwdrev()<cr>
inoremap <F8>   <esc>:call CVSwqf()<cr>a
noremap  <F8>   :call CVSwqf()<cr>
" }}}