#!/usr/bin/env python
# -*- coding: utf-8 -*-

# $Id: bacsource2configrules.py 11326 2010-10-25 07:56:12Z pstorz $

"""Helper script to generate resource classes from 
bacula c definitions.

Usage:
c2py_resource.py BACULA_SRC_PATH 
"""

####
#    RESOURCE_TYPE has to be either 'dird', 'console', 'filed' or 'stored'
#    CONF_C_FILE is the corresponding c source e.g. 'dird/dird_conf.c'
#"""




C_CONFS = (
           ("dird", "dird_conf.c"),
           ("console", "console_conf.c"),
           ("filed", "filed_conf.c"),
           ("stored", "stored_conf.c"),
           ("lib", "parse_conf.c"),
           ("inc" , "inc_conf.c"),
           )



import os
import re
from nosferatu.config_classes import *
from resource import Resource
from nosferatu import prettynames
import pprint

RXP_RESOURCE = re.compile(r'''
    ^(?:static\s+)?RES_ITEM(2)?\s+
    (?P<name>[^[]+)\[\]\s+=\s+\{\s*\n # First line with starting brace
    (?P<items>.*?)                    # Non greedy search
    \n}\s*;
''', re.M|re.X|re.S)


RXP_ITEM = re.compile(r'''
    ^\s+{"(?P<name>[^"]+)"\s*,
    \s*(?P<handler>[^,]+)\s*,
    \s*(?P<value>[^,]+)\s*,
    \s*(?P<code>[^,]+)\s*,
    \s*(?P<flags>[^,]+)\s*,
    \s*(?P<default_value>[^}]+)},?\s* 
''', re.X)


RXP_RESTABLE = re.compile(r'''
    ^(?:static\s+)?RES_TABLE\s+
    (?P<name>[^[]+)\[\]\s+=\s+\{\s*\n # First line with starting brace
    (?P<items>.*?)                    # Non greedy search
    \n}\s*;
''', re.M|re.X|re.S)


RXP_RESTABLE_ITEM = re.compile(r'''
    ^\s+{"(?P<name>[^"]+)"\s*,
    \s*(?P<resitem>[^,]+)\s*,
    \s*(?P<rcode>[^}]+)},?\s*
''', re.X)



RXP_STRUCT_TABLE = re.compile(r'''
    ^(?:static\s+?)?struct
    \ss_[a-zA-Z_]+\s(?P<name>[^[^{]+)\[\]\s+=\s+\{\s*\n # First line with starting brace
    (?P<items>.*?)                    # Non greedy search
    \n}\s*;
''', re.M|re.X|re.S)

RXP_STRUCT_ITEM = re.compile(r'''
    ^\s+{"(?P<name>[^"]+)"\s*,
    \s*(?P<resitem>[^,]+)\s*(,
    \s*(?P<rcode>[^}]+))?},?\s*(/\*.*\*/)?
''', re.X)




# handler to python type wrapping
CONF2TYPE = {
    'store_bool'    : bool,
    'store_pint32'  : int,
    'store_str'     : str,
    'store_name'    : str,
    #'store_dir'     : Path,
    }

CONF2VALUE = {
    'INT32_MAX'     : 0x7fffffffL,
    'true'          : True,
    'false'         : False,
    'REPLACE_ALWAYS': 0 ,
    #0             : '0' ,
    #'0'           : '0',
    0             : None ,
    '0'           : None ,
    
    '60*60*24*31*12*5' : '160704000',
    '60*60*24*60' : '5184000',
    '60*60*24*180': '15552000',
    '60*60*24*365': '31536000',
    '3 * 60'      : '180',
    '30 * 60'     : '1800',
    '60 * 30'     : '1800', 
    '5 * 60'      : '300',  
    }




# Read in Commands Info

RXP_COMMAND_ENTRY = re.compile(r'''
^\s+{\s+NT_\("(?P<cmdtext>[^"]+)"\),
\s*(?P<cmd>[^"]+),
\s*_\("(?P<usage>[^"]*)"\),
\s+NT_\("(?P<help>[^)]*)"\),
\s*(?P<inrunscript>\w+)} 
''', re.X|re.M|re.S)


RXP_DOT_COMMAND_ENTRY = re.compile(r'''
^\s+{\s+NT_\("(?P<cmdtext>[^"]+)"\),
\s*(?P<cmd>[^"]+),
\s*(?P<help>\w*),
\s*(?P<inrunscript>\w+)}
''', re.X|re.M|re.S)






BOOL_FILESET_OPTIONS = set([
'portable' ,
'ignorecase' ,
'honornodumpflag',
'exclude'  ,
'enhancedwild' ,
'hardlinks'  ,
'onefs'  ,
'xattrsupport'  ,
'hfsplussupport' ,
'aclsupport'  ,
'checkfilechanges'  ,
'keepatime'  ,
'readfifo'  ,
'recurse'  ,
'sparse'  ,
'mtimeonly'  ,
'noatime'  ,
])

