import config.base

class Configure(config.base.Configure):
  def __init__(self, framework, functions = []):
    config.base.Configure.__init__(self, framework)
    self.headerPrefix = ''
    self.substPrefix  = ''
    self.functions    = functions
    return

  def getDefineName(self, funcName):
    return 'HAVE_'+funcName.upper()

  def setupHelp(self, help):
    import nargs
    help.addArgument('Functions', '-with-memcmp-ok=<0 or 1>', nargs.ArgBool(None, 0, 'Does memcmp() work correctly?'))
    help.addArgument('Functions', '-with-bad-memcmp=<0 or 1>', nargs.ArgBool(None, 0, 'Flag set by the batch check to indicate a faulty memcmp()'))
    return

  def setupDependencies(self, framework):
    self.headers = self.framework.require('config.headers', self)
    return

  def haveFunction(self, function):
    return self.getDefineName(function) in self.defines

  def check(self, funcName, libraries = None):
    '''Checks for the function "funcName", and if found defines HAVE_"funcName"'''
    self.framework.log.write('Checking for function '+funcName+'\n')
    # Don't include <ctype.h> because on OSF/1 3.0 it includes <sys/types.h>
    # which includes <sys/select.h> which contains a prototype for
    # select.  Similarly for bzero.
    includes  = '/* System header to define __stub macros and hopefully few prototypes, which can conflict with char '+funcName+'(); below. */\n'
    includes += '''
    #include <assert.h>
    /* Override any gcc2 internal prototype to avoid an error. */
    '''
    if self.language[-1] == 'C++':
      includes += '''
      #ifdef __cplusplus
      extern "C"
      #endif'''
    includes += '''
    /* We use char because int might match the return type of a gcc2
    builtin and then its argument prototype would still apply. */
    '''
    includes += 'char '+funcName+'();\n'
    body = '''
    /* The GNU C library defines this for functions which it implements
    to always fail with ENOSYS.  Some functions are actually named
    something starting with __ and the normal name is an alias.  */
    #if defined (__stub_'''+funcName+''') || defined (__stub___'''+funcName+''')
    choke me
    #else
    '''+funcName+'''();
    #endif
    '''
    if libraries:
      oldLibs = self.framework.argDB['LIBS']
      if not isinstance(libraries, list):
        libraries = [libraries]
      for library in libraries:
        if not library.strip()[0] == '-':
          self.framework.argDB['LIBS'] += ' -l'+library
        else:
          self.framework.argDB['LIBS'] += ' '+library
    found = self.checkLink(includes, body)
    if libraries:
      self.framework.argDB['LIBS'] = oldLibs
    if found:
      self.addDefine(self.getDefineName(funcName), 1)
    return found

  def checkMemcmp(self):
    '''Check for 8-bit clean memcmp'''
    if self.framework.argDB['with-memcmp-ok']:
      return
    if not self.framework.argDB['with-batch']:
      if not self.checkRun('#include <string.h>\nvoid exit(int);\n\n', 'char c0 = 0x40;\nchar c1 = (char) 0x80;\nchar c2 = (char) 0x81;\nexit(memcmp(&c0, &c2, 1) < 0 && memcmp(&c1, &c2, 1) < 0 ? 0 : 1);\n'):
        raise RuntimeError('Failed to find 8-bit clean memcmp()')
    else:
      self.framework.addBatchInclude('#include <string.h>')
      self.framework.addBatchBody(['{',
                                   '  char c0 = 0x40;',
                                   '  char c1 = (char) 0x80;',
                                   '  char c2 = (char) 0x81;',
                                   '  if (memcmp(&c0, &c2, 1) < 0 && memcmp(&c1, &c2, 1) < 0 ? 0 : 1) {',
                                   '    fprintf(output, " \'--with-bad-memcmp\',");',
                                   '  } else {',
                                   '    fprintf(output, " \'--with-memcmp-ok\',");',
                                   '  }',
                                   '}'])
    return

  def checkSysinfo(self):
    '''Check whether sysinfo takes three arguments, and if it does define HAVE_SYSINFO_3ARG'''
    self.check('sysinfo')
    if self.defines.has_key(self.getDefineName('sysinfo')):
      map(self.headers.check, ['linux/kernel.h', 'sys/sysinfo.h', 'sys/systeminfo.h'])
      includes = '''
      #ifdef HAVE_LINUX_KERNEL_H
      #  include <linux/kernel.h>
      #  include <linux/sys.h>
      #  ifdef HAVE_SYS_SYSINFO_H
      #    include <sys/sysinfo.h>
      #  endif
      #elif defined(HAVE_SYS_SYSTEMINFO_H)
      #  include <sys/systeminfo.h>
      #else
      #  error "Cannot check sysinfo without special headers"
      #endif
      '''
      body = 'char buf[10]; long count=10; sysinfo(1, buf, count);\n'
      if self.checkCompile(includes, body):
        self.addDefine('HAVE_SYSINFO_3ARG', 1)
    return

  def checkVPrintf(self):
    self.check('vprintf')
    '''Checks whether vprintf requires a char * last argument, and if it does defines HAVE_VPRINTF_CHAR'''
    if not self.checkCompile('#include <stdio.h>\n#include <stdarg.h>\n', 'va_list Argp;\nvprintf( "%d", Argp );\n'):
      self.addDefine('HAVE_VPRINTF_CHAR', 1)
    return

  def checkSignalHandlerType(self):
    '''Checks the type of C++ signals handlers, and defines SIGNAL_CAST to the correct value'''
    self.pushLanguage('C++')
    if not self.checkCompile('#include <signal.h>\nstatic void myhandler(int sig) {}\n', 'signal(SIGFPE,myhandler);\n'):
      self.addDefine('SIGNAL_CAST', '(void (*)(int))')
    else:
      self.addDefine('SIGNAL_CAST', ' ')
    self.popLanguage()
    return

  def checkFreeReturnType(self):
    '''Checks whether free returns void or int, and defines HAVE_FREE_RETURN_INT'''
    if self.checkCompile('#include <stdlib.h>\n', 'int ierr; void *p; ierr = free(p); return 0;\n'):
      self.addDefine('HAVE_FREE_RETURN_INT', 1)
    return

  def configure(self):
    self.executeTest(self.checkMemcmp)
    self.executeTest(self.checkSysinfo)
    self.executeTest(self.checkVPrintf)
    if 'CXX' in self.framework.argDB:
      self.executeTest(self.checkSignalHandlerType)
    self.executeTest(self.checkFreeReturnType)
    map(lambda function: self.executeTest(self.check, function), self.functions)
    return
