"Fossies" - the Fresh Open Source Software Archive

Member "viewvc-1.2.1/bin/loginfo-handler" (26 Mar 2020, 10160 Bytes) of package /linux/misc/viewvc-1.2.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "loginfo-handler": 1.1.28_vs_1.2.1.

    1 #!/usr/bin/env python
    2 # -*-python-*-
    3 #
    4 # Copyright (C) 1999-2020 The ViewCVS Group. All Rights Reserved.
    5 #
    6 # By using this file, you agree to the terms and conditions set forth in
    7 # the LICENSE.html file which can be found at the top level of the ViewVC
    8 # distribution or at http://viewvc.org/license-1.html.
    9 #
   10 # For more information, visit http://viewvc.org/
   11 #
   12 # -----------------------------------------------------------------------
   13 #
   14 # updates SQL database with new commit records
   15 #
   16 # -----------------------------------------------------------------------
   17 #
   18 
   19 #########################################################################
   20 #
   21 # INSTALL-TIME CONFIGURATION
   22 #
   23 # These values will be set during the installation process. During
   24 # development, they will remain None.
   25 #
   26 
   27 LIBRARY_DIR = None
   28 CONF_PATHNAME = None
   29 
   30 # Adjust sys.path to include our library directory
   31 import sys
   32 import os
   33 
   34 if LIBRARY_DIR:
   35     sys.path.insert(0, LIBRARY_DIR)
   36 else:
   37     sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0], "../../lib")))
   38 
   39 #########################################################################
   40 
   41 import os
   42 import getopt
   43 import re
   44 import cvsdb
   45 import viewvc
   46 import vclib.ccvs
   47 
   48 DEBUG_FLAG = 0
   49 
   50 ## output functions
   51 def debug(text):        
   52     if DEBUG_FLAG:
   53         if type(text) != (type([])):
   54             text = [text]
   55         for line in text:
   56             line = line.rstrip('\n\r')
   57             print 'DEBUG(viewvc-loginfo):', line
   58 
   59 def warning(text):
   60     print 'WARNING(viewvc-loginfo):', text
   61 
   62 def error(text):
   63     print 'ERROR(viewvc-loginfo):', text
   64     sys.exit(1)
   65 
   66 _re_revisions = re.compile(                            
   67     r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)"  # comma and first revision
   68     r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)"  # comma and second revision
   69     r"(?:$| )"                                     # space or end of string
   70 )    
   71 
   72 def Cvs1Dot12ArgParse(args):
   73     """CVS 1.12 introduced a new loginfo format while provides the various
   74     pieces of interesting version information to the handler script as
   75     individual arguments instead of as a single string."""
   76 
   77     if args[1] == '- New directory':
   78         return None, None
   79     elif args[1] == '- Imported sources':
   80         return None, None
   81     else:
   82         directory = args.pop(0)
   83         files = []
   84         while len(args) >= 3:
   85             files.append(args[0:3])
   86             args = args[3:]
   87         return directory, files
   88   
   89 def HeuristicArgParse(s, repository):
   90     """Older versions of CVS (except for CVSNT) do not escape spaces in file
   91     and directory names that are passed to the loginfo handler. Since the input
   92     to loginfo is a space separated string, this can lead to ambiguities. This
   93     function attempts to guess intelligently which spaces are separators and
   94     which are part of file or directory names. It disambiguates spaces in
   95     filenames from the separator spaces between files by assuming that every
   96     space which is preceded by two well-formed revision numbers is in fact a 
   97     separator. It disambiguates the first separator space from spaces in the 
   98     directory name by choosing the longest possible directory name that
   99     actually exists in the repository"""
  100 
  101     if (s[-16:] == ' - New directory'
  102             or s[:26] == ' - New directory,NONE,NONE'):
  103         return None, None
  104 
  105     if (s[-19:] == ' - Imported sources'
  106             or s[-29:] == ' - Imported sources,NONE,NONE'):
  107         return None, None
  108 
  109     file_data_list = []
  110     start = 0
  111 
  112     while 1:
  113         m = _re_revisions.search(s, start)
  114 
  115         if start == 0:
  116             if m is None: 
  117                 error('Argument "%s" does not contain any revision numbers' \
  118                       % s)
  119         
  120             directory, filename = FindLongestDirectory(s[:m.start()],
  121                                                        repository)
  122             if directory is None:
  123                 error('Argument "%s" does not start with a valid directory' \
  124                       % s)
  125 
  126             debug('Directory name is "%s"' % directory)
  127 
  128         else:
  129             if m is None:
  130                 warning('Failed to interpret past position %i in the loginfo '
  131                         'argument, leftover string is "%s"' \
  132                         % start, pos[start:])
  133             
  134             filename = s[start:m.start()]
  135             
  136         old_version, new_version = m.group('old', 'new')
  137 
  138         file_data_list.append((filename, old_version, new_version))
  139 
  140         debug('File "%s", old revision %s, new revision %s' 
  141             % (filename, old_version, new_version))
  142 
  143         start = m.end()
  144         
  145         if start == len(s): break
  146 
  147     return directory, file_data_list
  148 
  149 def FindLongestDirectory(s, repository):
  150     """Splits the first part of the argument string into a directory name
  151     and a file name, either of which may contain spaces. Returns the longest
  152     possible directory name that actually exists"""
  153 
  154     parts = s.split()
  155     
  156     for i in range(len(parts)-1, 0, -1):
  157         directory = ' '.join(parts[:i])
  158         filename = ' '.join(parts[i:])
  159         if os.path.isdir(os.path.join(repository, directory)):
  160             return directory, filename
  161     
  162     return None, None
  163 
  164 _re_cvsnt_revisions = re.compile(                            
  165     r"(?P<filename>.*)"                            # comma and first revision
  166     r",(?P<old>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)"  # comma and first revision
  167     r",(?P<new>(?:\d+\.\d+)(?:\.\d+\.\d+)*|NONE)"  # comma and second revision
  168     r"$"                                           # end of string
  169 )
  170 
  171 def CvsNtArgParse(s, repository):
  172     """CVSNT escapes all spaces in filenames and directory names with 
  173     backslashes"""
  174 
  175     if s[-18:] == r' -\ New\ directory':
  176         return None, None
  177 
  178     if s[-21:] == r' -\ Imported\ sources':
  179         return None, None
  180 
  181     file_data_list = []
  182     directory, pos = NextFile(s)
  183     
  184     debug('Directory name is "%s"' % directory)
  185 
  186     while 1:
  187         fileinfo, pos = NextFile(s, pos)
  188         if fileinfo is None:
  189             break
  190 
  191         m = _re_cvsnt_revisions.match(fileinfo)
  192         if m is None:
  193             warning('Can\'t parse file information in "%s"' % fileinfo)
  194             continue
  195 
  196         file_data = m.group('filename', 'old', 'new')
  197         file_data_list.append(file_data)
  198 
  199         debug('File "%s", old revision %s, new revision %s' % file_data)
  200 
  201     return directory, file_data_list
  202 
  203 def NextFile(s, pos = 0):
  204     escaped = 0
  205     ret = ''
  206     i = pos
  207     while i < len(s):
  208         c = s[i]
  209         if escaped:
  210             ret += c
  211             escaped = 0
  212         elif c == '\\':
  213             escaped = 1
  214         elif c == ' ':
  215             return ret, i + 1
  216         else:
  217             ret += c
  218         i += 1
  219 
  220     return ret or None, i
  221 
  222 def ProcessLoginfo(rootpath, directory, files):
  223     cfg = viewvc.load_config(CONF_PATHNAME)
  224     db = cvsdb.ConnectDatabase(cfg)
  225     repository = vclib.ccvs.CVSRepository(None, rootpath, None,
  226                                           cfg.utilities, 0)
  227 
  228     # split up the directory components
  229     dirpath = filter(None, os.path.normpath(directory).split(os.sep))
  230 
  231     ## build a list of Commit objects
  232     commit_list = []
  233     for filename, old_version, new_version in files:
  234         filepath = dirpath + [filename]
  235 
  236         ## XXX: this is nasty: in the case of a removed file, we are not
  237         ##      given enough information to find it in the rlog output!
  238         ##      So instead, we rlog everything in the removed file, and
  239         ##      add any commits not already in the database
  240         if new_version == 'NONE':
  241             commits = cvsdb.GetUnrecordedCommitList(repository, filepath, db)
  242         else:
  243             commits = cvsdb.GetCommitListFromRCSFile(repository, filepath,
  244                                                      new_version)
  245 
  246         commit_list.extend(commits)
  247 
  248     ## add to the database
  249     db.AddCommitList(commit_list)
  250 
  251 
  252 ## MAIN
  253 if __name__ == '__main__':
  254     ## get the repository from the environment
  255     try:
  256         repository = os.environ['CVSROOT']
  257     except KeyError:
  258         error('CVSROOT not in environment')
  259 
  260     debug('Repository name is "%s"' % repository)
  261 
  262     ## parse arguments
  263 
  264     argc = len(sys.argv)
  265     debug('Got %d arguments:' % (argc))
  266     debug(map(lambda x: '   ' + x, sys.argv))
  267     
  268     # if we have more than 3 arguments, we are likely using the
  269     # newer loginfo format introduced in CVS 1.12:
  270     # 
  271     #    ALL <path>/bin/loginfo-handler %p %{sVv}
  272     if argc > 3:
  273         directory, files = Cvs1Dot12ArgParse(sys.argv[1:])
  274     else:
  275         if len(sys.argv) > 1:
  276             # the first argument should contain file version information
  277             arg = sys.argv[1]
  278         else:
  279             # if there are no arguments, read version information from
  280             # first line of input like old versions of ViewCVS did
  281             arg = sys.stdin.readline().rstrip()
  282     
  283         if len(sys.argv) > 2:
  284             # if there is a second argument it indicates which parser
  285             # should be used to interpret the version information
  286             if sys.argv[2] == 'cvs':
  287                 fun = HeuristicArgParse
  288             elif sys.argv[2] == 'cvsnt':
  289                 fun = CvsNtArgParse
  290             else:
  291                 error('Bad arguments')
  292         else:
  293             # if there is no second argument, guess which parser to use based
  294             # on the operating system. Since CVSNT now runs on Windows and
  295             # Linux, the guess isn't necessarily correct
  296             if sys.platform == "win32":
  297                 fun = CvsNtArgParse
  298             else:
  299                 fun = HeuristicArgParse
  300         
  301         directory, files = fun(arg, repository)
  302     
  303     debug('Discarded from stdin:')
  304     debug(map(lambda x: '   ' + x, sys.stdin.readlines())) # consume stdin
  305 
  306     repository = cvsdb.CleanRepository(repository)
  307 
  308     debug('Repository: %s' % (repository))
  309     debug('Directory: %s' % (directory))
  310     debug('Files: %s' % (str(files)))
  311 
  312     if files is None:
  313         debug('Not a checkin, nothing to do')
  314     else:
  315         ProcessLoginfo(repository, directory, files)
  316 
  317     sys.exit(0)