def defvalue(type, name, value):
  if type == 'store_opts':
    if name in BOOL_FILESET_OPTIONS:
      if value == 0:
        defval =  False
      else:
        defval = True
    else: 
      defval = value
      
  elif type == 'store_bool' or type == 'store_bit':
    if value == 0:
      defval =  False
    else:
      defval = True    
  else:
    defval = CONF2VALUE.get(value,value)
  return defval



store_classes = []

def parse_conf_c(filename):
    """parse a bacula c file for resoure item definitions"""
    cfg = {}
    cf = open(filename).read()
    for block in RXP_RESOURCE.finditer(cf):
        bl = cfg[block.group("name")] = {}
        for line in block.group("items").split('\n'):
            m = RXP_ITEM.search(line)
            if not m: continue
            item = m.groupdict()
            bl[item.pop("name")] = item
    return cfg


def parse_conf_c_for_res_table(filename):
    """parse a bacula c file for resoure table definitions"""
    cfg = {}
    cf = open(filename).read()
    for block in RXP_RESTABLE.finditer(cf):
        bl = cfg[block.group("name")] = {}
        for line in block.group("items").split('\n'):
            m = RXP_RESTABLE_ITEM.search(line)
            if not m: continue
            item = m.groupdict()
            bl[item.pop("name")] = item
    return cfg



def directives(daemon, conf):
    res = conf.keys()
    res.sort()
    for r in res:
        s = "%s_%s = [\n" % (daemon, r)
        #s = ' '
        keys = sorted(conf[r].iterkeys())
        try:      # put name first
          keys.remove('name')
          keys.insert(0,'name')
        except:
          pass
        for i in keys:
        #for i, v in conf[r].iteritems():
            name = i
            v = conf[r][i]
            type = v["handler"]        
            required = v["flags"] == "ITEM_REQUIRED"
            default = v["flags"] == "ITEM_DEFAULT"
            defaultvalue = defvalue(type, name,  v["default_value"])
            #defaultvalue = CONF2VALUE.get(v["default_value"],v["default_value"])
            if default: 
              value = defaultvalue
            else:
              value = defvalue(type, name, None)
            s += "  %s,\n" % repr(Item(name, value, defaultvalue, default, type, required))
        s += "]\n"
        print s
 
        
def ressources(daemon, conf):
    res = conf.keys()
    res.sort()
    for r in res:
        s = "# This is the master resource definition. \n# It must have one item for each of the resources.\n"
        s += "%s_%s = [\n" % (daemon, r)
        for i, v in conf[r].iteritems():
            name = i
            rcode = v["rcode"]
            resitem = v["resitem"]
            #print resitem
            if resitem == 'NULL': continue
            if resitem == 'msgs_items': # Message ressource is defined in lib file
              s += "  [ \'%s\' , \'%s\', %s_%s ], \n" % (name, rcode, 'lib', resitem)
            else:
              s += "  [ \'%s\' , \'%s\', %s_%s ], \n" % (name, rcode, daemon, resitem)
        s += "]\n"
        print s     




def parse_conf_c_for_structs(filename):
    """parse a bacula c file for resoure table definitions"""
    cfg = {}
    cf = open(filename).read()
    for block in RXP_STRUCT_TABLE.finditer(cf):
        if block.group("name") != "FS_options" and block.group("name") != "FS_option_kw":
          bl = cfg[block.group("name")] = {}
          for line in block.group("items").split('\n'):
            m = RXP_STRUCT_ITEM.search(line)
            if not m: continue
            item = m.groupdict()
            bl[item.pop("name")] = item
            
        elif block.group("name") == "FS_options":
          bl = cfg[block.group("name")] = {} #  keywords
          for line in block.group("items").split('\n'):
            m = RXP_STRUCT_ITEM.search(line)
            if not m: continue
            item = m.groupdict()
            if not item["resitem"] in bl:
              #print "creating", item["resitem"], item 
              bl[item["resitem"]] = []
            #else:
            #  print "not creating" , item["resitem"] 
            #bl[item["resitem"]].append(item["name"])
            #item.pop('rcode')
            bl[item["resitem"]].append(PrettyNames[item.pop('name')])
            
            #print " appended to bl[" ,  item["resitem"] , "]" , item
        
        elif block.group("name") == "FS_option_kw":
          bl = cfg[block.group("name")] = {} #  keywords
          for line in block.group("items").split('\n'):
            m = RXP_STRUCT_ITEM.search(line)
            if not m: continue
            item = m.groupdict()
            if not item["name"] in bl:
              #print "adding", item["resitem"], item["name"]
              bl[item["name"]] = []
            #else:
            #  print "not adding" , item["resitem"] 
            #bl[item["resitem"]].append(item["name"])
            bl[item["name"]] = item.pop('resitem')
            #print " appending  to bl[" ,  item["resitem"] , "]"
    #print cfg      
    return cfg

#def structs(daemon, conf):
  

    
    
if __name__ == "__main__":
    try:
        bacula_src = sys.argv[1]
        #bacula_src = '/home/pstorz/bacula_git/trunk/bacula/src/'
    except:
        raise(__doc__)

    rsc = []

    print '''#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# auto_types.py
#
# This file is autogenerated from the bacula sources.
# 
from auto_types import *
from command import *

    '''
    
    
    # for parse_conf_c_for_structs(bacula_src + 'dird/dird_conf.c')
    conf = parse_conf_c_for_structs(bacula_src + 'lib/parse_conf.c')
    for r in conf.keys():
      print r + ' = [' ,
      #print r, conf[r]
      for i, v in conf[r].iteritems():
        if str(i) != ' ':
          print "'" + i.lower() + "'" +', ',
          
      print ']'
    
    # for parse_conf_c_for_structs(bacula_src + 'dird/dird_conf.c')
    conf = parse_conf_c_for_structs(bacula_src + 'dird/dird_conf.c')
    for r in conf.keys():
      print r + ' = [' ,
      #print r, conf[r]
      for i, v in conf[r].iteritems():
        if str(i) != ' ':
          print "'" + prettynames.prettyName(i.lower()) + "'" +', ',
          #print "'" + i.lower() + "'" +', ',
      print ']'
    #print 'READY1'
    
    
    
    
    conf = parse_conf_c_for_structs(bacula_src + 'dird/inc_conf.c')
    #print conf
    
    for r in conf.keys():
      if r != 'FS_options' and r != 'FS_option_kw':  
        #print 'R:' + r  + ':R'
        print r + ' = [' ,
        #print r,type( conf[r] )
        #if type(conf[r]) == 'type <dict>':
        for i, v in conf[r].iteritems():
          if type(v) == 'type <str>':
            print "'" + i.lower() + "'" +', ',
          else:
            print '[' , i,'=',v, ']'
          #print "i,v:",i,v
        print ']\n'
        
      elif r == "FS_option_kw":
        print r + ' = {' ,
        for i, v in conf[r].iteritems():
          #if type(v) == 'type <str>':
          #  print "'" + i.lower() + "'" +', ',
          #else:
            #print '[' , i,'=',v, ']'
            
            #print conf['FS_options']
            try:
              options = conf['FS_options'][v]
              #print conf['FS_options'][v]
            except:
              options =  ['Yes', 'No']
              #print "excepting on "
            print  "'" + i + "'" , ':',  options, ','
          #print "i,v:",i,v
        print '}\n'
        
        
      #else:
      #  for i in conf[r]:
      #    print i
      #  print ']\n'
          
    #print 'READY2'    
    

    commands = {}
    
    filename = bacula_src+"/dird/ua_cmds.c"

    cf = open(filename).read()
    for entry in RXP_COMMAND_ENTRY.finditer(cf):
      #print entry.groups()
      commands[entry.group("cmdtext")]=    command(
                          entry.group("cmdtext"),
                          entry.group("cmd"),
                          entry.group("usage"),
                          entry.group("help").replace('"','').replace('\n',''),
                          entry.group("inrunscript"),
                          )
 
    filename = bacula_src+"/dird/ua_dotcmds.c"
  
    cf = open(filename).read()
    for entry in RXP_DOT_COMMAND_ENTRY.finditer(cf):
  #print entry.groups()
      commands[entry.group("cmdtext")]= command(entry.group("cmdtext"),
                          entry.group("cmd"),
                          '',
                          entry.group("help").replace('"','').replace('\n',''),
                          entry.group("inrunscript"),
                          )
    print "commands = ",
    pprint.pprint(commands)
    

    
    
    print '''
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# auto_configrules.py
#
# This file is autogenerated from the bacula sources.
# 
from auto_types import *
from config_classes import *


    '''
       
    
    inc = False

    for daemon, conf in C_CONFS:
        if daemon == 'inc':
          inc = True
          daemon = 'dird'
        filename = os.path.join(bacula_src, daemon, conf)
        if inc:
          daemon = 'inc'
        directives(daemon, parse_conf_c(filename))
 
        
    inc = False   
    for daemon, conf in C_CONFS:
        if daemon == 'inc':
          inc = True
          daemon = 'dird'
        filename = os.path.join(bacula_src, daemon, conf)
        if inc:
          daemon = 'inc'
        ressources(daemon, parse_conf_c_for_res_table(filename))
 
    print ''' 
# manual configurations
  
dir_addresses_items = [ 
  # no items, just a container
]

cli_addresses_items = [
  # no items, just a container 
]

store_addresses_items = [
  # no items, just a container 
]


addresses_ip_items = [ 
  Item('port', 9102, 9102, True, 'store_str', False),
  Item('addr', 0, 0, False, 'store_str', False),
]

#fs_include_items = [
#  Item('file', 0, 0, False, 'store_dir', False),                
#]

fs_include_items = inc_newinc_items
        '''


