text:
#!/usr/bin/env python2
#
#  Process Duktape option metadata and produce various useful outputs:
#
#    - duk_config.h with specific or autodetected platform, compiler, and
#      architecture; forced options; sanity checks; etc
#    - option documentation for Duktape 1.x feature options (DUK_OPT_xxx)
#    - option documentation for Duktape 1.x/2.x config options (DUK_USE_xxx)
#
#  Genconfig tries to build all outputs based on modular metadata, so that
#  managing a large number of config options (which is hard to avoid given
#  the wide range of targets Duktape supports) remains maintainable.
#
#  Genconfig does *not* try to support all exotic platforms out there.
#  Instead, the goal is to allow the metadata to be extended, or to provide
#  a reasonable starting point for manual duk_config.h tweaking.
#
#  For Duktape 1.3 release the main goal was to autogenerate a Duktape 1.2
#  compatible "autodetect" header from legacy snippets, with other outputs
#  being experimental.  For Duktape 1.4 duk_config.h is always created from
#  modular sources.
#

import os
import sys
import re
import json
import yaml
import optparse
import tarfile
import tempfile
import atexit
import shutil
try:
	from StringIO import StringIO
except ImportError:
	from io import StringIO

#
#  Globals holding scanned metadata, helper snippets, etc
#

# Metadata to scan from config files.
use_defs = None
use_defs_list = None
opt_defs = None
opt_defs_list = None
use_tags = None
use_tags_list = None
tags_meta = None
required_use_meta_keys = [
	'define',
	'introduced',
	'default',
	'tags',
	'description'
]
allowed_use_meta_keys = [
	'define',
	'feature_enables',
	'feature_disables',
	'feature_snippet',
	'feature_no_default',
	'related_feature_defines',
	'introduced',
	'deprecated',
	'removed',
	'unused',
	'requires',
	'conflicts',
	'related',
	'default',
	'tags',
	'description',
]
required_opt_meta_keys = [
	'define',
	'introduced',
	'tags',
	'description'
]
allowed_opt_meta_keys = [
	'define',
	'introduced',
	'deprecated',
	'removed',
	'unused',
	'requires',
	'conflicts',
	'related',
	'tags',
	'description'
]

# Preferred tag order for option documentation.
doc_tag_order = [
	'portability',
	'memory',
	'lowmemory',
	'ecmascript',
	'execution',
	'debugger',
	'debug',
	'development'
]

# Preferred tag order for generated C header files.
header_tag_order = doc_tag_order

# Helper headers snippets.
helper_snippets = None

# Assume these provides come from outside.
assumed_provides = {
	'DUK_SINGLE_FILE': True,         # compiling Duktape from a single source file (duktape.c) version
	'DUK_COMPILING_DUKTAPE': True,   # compiling Duktape (not user application)
	'DUK_CONFIG_H_INCLUDED': True,   # artifact, include guard
}

# Platform files must provide at least these (additional checks
# in validate_platform_file()).  Fill-ins provide missing optionals.
platform_required_provides = [
	'DUK_USE_OS_STRING'  # must be #define'd
]

# Architecture files must provide at least these (additional checks
# in validate_architecture_file()).  Fill-ins provide missing optionals.
architecture_required_provides = [
	'DUK_USE_ARCH_STRING'
]

# Compiler files must provide at least these (additional checks
# in validate_compiler_file()).  Fill-ins provide missing optionals.
compiler_required_provides = [
	# Compilers need a lot of defines; missing defines are automatically
	# filled in with defaults (which are mostly compiler independent), so
	# the requires define list is not very large.

	'DUK_USE_COMPILER_STRING',    # must be #define'd
	'DUK_USE_BRANCH_HINTS',       # may be #undef'd, as long as provided
	'DUK_USE_VARIADIC_MACROS',    # may be #undef'd, as long as provided
	'DUK_USE_UNION_INITIALIZERS'  # may be #undef'd, as long as provided
]

#
#  Miscellaneous helpers
#

def get_auto_delete_tempdir():
	tmpdir = tempfile.mkdtemp(suffix='-genconfig')
	def _f(dirname):
		#print('Deleting temporary directory: %r' % dirname)
		if os.path.isdir(dirname) and '-genconfig' in dirname:
			shutil.rmtree(dirname)
	atexit.register(_f, tmpdir)
	return tmpdir

def strip_comments_from_lines(lines):
	# Not exact but close enough.  Doesn't handle string literals etc,
	# but these are not a concrete issue for scanning preprocessor
	# #define references.
	#
	# Comment contents are stripped of any DUK_ prefixed text to avoid
	# incorrect requires/provides detection.  Other comment text is kept;
	# in particular a "/* redefine */" comment must remain intact here.
	# (The 'redefine' hack is not actively needed now.)
	#
	# Avoid Python 2.6 vs. Python 2.7 argument differences.

	def censor(x):
		return re.sub(re.compile('DUK_\w+', re.MULTILINE), 'xxx', x.group(0))

	tmp = '\n'.join(lines)
	tmp = re.sub(re.compile('/\*.*?\*/', re.MULTILINE | re.DOTALL), censor, tmp)
	tmp = re.sub(re.compile('//.*?$', re.MULTILINE), censor, tmp)
	return tmp.split('\n')

# Header snippet representation: lines, provides defines, requires defines.
re_line_provides = re.compile(r'^#(?:define|undef)\s+(\w+).*$')
re_line_requires = re.compile(r'(DUK_[A-Z0-9_]+)')  # uppercase only, don't match DUK_USE_xxx for example
class Snippet:
	lines = None     # lines of text and/or snippets
	provides = None  # map from define to 'True' for now
	requires = None  # map from define to 'True' for now

	def __init__(self, lines, provides=None, requires=None, autoscan_requires=True, autoscan_provides=True):
		self.lines = []
		if not isinstance(lines, list):
			raise Exception('Snippet constructor must be a list (not e.g. a string): %s' % repr(lines))
		for line in lines:
			if isinstance(line, str):
				self.lines.append(line)
			elif isinstance(line, unicode):
				self.lines.append(line.encode('utf-8'))
			else:
				raise Exception('invalid line: %r' % line)
		self.provides = {}
		if provides is not None:
			for k in provides.keys():
				self.provides[k] = True
		self.requires = {}
		if requires is not None:
			for k in requires.keys():
				self.requires[k] = True

		stripped_lines = strip_comments_from_lines(lines)
		# for line in stripped_lines: print(line)

		for line in stripped_lines:
			# Careful with order, snippet may self-reference its own
			# defines in which case there's no outward dependency.
			# (This is not 100% because the order of require/provide
			# matters and this is not handled now.)
			#
			# Also, some snippets may #undef/#define another define but
			# they don't "provide" the define as such.  Such redefinitions
			# are marked "/* redefine */" in the snippets.  They're best
			# avoided (and not currently needed in Duktape 1.4.0).

			if autoscan_provides:
				m = re_line_provides.match(line)
				if m is not None and '/* redefine */' not in line and \
					len(m.group(1)) > 0 and m.group(1)[-1] != '_':
					# Don't allow e.g. DUK_USE_ which results from matching DUK_USE_xxx
					#print('PROVIDES: %r' % m.group(1))
					self.provides[m.group(1)] = True
			if autoscan_requires:
				matches = re.findall(re_line_requires, line)
				for m in matches:
					if len(m) > 0 and m[-1] == '_':
						# Don't allow e.g. DUK_USE_ which results from matching DUK_USE_xxx
						pass
					elif m[:7] == 'DUK_OPT':
						# DUK_OPT_xxx always come from outside
						pass
					elif m[:7] == 'DUK_USE':
						# DUK_USE_xxx are internal and they should not be 'requirements'
						pass
					elif self.provides.has_key(m):
						# Snippet provides it's own require; omit
						pass
					else:
						#print('REQUIRES: %r' % m)
						self.requires[m] = True

	def fromFile(cls, filename):
		lines = []
		with open(filename, 'rb') as f:
			for line in f:
				if line[-1] == '\n':
					line = line[:-1]
				if line[:8] == '#snippet':
					m = re.match(r'#snippet\s+"(.*?)"', line)
					# XXX: better plumbing for lookup path
					sub_fn = os.path.normpath(os.path.join(filename, '..', '..', 'header-snippets', m.group(1)))
					#print('#snippet ' + sub_fn)
					sn = Snippet.fromFile(sub_fn)
					lines += sn.lines
				else:
					lines.append(line)
		return Snippet(lines, autoscan_requires=True, autoscan_provides=True)
	fromFile = classmethod(fromFile)

	def merge(cls, snippets):
		ret = Snippet([], [], [])
		for s in snippets:
			ret.lines += s.lines
			for k in s.provides.keys():
				ret.provides[k] = True
			for k in s.requires.keys():
				ret.requires[k] = True
		return ret
	merge = classmethod(merge)

# Helper for building a text file from individual lines, injected files, etc.
# Inserted values are converted to Snippets so that their provides/requires
# information can be tracked.  When non-C outputs are created, these will be
# bogus but ignored.
class FileBuilder:
	vals = None  # snippet list
	base_dir = None
	use_cpp_warning = False

	def __init__(self, base_dir=None, use_cpp_warning=False):
		self.vals = []
		self.base_dir = base_dir
		self.use_cpp_warning = use_cpp_warning

	def line(self, line):
		self.vals.append(Snippet([ line ]))

	def lines(self, lines):
		if len(lines) > 0 and lines[-1] == '\n':
			lines = lines[:-1]  # strip last newline to avoid empty line
		self.vals.append(Snippet(lines.split('\n')))

	def empty(self):
		self.vals.append(Snippet([ '' ]))

	def rst_heading(self, title, char, doubled=False):
		tmp = []
		if doubled:
			tmp.append(char * len(title))
		tmp.append(title)
		tmp.append(char * len(title))
		self.vals.append(Snippet(tmp))

	def snippet_relative(self, fn):
		sn = Snippet.fromFile(os.path.join(self.base_dir, fn))
		self.vals.append(sn)
		return sn

	def snippet_absolute(self, fn):
		sn = Snippet.fromFile(fn)
		self.vals.append(sn)
		return sn

	def cpp_error(self, msg):
		# XXX: assume no newlines etc
		self.vals.append(Snippet([ '#error %s' % msg ]))

	def cpp_warning(self, msg):
		# XXX: assume no newlines etc
		# XXX: support compiler specific warning mechanisms
		if self.use_cpp_warning:
			# C preprocessor '#warning' is often supported
			self.vals.append(Snippet([ '#warning %s' % msg ]))
		else:
			self.vals.append(Snippet([ '/* WARNING: %s */' % msg ]))

	def cpp_warning_or_error(self, msg, is_error=True):
		if is_error:
			self.cpp_error(msg)
		else:
			self.cpp_warning(msg)

	def chdr_comment_line(self, msg):
		self.vals.append(Snippet([ '/* %s */' % msg ]))

	def chdr_block_heading(self, msg):
		lines = []
		lines.append('')
		lines.append('/*')
		lines.append(' *  ' + msg)
		lines.append(' */')
		lines.append('')
		self.vals.append(Snippet(lines))

	def join(self):
		tmp = []
		for line in self.vals:
			if not isinstance(line, object):
				raise Exception('self.vals must be all snippets')
			for x in line.lines:  # x is a Snippet
				tmp.append(x)
		return '\n'.join(tmp)

	def fill_dependencies_for_snippets(self, idx_deps):
		fill_dependencies_for_snippets(self.vals, idx_deps)

# Insert missing define dependencies into index 'idx_deps' repeatedly
# until no unsatisfied dependencies exist.  This is used to pull in
# the required DUK_F_xxx helper defines without pulling them all in.
# The resolution mechanism also ensures dependencies are pulled in the
# correct order, i.e. DUK_F_xxx helpers may depend on each other (as
# long as there are no circular dependencies).
#
# XXX: this can be simplified a lot
def fill_dependencies_for_snippets(snippets, idx_deps):
	# graph[A] = [ B, ... ] <-> B, ... provide something A requires.
	graph = {}
	snlist = []
	resolved = []   # for printing only

	def add(sn):
		if sn in snlist:
			return  # already present
		snlist.append(sn)

		to_add = []

		for k in sn.requires.keys():
			if assumed_provides.has_key(k):
				continue

			found = False
			for sn2 in snlist:
				if sn2.provides.has_key(k):
					if not graph.has_key(sn):
						graph[sn] = []
					graph[sn].append(sn2)
					found = True  # at least one other node provides 'k'

			if not found:
				#print('Resolving %r' % k)
				resolved.append(k)

				# Find a header snippet which provides the missing define.
				# Some DUK_F_xxx files provide multiple defines, so we don't
				# necessarily know the snippet filename here.

				sn_req = None
				for sn2 in helper_snippets:
					if sn2.provides.has_key(k):
						sn_req = sn2
						break
				if sn_req is None:
					print(repr(sn.lines))
					raise Exception('cannot resolve missing require: %r' % k)

				# Snippet may have further unresolved provides; add recursively
				to_add.append(sn_req)

				if not graph.has_key(sn):
					graph[sn] = []
				graph[sn].append(sn_req)

		for sn in to_add:
			add(sn)

	# Add original snippets.  This fills in the required nodes
	# recursively.
	for sn in snippets:
		add(sn)

	# Figure out fill-ins by looking for snippets not in original
	# list and without any unserialized dependent nodes.
	handled = {}
	for sn in snippets:
		handled[sn] = True
	keepgoing = True
	while keepgoing:
		keepgoing = False
		for sn in snlist:
			if handled.has_key(sn):
				continue

			success = True
			for dep in graph.get(sn, []):
				if not handled.has_key(dep):
					success = False
			if success:
				snippets.insert(idx_deps, sn)
				idx_deps += 1
				snippets.insert(idx_deps, Snippet([ '' ]))
				idx_deps += 1
				handled[sn] = True
				keepgoing = True
				break

	# XXX: detect and handle loops cleanly
	for sn in snlist:
		if handled.has_key(sn):
			continue
		print('UNHANDLED KEY')
		print('PROVIDES: %r' % sn.provides)
		print('REQUIRES: %r' % sn.requires)
		print('\n'.join(sn.lines))

#	print(repr(graph))
#	print(repr(snlist))
#	print('Resolved helper defines: %r' % resolved)
	print('Resolved %d helper defines' % len(resolved))

def serialize_snippet_list(snippets):
	ret = []

	emitted_provides = {}
	for k in assumed_provides.keys():
		emitted_provides[k] = True

	for sn in snippets:
		ret += sn.lines
		for k in sn.provides.keys():
			emitted_provides[k] = True
		for k in sn.requires.keys():
			if not emitted_provides.has_key(k):
				# XXX: conditional warning, happens in some normal cases
				#print('WARNING: define %r required, not provided so far' % k)
				pass

	return '\n'.join(ret)

def remove_duplicate_newlines(x):
	ret = []
	empty = False
	for line in x.split('\n'):
		if line == '':
			if empty:
				pass
			else:
				ret.append(line)
			empty = True
		else:
			empty = False
			ret.append(line)
	return '\n'.join(ret)

def scan_use_defs(dirname):
	global use_defs, use_defs_list
	use_defs = {}
	use_defs_list = []

	for fn in os.listdir(dirname):
		root, ext = os.path.splitext(fn)
		if not root.startswith('DUK_USE_') or ext != '.yaml':
			continue
		with open(os.path.join(dirname, fn), 'rb') as f:
			doc = yaml.load(f)
			if doc.get('example', False):
				continue
			if doc.get('unimplemented', False):
				print('WARNING: unimplemented: %s' % fn)
				continue
			dockeys = doc.keys()
			for k in dockeys:
				if not k in allowed_use_meta_keys:
					print('WARNING: unknown key %s in metadata file %s' % (k, fn))
			for k in required_use_meta_keys:
				if not k in dockeys:
					print('WARNING: missing key %s in metadata file %s' % (k, fn))

			use_defs[doc['define']] = doc

	keys = use_defs.keys()
	keys.sort()
	for k in keys:
		use_defs_list.append(use_defs[k])

def scan_opt_defs(dirname):
	global opt_defs, opt_defs_list
	opt_defs = {}
	opt_defs_list = []

	for fn in os.listdir(dirname):
		root, ext = os.path.splitext(fn)
		if not root.startswith('DUK_OPT_') or ext != '.yaml':
			continue
		with open(os.path.join(dirname, fn), 'rb') as f:
			doc = yaml.load(f)
			if doc.get('example', False):
				continue
			if doc.get('unimplemented', False):
				print('WARNING: unimplemented: %s' % fn)
				continue
			dockeys = doc.keys()
			for k in dockeys:
				if not k in allowed_opt_meta_keys:
					print('WARNING: unknown key %s in metadata file %s' % (k, fn))
			for k in required_opt_meta_keys:
				if not k in dockeys:
					print('WARNING: missing key %s in metadata file %s' % (k, fn))

			opt_defs[doc['define']] = doc

	keys = opt_defs.keys()
	keys.sort()
	for k in keys:
		opt_defs_list.append(opt_defs[k])

def scan_use_tags():
	global use_tags, use_tags_list
	use_tags = {}

	for doc in use_defs_list:
		for tag in doc.get('tags', []):
			use_tags[tag] = True

	use_tags_list = use_tags.keys()
	use_tags_list.sort()

def scan_tags_meta(filename):
	global tags_meta

	with open(filename, 'rb') as f:
		tags_meta = yaml.load(f)

def scan_helper_snippets(dirname):  # DUK_F_xxx snippets
	global helper_snippets
	helper_snippets = []

	for fn in os.listdir(dirname):
		if (fn[0:6] != 'DUK_F_'):
			continue
		#print('Autoscanning snippet: %s' % fn)
		helper_snippets.append(Snippet.fromFile(os.path.join(dirname, fn)))

def get_opt_defs(removed=True, deprecated=True, unused=True):
	ret = []
	for doc in opt_defs_list:
		# XXX: aware of target version
		if removed == False and doc.get('removed', None) is not None:
			continue
		if deprecated == False and doc.get('deprecated', None) is not None:
			continue
		if unused == False and doc.get('unused', False) == True:
			continue
		ret.append(doc)
	return ret

def get_use_defs(removed=True, deprecated=True, unused=True):
	ret = []
	for doc in use_defs_list:
		# XXX: aware of target version
		if removed == False and doc.get('removed', None) is not None:
			continue
		if deprecated == False and doc.get('deprecated', None) is not None:
			continue
		if unused == False and doc.get('unused', False) == True:
			continue
		ret.append(doc)
	return ret

def validate_platform_file(filename):
	sn = Snippet.fromFile(filename)

	for req in platform_required_provides:
		if req not in sn.provides:
			raise Exception('Platform %s is missing %s' % (filename, req))

	# DUK_SETJMP, DUK_LONGJMP, DUK_JMPBUF_TYPE are optional, fill-in
	# provides if none defined.

def validate_architecture_file(filename):
	sn = Snippet.fromFile(filename)

	for req in architecture_required_provides:
		if req not in sn.provides:
			raise Exception('Architecture %s is missing %s' % (filename, req))

	# Byte order and alignment defines are allowed to be missing,
	# a fill-in will handle them.  This is necessary because for
	# some architecture byte order and/or alignment may vary between
	# targets and may be software configurable.

	# XXX: require automatic detection to be signaled?
	# e.g. define DUK_USE_ALIGN_BY -1
	#      define DUK_USE_BYTE_ORDER -1

def validate_compiler_file(filename):
	sn = Snippet.fromFile(filename)

	for req in compiler_required_provides:
		if req not in sn.provides:
			raise Exception('Compiler %s is missing %s' % (filename, req))

def get_tag_title(tag):
	meta = tags_meta.get(tag, None)
	if meta is None:
		return tag
	else:
		return meta.get('title', tag)

def get_tag_description(tag):
	meta = tags_meta.get(tag, None)
	if meta is None:
		return None
	else:
		return meta.get('description', None)

def get_tag_list_with_preferred_order(preferred):
	tags = []

	# Preferred tags first
	for tag in preferred:
		if tag not in tags:
			tags.append(tag)

	# Remaining tags in alphabetic order
	for tag in use_tags_list:
		if tag not in tags:
			tags.append(tag)

	#print('Effective tag order: %r' % tags)
	return tags

def rst_format(text):
	# XXX: placeholder, need to decide on markup conventions for YAML files
	ret = []
	for para in text.split('\n'):
		if para == '':
			continue
		ret.append(para)
	return '\n\n'.join(ret)

def cint_encode(x):
	if not isinstance(x, (int, long)):
		raise Exception('invalid input: %r' % x)

	# XXX: unsigned constants?
	if x > 0x7fffffff or x < -0x80000000:
		return '%dLL' % x
	elif x > 0x7fff or x < -0x8000:
		return '%dL' % x
	else:
		return '%d' % x

def cstr_encode(x):
	if isinstance(x, unicode):
		x = x.encode('utf-8')
	if not isinstance(x, str):
		raise Exception('invalid input: %r' % x)

	res = '"'
	term = False
	has_terms = False
	for c in x:
		if term:
			# Avoid ambiguous hex escapes
			res += '" "'
			term = False
			has_terms = True
		o = ord(c)
		if o < 0x20 or o > 0x7e or c in '"\\':
			res += '\\x%02x' % o
			term = True
		else:
			res += c
	res += '"'

	if has_terms:
		res = '(' + res + ')'

	return res

#
#  Autogeneration of option documentation
#

# Shared helper to generate DUK_OPT_xxx and DUK_USE_xxx documentation.
# XXX: unfinished placeholder
def generate_option_documentation(opts, opt_list=None, rst_title=None, include_default=False):
	ret = FileBuilder(use_cpp_warning=opts.use_cpp_warning)

	tags = get_tag_list_with_preferred_order(doc_tag_order)

	title = rst_title
	ret.rst_heading(title, '=', doubled=True)

	handled = {}

	for tag in tags:
		first = True

		for doc in opt_list:
			if tag != doc['tags'][0]:  # sort under primary tag
				continue
			dname = doc['define']
			desc = doc.get('description', None)

			if handled.has_key(dname):
				raise Exception('define handled twice, should not happen: %r' % dname)
			handled[dname] = True

			if first:  # emit tag heading only if there are subsections
				ret.empty()
				ret.rst_heading(get_tag_title(tag), '=')

				tag_desc = get_tag_description(tag)
				if tag_desc is not None:
					ret.empty()
					ret.line(rst_format(tag_desc))
				first = False

			ret.empty()
			ret.rst_heading(dname, '-')

			if desc is not None:
				ret.empty()
				ret.line(rst_format(desc))

			if include_default:
				ret.empty()
				ret.line('Default: ``' + str(doc['default']) + '``')  # XXX: rst or other format

	for doc in opt_list:
		dname = doc['define']
		if not handled.has_key(dname):
			raise Exception('unhandled define (maybe missing from tags list?): %r' % dname)

	ret.empty()
	return ret.join()

def generate_feature_option_documentation(opts):
	defs = get_opt_defs()
	return generate_option_documentation(opts, opt_list=defs, rst_title='Duktape feature options', include_default=False)

def generate_config_option_documentation(opts):
	defs = get_use_defs()
	return generate_option_documentation(opts, opt_list=defs, rst_title='Duktape config options', include_default=True)

#
#  Helpers for duk_config.h generation
#

def get_forced_options(opts):
	# Forced options, last occurrence wins (allows a base config file to be
	# overridden by a more specific one).
	forced_opts = {}
	for val in opts.force_options_yaml:
		doc = yaml.load(StringIO(val))
		for k in doc.keys():
			if use_defs.has_key(k):
				pass  # key is known
			else:
				print('WARNING: option override key %s not defined in metadata, ignoring' % k)
			forced_opts[k] = doc[k]  # shallow copy

	if len(forced_opts.keys()) > 0:
		print('Overrides: %s' % json.dumps(forced_opts))

	return forced_opts

# Emit a default #define / #undef for an option based on
# a config option metadata node (parsed YAML doc).
def emit_default_from_config_meta(ret, doc, forced_opts, undef_done):
	defname = doc['define']
	defval = forced_opts.get(defname, doc['default'])

	# NOTE: careful with Python equality, e.g. "0 == False" is true.
	if isinstance(defval, bool) and defval == True:
		ret.line('#define ' + defname)
	elif isinstance(defval, bool) and defval == False:
		if not undef_done:
			ret.line('#undef ' + defname)
		else:
			# Default value is false, and caller has emitted
			# an unconditional #undef, so don't emit a duplicate
			pass
	elif isinstance(defval, (int, long)):
		# integer value
		ret.line('#define ' + defname + ' ' + cint_encode(defval))
	elif isinstance(defval, (str, unicode)):
		# verbatim value
		ret.line('#define ' + defname + ' ' + defval)
	elif isinstance(defval, dict):
		if defval.has_key('verbatim'):
			# verbatim text for the entire line
			ret.line(defval['verbatim'])
		elif defval.has_key('string'):
			# C string value
			ret.line('#define ' + defname + ' ' + cstr_encode(defval['string']))
		else:
			raise Exception('unsupported value for option %s: %r' % (defname, defval))
	else:
		raise Exception('unsupported value for option %s: %r' % (defname, defval))

# Add a header snippet for detecting presence of DUK_OPT_xxx feature
# options which will be removed in Duktape 2.x.
def add_legacy_feature_option_checks(opts, ret):
	ret.chdr_block_heading('Checks for legacy feature options (DUK_OPT_xxx)')
	ret.empty()

	defs = []
	for doc in get_opt_defs():
		if doc['define'] not in defs:
			defs.append(doc['define'])
	for doc in get_opt_defs():
		for dname in doc.get('related_feature_defines', []):
			if dname not in defs:
				defs.append(dname)
	defs.sort()

	for optname in defs:
		suggested = []
		for doc in get_use_defs():
			if optname in doc.get('related_feature_defines', []):
				suggested.append(doc['define'])
		ret.line('#if defined(%s)' % optname)
		if len(suggested) > 0:
			ret.cpp_warning_or_error('unsupported legacy feature option %s used, consider options: %s' % (optname, ', '.join(suggested)), opts.sanity_strict)
		else:
			ret.cpp_warning_or_error('unsupported legacy feature option %s used' % optname, opts.sanity_strict)
		ret.line('#endif')

	ret.empty()

# Add a header snippet for checking consistency of DUK_USE_xxx config
# options, e.g. inconsistent options, invalid option values.
def add_config_option_checks(opts, ret):
	ret.chdr_block_heading('Checks for config option consistency (DUK_USE_xxx)')
	ret.empty()

	defs = []
	for doc in get_use_defs():
		if doc['define'] not in defs:
			defs.append(doc['define'])
	defs.sort()

	for optname in defs:
		doc = use_defs[optname]
		dname = doc['define']

		# XXX: more checks

		if doc.get('removed', None) is not None:
			ret.line('#if defined(%s)' % dname)
			ret.cpp_warning_or_error('unsupported config option used (option has been removed): %s' % dname, opts.sanity_strict)
			ret.line('#endif')
		elif doc.get('deprecated', None) is not None:
			ret.line('#if defined(%s)' % dname)
			ret.cpp_warning_or_error('unsupported config option used (option has been deprecated): %s' % dname, opts.sanity_strict)
			ret.line('#endif')

		for req in doc.get('requires', []):
			ret.line('#if defined(%s) && !defined(%s)' % (dname, req))
			ret.cpp_warning_or_error('config option %s requires option %s (which is missing)' % (dname, req), opts.sanity_strict)
			ret.line('#endif')

		for req in doc.get('conflicts', []):
			ret.line('#if defined(%s) && defined(%s)' % (dname, req))
			ret.cpp_warning_or_error('config option %s conflicts with option %s (which is also defined)' % (dname, req), opts.sanity_strict)
			ret.line('#endif')

	ret.empty()
	ret.snippet_relative('cpp_exception_sanity.h.in')
	ret.empty()

# Add a header snippet for providing a __OVERRIDE_DEFINES__ section.
def add_override_defines_section(opts, ret):
	ret.empty()
	ret.line('/*')
	ret.line(' *  You may add overriding #define/#undef directives below for')
	ret.line(' *  customization.  You of course cannot un-#include or un-typedef')
	ret.line(' *  anything; these require direct changes above.')
	ret.line(' */')
	ret.empty()
	ret.line('/* __OVERRIDE_DEFINES__ */')
	ret.empty()

# Add automatic DUK_OPT_XXX and DUK_OPT_NO_XXX handling for backwards
# compatibility with Duktape 1.2 and before.
def add_feature_option_handling(opts, ret, forced_opts, already_provided_keys):
	ret.chdr_block_heading('Feature option handling')

	for doc in get_use_defs(removed=False, deprecated=False, unused=False):
		# If a related feature option exists, it can be used to force
		# enable/disable the target feature.  If neither feature option
		# (DUK_OPT_xxx or DUK_OPT_NO_xxx) is given, revert to default.

		config_define = doc['define']

		feature_define = None
		feature_no_define = None
		inverted = False
		if doc.has_key('feature_enables'):
			feature_define = doc['feature_enables']
		elif doc.has_key('feature_disables'):
			feature_define = doc['feature_disables']
			inverted = True
		else:
			pass

		if feature_define is not None:
			feature_no_define = 'DUK_OPT_NO_' + feature_define[8:]
			ret.line('#if defined(%s)' % feature_define)
			if inverted:
				ret.line('#undef %s' % config_define)
			else:
				ret.line('#define %s' % config_define)
			ret.line('#elif defined(%s)' % feature_no_define)
			if inverted:
				ret.line('#define %s' % config_define)
			else:
				ret.line('#undef %s' % config_define)
			ret.line('#else')
			undef_done = False

			# For some options like DUK_OPT_PACKED_TVAL the default comes
			# from platform definition.
			if doc.get('feature_no_default', False):
				print('Skip default for option %s' % config_define)
				ret.line('/* Already provided above */')
			elif already_provided_keys.has_key(config_define):
				# This is a fallback in case config option metadata is wrong.
				print('Skip default for option %s (already provided but not flagged in metadata!)' % config_define)
				ret.line('/* Already provided above */')
			else:
				emit_default_from_config_meta(ret, doc, forced_opts, undef_done)
			ret.line('#endif')
		elif doc.has_key('feature_snippet'):
			ret.lines(doc['feature_snippet'])
		else:
			pass

		ret.empty()

	ret.empty()

# Development time helper: add DUK_ACTIVE which provides a runtime C string
# indicating what DUK_USE_xxx config options are active at run time.  This
# is useful in genconfig development so that one can e.g. diff the active
# run time options of two headers.  This is intended just for genconfig
# development and is not available in normal headers.
def add_duk_active_defines_macro(ret):
	ret.chdr_block_heading('DUK_ACTIVE_DEFINES macro (development only)')

	idx = 0
	for doc in get_use_defs():
		defname = doc['define']

		ret.line('#if defined(%s)' % defname)
		ret.line('#define DUK_ACTIVE_DEF%d " %s"' % (idx, defname))
		ret.line('#else')
		ret.line('#define DUK_ACTIVE_DEF%d ""' % idx)
		ret.line('#endif')

		idx += 1

	tmp = []
	for i in xrange(idx):
		tmp.append('DUK_ACTIVE_DEF%d' % i)

	ret.line('#define DUK_ACTIVE_DEFINES ("Active: ["' + ' '.join(tmp) + ' " ]")')

#
#  duk_config.h generation
#

# Generate a duk_config.h where platform, architecture, and compiler are
# all either autodetected or specified by user.
#
# Autodetection is based on a configured list of supported platforms,
# architectures, and compilers.  For example, platforms.yaml defines the
# supported platforms and provides a helper define (DUK_F_xxx) to use for
# detecting that platform, and names the header snippet to provide the
# platform-specific definitions.  Necessary dependencies (DUK_F_xxx) are
# automatically pulled in.
#
# Automatic "fill ins" are used for mandatory platform, architecture, and
# compiler defines which have a reasonable portable default.  This reduces
# e.g. compiler-specific define count because there are a lot compiler
# macros which have a good default.
def generate_duk_config_header(opts, meta_dir):
	ret = FileBuilder(base_dir=os.path.join(meta_dir, 'header-snippets'), \
	                  use_cpp_warning=opts.use_cpp_warning)

	forced_opts = get_forced_options(opts)

	platforms = None
	with open(os.path.join(meta_dir, 'platforms.yaml'), 'rb') as f:
		platforms = yaml.load(f)
	architectures = None
	with open(os.path.join(meta_dir, 'architectures.yaml'), 'rb') as f:
		architectures = yaml.load(f)
	compilers = None
	with open(os.path.join(meta_dir, 'compilers.yaml'), 'rb') as f:
		compilers = yaml.load(f)

	# XXX: indicate feature option support, sanity checks enabled, etc
	# in general summary of options, perhaps genconfig command line?

	ret.line('/*')
	ret.line(' *  duk_config.h configuration header generated by genconfig.py.')
	ret.line(' *')
	ret.line(' *  Git commit: %s' % opts.git_commit or 'n/a')
	ret.line(' *  Git describe: %s' % opts.git_describe or 'n/a')
	ret.line(' *  Git branch: %s' % opts.git_branch or 'n/a')
	ret.line(' *')
	if opts.platform is not None:
		ret.line(' *  Platform: ' + opts.platform)
	else:
		ret.line(' *  Supported platforms:')
		for platf in platforms['autodetect']:
			ret.line(' *      - %s' % platf.get('name', platf.get('check')))
	ret.line(' *')
	if opts.architecture is not None:
		ret.line(' *  Architecture: ' + opts.architecture)
	else:
		ret.line(' *  Supported architectures:')
		for arch in architectures['autodetect']:
			ret.line(' *      - %s' % arch.get('name', arch.get('check')))
	ret.line(' *')
	if opts.compiler is not None:
		ret.line(' *  Compiler: ' + opts.compiler)
	else:
		ret.line(' *  Supported compilers:')
		for comp in compilers['autodetect']:
			ret.line(' *      - %s' % comp.get('name', comp.get('check')))
	ret.line(' *')
	ret.line(' */')
	ret.empty()
	ret.line('#if !defined(DUK_CONFIG_H_INCLUDED)')
	ret.line('#define DUK_CONFIG_H_INCLUDED')
	ret.empty()

	ret.chdr_block_heading('Intermediate helper defines')

	# DLL build affects visibility attributes on Windows but unfortunately
	# cannot be detected automatically from preprocessor defines or such.
	# DLL build status is hidden behind DUK_F_DLL_BUILD and there are two
	# ways for that to be set:
	#
	#   - Duktape 1.3 backwards compatible DUK_OPT_DLL_BUILD
	#   - Genconfig --dll option
	ret.chdr_comment_line('DLL build detection')
	ret.line('#if defined(DUK_OPT_DLL_BUILD)')
	ret.line('#define DUK_F_DLL_BUILD')
	ret.line('#elif defined(DUK_OPT_NO_DLL_BUILD)')
	ret.line('#undef DUK_F_DLL_BUILD')
	ret.line('#else')
	if opts.dll:
		ret.line('/* configured for DLL build */')
		ret.line('#define DUK_F_DLL_BUILD')
	else:
		ret.line('/* not configured for DLL build */')
		ret.line('#undef DUK_F_DLL_BUILD')
	ret.line('#endif')
	ret.empty()

	idx_deps = len(ret.vals)  # position where to emit DUK_F_xxx dependencies

	# Feature selection, system include, Date provider
	# Most #include statements are here

	if opts.platform is not None:
		ret.chdr_block_heading('Platform: ' + opts.platform)

		ret.snippet_relative('platform_cppextras.h.in')
		ret.empty()

		# XXX: better to lookup platforms metadata
		include = 'platform_%s.h.in' % opts.platform
		abs_fn = os.path.join(meta_dir, 'platforms', include)
		validate_platform_file(abs_fn)
		ret.snippet_absolute(abs_fn)
	else:
		ret.chdr_block_heading('Platform autodetection')

		ret.snippet_relative('platform_cppextras.h.in')
		ret.empty()

		for idx, platf in enumerate(platforms['autodetect']):
			check = platf.get('check', None)
			include = platf['include']
			abs_fn = os.path.join(meta_dir, 'platforms', include)

			validate_platform_file(abs_fn)

			if idx == 0:
				ret.line('#if defined(%s)' % check)
			else:
				if check is None:
					ret.line('#else')
				else:
					ret.line('#elif defined(%s)' % check)
			ret.line('/* --- %s --- */' % platf.get('name', '???'))
			ret.snippet_absolute(abs_fn)
		ret.line('#endif  /* autodetect platform */')

	ret.empty()
	ret.snippet_relative('platform_sharedincludes.h.in')
	ret.empty()

	byteorder_provided_by_all = True  # byteorder provided by all architecture files
	alignment_provided_by_all = True  # alignment provided by all architecture files
	packedtval_provided_by_all = True # packed tval provided by all architecture files

	if opts.architecture is not None:
		ret.chdr_block_heading('Architecture: ' + opts.architecture)

		# XXX: better to lookup architectures metadata
		include = 'architecture_%s.h.in' % opts.architecture
		abs_fn = os.path.join(meta_dir, 'architectures', include)
		validate_architecture_file(abs_fn)
		sn = ret.snippet_absolute(abs_fn)
		if not sn.provides.get('DUK_USE_BYTEORDER', False):
			byteorder_provided_by_all = False
		if not sn.provides.get('DUK_USE_ALIGN_BY', False):
			alignment_provided_by_all = False
		if sn.provides.get('DUK_USE_PACKED_TVAL', False):
			ret.line('#define DUK_F_PACKED_TVAL_PROVIDED')  # signal to fillin
		else:
			packedtval_provided_by_all = False
	else:
		ret.chdr_block_heading('Architecture autodetection')

		for idx, arch in enumerate(architectures['autodetect']):
			check = arch.get('check', None)
			include = arch['include']
			abs_fn = os.path.join(meta_dir, 'architectures', include)

			validate_architecture_file(abs_fn)

			if idx == 0:
				ret.line('#if defined(%s)' % check)
			else:
				if check is None:
					ret.line('#else')
				else:
					ret.line('#elif defined(%s)' % check)
			ret.line('/* --- %s --- */' % arch.get('name', '???'))
			sn = ret.snippet_absolute(abs_fn)
			if not sn.provides.get('DUK_USE_BYTEORDER', False):
				byteorder_provided_by_all = False
			if not sn.provides.get('DUK_USE_ALIGN_BY', False):
				alignment_provided_by_all = False
			if sn.provides.get('DUK_USE_PACKED_TVAL', False):
				ret.line('#define DUK_F_PACKED_TVAL_PROVIDED')  # signal to fillin
			else:
				packedtval_provided_by_all = False
		ret.line('#endif  /* autodetect architecture */')

	ret.empty()

	if opts.compiler is not None:
		ret.chdr_block_heading('Compiler: ' + opts.compiler)

		# XXX: better to lookup compilers metadata
		include = 'compiler_%s.h.in' % opts.compiler
		abs_fn = os.path.join(meta_dir, 'compilers', include)
		validate_compiler_file(abs_fn)
		sn = ret.snippet_absolute(abs_fn)
	else:
		ret.chdr_block_heading('Compiler autodetection')

		for idx, comp in enumerate(compilers['autodetect']):
			check = comp.get('check', None)
			include = comp['include']
			abs_fn = os.path.join(meta_dir, 'compilers', include)

			validate_compiler_file(abs_fn)

			if idx == 0:
				ret.line('#if defined(%s)' % check)
			else:
				if check is None:
					ret.line('#else')
				else:
					ret.line('#elif defined(%s)' % check)
			ret.line('/* --- %s --- */' % comp.get('name', '???'))
			sn = ret.snippet_absolute(abs_fn)
		ret.line('#endif  /* autodetect compiler */')

	ret.empty()

	# DUK_F_UCLIBC is special because __UCLIBC__ is provided by an #include
	# file, so the check must happen after platform includes.  It'd be nice
	# for this to be automatic (e.g. DUK_F_UCLIBC.h.in could indicate the
	# dependency somehow).

	ret.snippet_absolute(os.path.join(meta_dir, 'helper-snippets', 'DUK_F_UCLIBC.h.in'))
	ret.empty()

	# XXX: platform/compiler could provide types; if so, need some signaling
	# defines like DUK_F_TYPEDEFS_DEFINED

	# Number types
	if opts.c99_types_only:
		ret.snippet_relative('types1.h.in')
		ret.line('/* C99 types assumed */')
		ret.snippet_relative('types_c99.h.in')
		ret.empty()
	else:
		ret.snippet_relative('types1.h.in')
		ret.line('#if defined(DUK_F_HAVE_INTTYPES)')
		ret.line('/* C99 or compatible */')
		ret.empty()
		ret.snippet_relative('types_c99.h.in')
		ret.empty()
		ret.line('#else  /* C99 types */')
		ret.empty()
		ret.snippet_relative('types_legacy.h.in')
		ret.empty()
		ret.line('#endif  /* C99 types */')
		ret.empty()
	ret.snippet_relative('types2.h.in')
	ret.empty()
	ret.snippet_relative('64bitops.h.in')
	ret.empty()

	# Platform, architecture, compiler fillins.  These are after all
	# detection so that e.g. DUK_SPRINTF() can be provided by platform
	# or compiler before trying a fill-in.

	ret.chdr_block_heading('Fill-ins for platform, architecture, and compiler')

	ret.snippet_relative('platform_fillins.h.in')
	ret.empty()
	ret.snippet_relative('architecture_fillins.h.in')
	if not byteorder_provided_by_all:
		ret.empty()
		ret.snippet_relative('byteorder_fillin.h.in')
	if not alignment_provided_by_all:
		ret.empty()
		ret.snippet_relative('alignment_fillin.h.in')
	ret.empty()
	ret.snippet_relative('compiler_fillins.h.in')
	ret.empty()
	ret.snippet_relative('inline_workaround.h.in')
	ret.empty()
	if not packedtval_provided_by_all:
		ret.empty()
		ret.snippet_relative('packed_tval_fillin.h.in')

	# Object layout
	ret.snippet_relative('object_layout.h.in')
	ret.empty()

	# Detect and reject 'fast math'
	ret.snippet_relative('reject_fast_math.h.in')
	ret.empty()

	# Automatic DUK_OPT_xxx feature option handling
	if opts.support_feature_options:
		print('Autogenerating feature option (DUK_OPT_xxx) support')
		tmp = Snippet(ret.join().split('\n'))
		add_feature_option_handling(opts, ret, forced_opts, tmp.provides)

	# Emit forced options.  If a corresponding option is already defined
	# by a snippet above, #undef it first.

	tmp = Snippet(ret.join().split('\n'))
	first_forced = True
	for doc in get_use_defs(removed=not opts.omit_removed_config_options,
	                        deprecated=not opts.omit_deprecated_config_options,
	                        unused=not opts.omit_unused_config_options):
		defname = doc['define']

		if not forced_opts.has_key(defname):
			continue

		if not doc.has_key('default'):
			raise Exception('config option %s is missing default value' % defname)

		if first_forced:
			ret.chdr_block_heading('Forced options')
			first_forced = False

		undef_done = False
		if tmp.provides.has_key(defname):
			ret.line('#undef ' + defname)
			undef_done = True

		emit_default_from_config_meta(ret, doc, forced_opts, undef_done)

	ret.empty()

	# If manually-edited snippets don't #define or #undef a certain
	# config option, emit a default value here.  This is useful to
	# fill-in for new config options not covered by manual snippets
	# (which is intentional).

	tmp = Snippet(ret.join().split('\n'))
	need = {}
	for doc in get_use_defs(removed=False):
		need[doc['define']] = True
	for k in tmp.provides.keys():
		if need.has_key(k):
			del need[k]
	need_keys = sorted(need.keys())

	if len(need_keys) > 0:
		ret.chdr_block_heading('Autogenerated defaults')

		for k in need_keys:
			#print('config option %s not covered by manual snippets, emitting default automatically' % k)
			emit_default_from_config_meta(ret, use_defs[k], {}, False)

		ret.empty()

	ret.snippet_relative('custom_header.h.in')
	ret.empty()

	if len(opts.fixup_header_lines) > 0:
		ret.chdr_block_heading('Fixups')
		for line in opts.fixup_header_lines:
			ret.line(line)
		ret.empty()

	add_override_defines_section(opts, ret)

	# Date provider snippet is after custom header and overrides, so that
	# the user may define e.g. DUK_USE_DATE_NOW_GETTIMEOFDAY in their
	# custom header.
	ret.snippet_relative('date_provider.h.in')
	ret.empty()

	ret.fill_dependencies_for_snippets(idx_deps)

	if opts.emit_legacy_feature_check:
		add_legacy_feature_option_checks(opts, ret)
	if opts.emit_config_sanity_check:
		add_config_option_checks(opts, ret)
	if opts.add_active_defines_macro:
		add_duk_active_defines_macro(ret)

	# Derived defines (DUK_USE_INTEGER_LE, etc) from DUK_USE_BYTEORDER.
	# Duktape internals currently rely on the derived defines.  This is
	# after sanity checks because the derived defines are marked removed.
	ret.snippet_relative('byteorder_derived.h.in')
	ret.empty()

	ret.line('#endif  /* DUK_CONFIG_H_INCLUDED */')
	ret.empty()  # for trailing newline
	return remove_duplicate_newlines(ret.join())

#
#  Main
#

def main():
	# Forced options from multiple sources are gathered into a shared list
	# so that the override order remains the same as on the command line.
	force_options_yaml = []
	def add_force_option_yaml(option, opt, value, parser):
		# XXX: check that YAML parses
		force_options_yaml.append(value)
	def add_force_option_file(option, opt, value, parser):
		# XXX: check that YAML parses
		with open(value, 'rb') as f:
			force_options_yaml.append(f.read())
	def add_force_option_define(option, opt, value, parser):
		tmp = value.split('=')
		if len(tmp) == 1:
			doc = { tmp[0]: True }
		elif len(tmp) == 2:
			doc = { tmp[0]: tmp[1] }
		else:
			raise Exception('invalid option value: %r' % value)
		force_options_yaml.append(yaml.safe_dump(doc))
	def add_force_option_undefine(option, opt, value, parser):
		tmp = value.split('=')
		if len(tmp) == 1:
			doc = { tmp[0]: False }
		else:
			raise Exception('invalid option value: %r' % value)
		force_options_yaml.append(yaml.safe_dump(doc))

	fixup_header_lines = []
	def add_fixup_header_line(option, opt, value, parser):
		fixup_header_lines.append(value)
	def add_fixup_header_file(option, opt, value, parser):
		with open(value, 'rb') as f:
			for line in f:
				if line[-1] == '\n':
					line = line[:-1]
				fixup_header_lines.append(line)

	commands = [
		'duk-config-header',
		'feature-documentation',
		'config-documentation'
	]
	parser = optparse.OptionParser(
		usage='Usage: %prog [options] COMMAND',
		description='Generate a duk_config.h or config option documentation based on config metadata.',
		epilog='COMMAND can be one of: ' + ', '.join(commands) + '.'
	)

	parser.add_option('--metadata', dest='metadata', default=None, help='metadata directory or metadata tar.gz file')
	parser.add_option('--output', dest='output', default=None, help='output filename for C header or RST documentation file')
	parser.add_option('--platform', dest='platform', default=None, help='platform (for "barebones-header" command)')
	parser.add_option('--compiler', dest='compiler', default=None, help='compiler (for "barebones-header" command)')
	parser.add_option('--architecture', dest='architecture', default=None, help='architecture (for "barebones-header" command)')
	parser.add_option('--c99-types-only', dest='c99_types_only', action='store_true', default=False, help='assume C99 types, no legacy type detection')
	parser.add_option('--dll', dest='dll', action='store_true', default=False, help='dll build of Duktape, affects symbol visibility macros especially on Windows')
	parser.add_option('--support-feature-options', dest='support_feature_options', action='store_true', default=False, help='support DUK_OPT_xxx feature options in duk_config.h')
	parser.add_option('--emit-legacy-feature-check', dest='emit_legacy_feature_check', action='store_true', default=False, help='emit preprocessor checks to reject legacy feature options (DUK_OPT_xxx)')
	parser.add_option('--emit-config-sanity-check', dest='emit_config_sanity_check', action='store_true', default=False, help='emit preprocessor checks for config option consistency (DUK_OPT_xxx)')
	parser.add_option('--omit-removed-config-options', dest='omit_removed_config_options', action='store_true', default=False, help='omit removed config options from generated headers')
	parser.add_option('--omit-deprecated-config-options', dest='omit_deprecated_config_options', action='store_true', default=False, help='omit deprecated config options from generated headers')
	parser.add_option('--omit-unused-config-options', dest='omit_unused_config_options', action='store_true', default=False, help='omit unused config options from generated headers')
	parser.add_option('--add-active-defines-macro', dest='add_active_defines_macro', action='store_true', default=False, help='add DUK_ACTIVE_DEFINES macro, for development only')
	parser.add_option('--define', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_define, default=force_options_yaml, help='force #define option using a C compiler like syntax, e.g. "--define DUK_USE_DEEP_C_STACK" or "--define DUK_USE_TRACEBACK_DEPTH=10"')
	parser.add_option('-D', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_define, default=force_options_yaml, help='synonym for --define, e.g. "-DDUK_USE_DEEP_C_STACK" or "-DDUK_USE_TRACEBACK_DEPTH=10"')
	parser.add_option('--undefine', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_undefine, default=force_options_yaml, help='force #undef option using a C compiler like syntax, e.g. "--undefine DUK_USE_DEEP_C_STACK"')
	parser.add_option('-U', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_undefine, default=force_options_yaml, help='synonym for --undefine, e.g. "-UDUK_USE_DEEP_C_STACK"')
	parser.add_option('--option-yaml', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_yaml, default=force_options_yaml, help='force option(s) using inline YAML (e.g. --option-yaml "DUK_USE_DEEP_C_STACK: true")')
	parser.add_option('--option-file', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_file, default=force_options_yaml, help='YAML file(s) providing config option overrides')
	parser.add_option('--fixup-file', type='string', dest='fixup_header_lines', action='callback', callback=add_fixup_header_file, default=fixup_header_lines, help='C header snippet file(s) to be appended to generated header, useful for manual option fixups')
	parser.add_option('--fixup-line', type='string', dest='fixup_header_lines', action='callback', callback=add_fixup_header_line, default=fixup_header_lines, help='C header fixup line to be appended to generated header (e.g. --fixup-line "#define DUK_USE_FASTINT")')
	parser.add_option('--sanity-warning', dest='sanity_strict', action='store_false', default=True, help='emit a warning instead of #error for option sanity check issues')
	parser.add_option('--use-cpp-warning', dest='use_cpp_warning', action='store_true', default=False, help='emit a (non-portable) #warning when appropriate')
	parser.add_option('--git-commit', dest='git_commit', default=None, help='git commit hash to be included in header comments')
	parser.add_option('--git-describe', dest='git_describe', default=None, help='git describe string to be included in header comments')
	parser.add_option('--git-branch', dest='git_branch', default=None, help='git branch string to be included in header comments')
	(opts, args) = parser.parse_args()

	meta_dir = opts.metadata
	if opts.metadata is None:
		if os.path.isfile(os.path.join('.', 'genconfig_metadata.tar.gz')):
			opts.metadata = 'genconfig_metadata.tar.gz'
		elif os.path.isdir(os.path.join('.', 'config-options')):
			opts.metadata = '.'

	if opts.metadata is not None and os.path.isdir(opts.metadata):
		meta_dir = opts.metadata
		metadata_src_text = 'Using metadata directory: %r' % meta_dir
	elif opts.metadata is not None and os.path.isfile(opts.metadata) and tarfile.is_tarfile(opts.metadata):
		meta_dir = get_auto_delete_tempdir()
		tar = tarfile.open(name=opts.metadata, mode='r:*')
		tar.extractall(path=meta_dir)
		metadata_src_text = 'Using metadata tar file %r, unpacked to directory: %r' % (opts.metadata, meta_dir)
	else:
		raise Exception('metadata source must be a directory or a tar.gz file')

	scan_helper_snippets(os.path.join(meta_dir, 'helper-snippets'))
	scan_use_defs(os.path.join(meta_dir, 'config-options'))
	scan_opt_defs(os.path.join(meta_dir, 'feature-options'))
	scan_use_tags()
	scan_tags_meta(os.path.join(meta_dir, 'tags.yaml'))
	print('%s, scanned %d DUK_OPT_xxx, %d DUK_USE_XXX, %d helper snippets' % \
		(metadata_src_text, len(opt_defs.keys()), len(use_defs.keys()), len(helper_snippets)))
	#print('Tags: %r' % use_tags_list)

	if len(args) == 0:
		raise Exception('missing command')
	cmd = args[0]

	# Compatibility with Duktape 1.3
	if cmd == 'autodetect-header':
		cmd = 'duk-config-header'
	if cmd == 'barebones-header':
		cmd = 'duk-config-header'

	if cmd == 'duk-config-header':
		# Generate a duk_config.h header with platform, compiler, and
		# architecture either autodetected (default) or specified by
		# user.  Support for autogenerated DUK_OPT_xxx flags is also
		# selected by user.
		result = generate_duk_config_header(opts, meta_dir)
		with open(opts.output, 'wb') as f:
			f.write(result)
	elif cmd == 'feature-documentation':
		result = generate_feature_option_documentation(opts)
		with open(opts.output, 'wb') as f:
			f.write(result)
	elif cmd == 'config-documentation':
		result = generate_config_option_documentation(opts)
		with open(opts.output, 'wb') as f:
			f.write(result)
	else:
		raise Exception('invalid command: %r' % cmd)

if __name__ == '__main__':
	main()


text:
ATC and the Pilot Who Turned Back
15 Feb 19 5 Comments
Last week we looked at a general aviation crash in which the pilot reported a Mayday as a result of an engine failure. Eight minutes later, the pilot crashed into a forest near Colbert, Oklahoma.
Today, I want to focus on what happened with the air traffic control centre who advised the pilot. The pilot was originally told that North Texas Regional Airport (GYI) was at his 12 o'clock and about 15 miles, and he was given vectors to fly there. Then the pilot asked for something closer and was advised that Durant was at his 3 to 4 o'clock and 10 miles.
A clear issue was that the controller was not very clear or confident about where the nearest airfield was and effectively offered the pilot a number of choices. This became critical when the controller mentioned a private airstrip close to the stricken aircraft and then dismissed it as too far.
Controller: They're showing a private field about 1 mile behind you called Cross Winds.
Pilot: Wish I knew where that was.
The controller gave the pilot the airport configuration for Durant and the pilot said something about the private strip. The controller asked him to say again.
Pilot: Where's that private strip?
Controller: It's not close enough for you to get to. There is GYI at your 2 to 3 o'clock 10 miles and Durant is at your 6 to 7 o'clock and 10 miles.
The pilot never responded again.
The Beechcraft had turned back, presumably to find the private airfield. It crash landed into a forested area; both the pilot and his wife were killed in the impact.
You can read the initial post here: The Pilot Turned Back: Engine Failure Leads to Fatal Crash
An internal investigation into this fatal accident was launched and it is particularly interesting to me as it gives us some insight into the communications within the control centre and why the advice offered to the pilot appears so confused.
Fort Worth Air Traffic Control Center were contacted by the pilot of a Beechcraft Bonanza who declared an emergency, having lost engine power. He was at just over 8,000 feet and descending. He requested help to get to an airport right away.
The controller at Fort Worth Air Traffic Control Center who was working the radar positions was a developmental controller, that is, still under instruction to become a certified professional controller. She was working a ten-hour shift covering three sectors, which were her last radar positions to train on. She'd been on the position for almost two hours when the incident started. At the same time, there was a 'rebank' or arrival push, so the sector suddenly became more complex. Because of the variation of traffic, it is common to combine and decombine the sectors throughout the day, based on load.
From an FAA standpoint, traffic counts above 18 aircraft are considered a reason for workload concern. That day at 15:00, the developmental controller was handling twenty four aircraft. The controller in charge had already decided to split the combined sector into individual sectors and instructed the Sector 37 controller to do so. The Sector 37 controller was surprised that they were combined, as usually by that time on a Sunday the sectors were always split.
Her instructor had also noticed the traffic increasing but he wasn't concerned as the controller in charge had already said that he was going to split the combined sectors.
Decombining the sectors normally took three to five minutes. He set everything up and attempted to split the positions, but the radar controller training team said that they were too busy.
It was at this point that the accident pilot first attempted to make contact. His transmission was "stepped on", that is, blocked by another person trasmitting at the same time. The developmental controller acknowledged the call and told the pilot to go ahead. He immediately declared an emergency.
We've lost engine power and we need…to get to an airport right away.
The developmental controller and her instructor quickly discussed his options and agreed that the aircraft should go straight ahead to North Texas Regional, about twelve miles away. The instructor remembers noticing Durant as about the same distance from the aircraft but North Texas Regional had a control tower, emergency services and it would be easier to for the pilot to get vectors.
Position of N252G after declaring emergency
The developmental controller advised the pilot that North Texas Regional was at the pilot's 12 o'clock and about 15 miles. The controller thought that North Texas Regional was a good choice because it was straight ahead and she believed that any significant turn would result in altitude loss. She wasn't sure why she hadn't recommended Durant (DUA) from the start. Her instructor also neglected to notice that this was the closer airport at the time of that first call. He believed that flying straight and level was preferred and couldn't remember where he'd received the training that turns should be avoided.
The controller in charge walked over to her position to monitor the situation. He assigned another controller to the radar associate position to assist the developmental controller by accepting hand-offs from adjacent sectors but asking them to delay switching the aircraft to their frequency to allow the controller time time to deal with the emergency. He doesn't remember anyone asking for the number of persons on board or the aircraft fuel or, when it came to it, any additional information about the Bonanza.
After the pilot asked for something closer, the controller gave him the details of Durant. The pilot asked if it was a left turn towards Durant and the controller corrected him, saying it was a right turn.
The Sector 37 controller who had been waiting to split the sectors heard the pilot say that he had lost power and that he needed an airport. Concerned that the pilot couldn't make either North Texas Regional or Durant, the Section 37 controller started searching the VFR sectional map. He remembers that he stood up on the console and put his reading glasses on, but the differences in scale made it difficult to determine the position of the aircraft on the VFR map. The instructor joined him to look at the VFR sectional map.
The Front Line Manager heard the commotion at the controller's position and noticed that there were three people, including the controller in charge, offering advice. He thought it would probably be easier for the Bonanza not to make any hard turns (towards Durant) but he wasn't sure if any turn would be required for the pilot to continue to North Texas Regional Airport. Then his airspace became busy and he stopped paying attention. He thought the aircraft had landed safely and did not find out that the plane had crashed until he was relieved of his position.
The Radar Associate was on break when the emergency call came in. When he came back, he heard someone say that a pilot had declared an emergency. He didn't think the sector was that busy but sat down on the radar associate position to help as needed. He felt that the recommendation for North Texas Regional Airport was reasonable as the airport was directly in front of the aircraft.
The Sector 37 controller, the controller in charge and the instructor continued to search the VFR sectional map on the overhead, trying to find an airport. The radar display did not have major features such as rivers and highways, so it was difficult to correlate the aircraft target symbol on the radar display with the VFR sectional charge.
Meanwhile, the developmental controller was speaking to the pilot. She doesn't remember which of them told her about Crosswind Airport, a private field which was not on her radar. Someone, again she doesn't remember who, said the private field was about a mile behind the aircraft.
She told the pilot about the private field. But then when she asked the others where it was, she got no response. Eventually someone told her that it was too far away.
The controller passed this on to the pilot and told him again about Durant. This was when the pilot decided to turn around, apparently hoping to try to find the private strip on his own.
Radar position of N252G after pilot was told that Crosswind was 1 mile behind his location (actually 9.81 NM behind the aircraft)
The problem was that Crosswind airport was actually ten miles behind the pilot, not one mile. Someone had misread the VFR sectional map.
The Radar Associate remembers Crosswind airport being brought up as a possibility. He didn't know much about it and said that he'd seen maybe three airplanes land there in five years. In his recollection, it was the Sector 37 controller who brought it up first. He couldn't recall who said it was only a mile away or who later said that it was too far.
The Sector 37 controller remembers the discussion of the private airport but not who relayed the information to the developmental controller. He said that if it was in range, he certainly would have mentioned it as an option but didn't know who came up with the one mile distance or how that was determined. He said that he would never attempt to inform the pilot of the location with that much precision. Possibly, he said, the training team misinterpreted some of the details that they overheard from those standing at the map.
He didn't hear the pilot asking about Crosswind and said he was definitely not the person who said that it was too far away.
The instructor also remembered the Section 37 controller as the one who suggested the Crosswind airport and also thought it was him who estimated the field as a mile behind the aircraft in distress. He agreed that it was very difficult to correlate the radar to the VFR sectional map and that it would have been much easier with geographic features on the radar display.
His recollection was that he and the Section 37 controller worked out that the initial estimate was inaccurate and that Crosswind airport was too far away. Durant was clearly the best option. He also noticed that the traffic had increased but it seemed a bad idea to decombine the sector given the volume of traffic and the emergency. He had not realized that the traffic volume had exceeded the normal level of 18 aircraft, although that level had already been exceeded before the distress call. The instructor said there was nothing more that they could have done for the pilot and he would not necessarily change anything they had done.
The Operations Manager in Charge was preparing for a meeting when the controller in charge told him about the emergency. Later, he reviewed a replay of the event, he was alarmed that the two sectors were combined with that level of traffic.
In retrospect, the Sector 37 controller said that he should have isolated the emergency aircraft from the others so that an exclusive service could have been provided to the pilot. However, he did not believe that he ever suggested the Crosswind airport to the controller talking to the pilot.
The Fort Worth Quality Control Manager saw the incident on the news and phoned in. He was told that everything was under control. When the quality control staff came in on Monday the 27th of July, they gathered the audio recordings to make transcripts. The QC manager was immediately concerned both that the sectors were combined and that the information provided to the Bonanza pilot did not meet his expectations. He had been of the impression that the developmental controller was close to certification and did not know that she was inexperienced on those sectors which had been combined. He felt that the controller had cleared the aircraft to one airport and then to a second airport before diverting the aircraft to a private strip without providing additional information.
When the Quality Control Manager later asked the controllers involved about the decision to clear the aircraft to North Texas Regional airport, they responded that 'you give them the closest airport straight in front of them." He could not find that guidance as part of the Fort Worth training and came to the conclusion that it may have been learned from previous facilities.
The Central Service Area (CSA) Quality Control Group Manager was notified by Regional Operations Center that the aircraft crashed and became involved in the accident review. He said that the the biggest roadblocks to his efforts are a lack of clear expectations of what quality control looked like. There are nine specialists covering the entire central United States and they are struggling to keep up. The turnover at such facilities is high and often as soon as a facility QC person was "up to speed" on quality control, they would move on and the process at their facility would need to start over.
Radar Track and Nearby Airports
In the end, what should have been a non-event turned into a tragic crash. The pilot starved his engine of fuel and then never followed the emergency checklist prompt to check the fuel selector, which would have solved the problem. The pilot also decided to turn around without any clear details of the "private strip" and failed to land on any of the nearby fields, instead crashing into a wooded area.
However, it's also important to think about the fact that when he first declared an emergency, he was 15.8 nautical miles from North Texas Regional, 7.5 nautical miles from Durant and 6.2 nautical miles from the Crosswind private airstrip. Flying at best glide, he should have been able to travel 17 nautical miles. The pilot failed to commit but it is also true that the air traffic control advice given him that day failed to support him. It is critical that all systems, not just those in the air, be investigated and analysed in order to learn how to do better in the future.
You can read the full report, listed under Air Traffic Control Factual, in the NTSB docket for the investigation.
Aircraft wreckage
Category: Accident Reports,
Rudy Jaka says:
A litany of errors and coincidences. The best advice, of course, would have been to tell the pilot to keep flying straight ahead.
An altitude of 8000 feet? I remember once coming into Heathrow at night with the Corvette (a 14 passenger light jet transport). It was a clear, windless night. At 10.000 feet ATC told us that we were "clear to land" at any runway we liked. I forgot which one we picked, but we decided to turn it into a practice. We closed the taps and made what amounted to a virtual "deadstick" approach and landing. Plenty of time and gllding distance.
What is the best speed for the Bonanza? Let's assume 120 KIAS. I reckon that the descent rate, the loss of altitude over time, would not be much more than 800 ft/min.
Which would have resulted in 10 minutes before hitting the ground – assuming 8000 ft AGL.So in that scenario, the range would have been 15, maybe 20 miles. But as soon as the pilot makes a sharp change of direction, the descent becomes much more steep, a lot of precious altitude lost plus an increasing disorientation and panic.
What is that famous saying again?
In aviation, in case of an emergency nothing is as useless as the sky above you, the runway behind you and the fuel you did not put in the tanks..
A sad litany of errors, errors of judgment and confusion.
The conflicting and confusing ATC advice, instead of being helpful and no matter how well intended, must have had a direct impact on the sequence of events that led to this fatal crash.
I don't know the Bonanza, but let us assume that the optimum gliding speed is 120 kts. And let us assume that it would descend about 700-800 ft./minute.
The aircraft was at 8000 feet, was that AGL? If correct, it would have been able to stay aloft for the better part of 10 minutes.
And, provided it would have been flying straight ahead, that meant a potential distance of better than 15 miles. In other words: North Texas should have been nicely within gliding range.
The offering of other airports to a pilot in distress, in an increasing state of disorientation, may well have been the final straw. It is well known that turning (without engine power) will cause a substantial loss of altitude and, if the pilot is attempting to "stretch" the glide by reducing speed, also will increase the risk of stalling.
It seems that when making the turn to a nearer landing place, the pilot became confused and disorientated. His flying must have deteriorated and instead of making a survivable crash-landing on more or less open terrain, he crashed into trees and killed himself and his wife.
There is an old saying in aviation that still is valid:
In an emergency situation, nothing is as useless as the air above you, the runway behind you and the fuel that is not in the tanks
Rudy — those numbers are close to what I recall for the C172, but possibly a bit optimistic; OTOH, a retractable might do a bit better than my 5 miles per 3000 feet. I had to know the number, because the standard rule is not to take a single-engine plane further out over water than it can glide back — but some inattentive air traffic controller once tried to route me from Islip direct to Atlantic city, up to ~35 miles from the nearest coastline.
But this strikes me as what a friend calls a yak-shaving (a step beyond a goat-roping); nobody seems to have taken command and said "The exercise is over; we've got an emergency", but I wouldn't be surprised if the authority (and the personnel to re-divide the zones) weren't there. It's true this mess took a cascade of errors, not unlike most crashes that can be blamed entirely on the flying elements, but ISTM that circumstances have become more unfavorable. The controllers' union made the incredible mistake of endorsing Reagan in 1980 and then believing he wouldn't act when they struck; he fired them all, and I'm not sure ATC in the US has yet recovered. (Recovering from the same approach to a police strike in my hometown (Boston) took over two generations.) I never got the feeling when I was flying (5-8 years before the strike) that ATC was trivializing my flight because I was a lot smaller and slower than the scheduled airlines, but I'm less unhappy not have kept flying since; solo pilots have far less room for error than a crew, and it sounds like ATC know less about small equipment than would be desirable.
Chip,
I can only agree with all you just wrote, but the situation in Europe is probably different from that in the USA. There is a lot less free airspace, but that also means that ATC are often more used to light aircraft mixing it with the big boys (and girls) in their big toys. I have had some hilarious moments when ATC were dealing with inexperienced pilots, making sure that they would not come to any harm, but at the same time making them painfully aware of their failings. But that would be something for another day (and comment).
About gliding distance: During training, studying for the CPL I think, a question came up:
A Piper Cub and a jet fighter (F104) have an engine failure at 10.000 feet.
Assuming that the fighter pilot does not bail out, which aircraft will cover the greater distance before hitting the deck?
Surprisingly, the fighter covered a far greater distance but when it crashed the Cub still had a few minutes up in he air.
Another (actual exam) question was: A world war 2 bomber loses power over the channel on its way back from a raid over Germany (after all, I did my CPL in 1969). It was hit by flack and did not make it to it's target, so it still has all its ordnance on board.
What will be the best course of action to extend the gliding range:
1. Drop the bombs and as much weight as they can over the channel? or
2. Keep it all on board?
Surprisingly, the RANGE increased with the weight still on board.
So, with that in mind I would not be surprised to find that the Bonanza, as you already mentioned, with less drag from wheels and wing struts, would actually cover the same or possibly more distance.
The 172 pilot would have more TIME to consider his options.
That's a fascinating set of results. I wonder how old they are; I used to know someone who flew bombers out of England in WWII and would love to know whether he was taught this. (He learned to fly before learning to drive, and said he had to conquer the urge to yank back on the steering wheel at crowded intersections.)
It sounds like European ATC may have had more time than US ATC to deal with low-and-slow even before Reagan damaged the system.

redpajama_set_name: RedPajamaCommonCrawl


text:
Pandulfo II el Viejo (m. agosto de 1014) fue príncipe de Benevento de 981 y Capua (como Pandulfo III) desde 1008 o 1009 hasta su muerte. Era hijo de Landulfo III que fue copríncipe entre 959 y 968. Pandulfo fue asociado por primera vez como copríncipe (en Capua) en 977.

A la muerte de su padre, Pandulfo fue marginado por su tío,  Pandulfo Cabeza de Hierro, que entregó Capua y Benevento a su hijo mayor Landulfo IV a su muerte en 981. Ese año, sin embargo, Landulfo IV se vio obligado a dividir y Benevento fue entregado a Pandulfo. En mayo de 987, asoció a su hijo Landulfo al trono, siguiendo la tradición dinástica iniciada por Atenulf I.

En 999, el emperador Otón III visitó el santuario de San Miguel Arcángel en el Monte Gargano. A su regreso a través de Benevento, firmó un diploma a favor del monasterio de Santa Sofía el 11 de marzo. S. Sofía fue la fundación del linaje de Pandulfo y probablemente se utilizó como panteón. Por razones desconocidas, Otón y Pandulfo se enfrentaron en el año 1000, posiblemente por las reliquias de San Bartolomé patrono de Benevento, al que Otón había erigido una nueva iglesia en la Isla Tiberina—San Bartolomeo all'Isola—poco antes. De acuerdo con los Annales Beneventani, Otto rex cum magno exercitu obsedit Benevento: "El rey Otón con un gran ejército sitió Benevento." Sin embargo, no sucedió nada significativo salvo, quizás, la entrega de ciertas reliquias (posiblemente la piel de Bartolomé).

En ese año mismo año 1000, los capuanos expulsaron a su príncipe  Adhemar, pro-Imperio e invitaron a Landulf di Sant'Agata, el hermano de Pandulfo, a ser su nuevo príncipe. En 1003, una rebelión dirigida por Adelfer, conde de Avellino, expulsó a Pandulfo y a su hijo de Benevento. Sin embargo, en 1005, los encontramos gobernando de nuevo desde su capital. No obstante, los disturbios proliferaron por todo el principado.

En 1007, Landulfo de Capua murió y Pandulfo lo sucedió junto a su hijo, Pandulfo II de Capua. En 1011, 1012 o 1013, el nieto epónimo de Pandulfo, Pandulfo III, hijo de Landulfo, fue asociado con el gobierno de Benevento. A partir de entonces, Pandulfo desaparece del primer plano hasta su muerte en 1014.

Descendencia

 Gaitelgrima, esposa de Guaimario III de Salerno
 Maria, esposa de Sergio II de Amalfi
 Landolfo V de Benevento
 Pandulfo IV de Capua
 Atenulfo, abad de Montecassino

Referencias

 Caravale, Mario (ed). Dizionario Biografico degli Italiani: LXIII Labroca-Laterza. Roma, 2004.
Príncipes de Capua
Príncipes de Benevento
Guerreros lombardos
Fallecidos en 1014
Nacidos en el siglo X

redpajama_set_name: RedPajamaWikipedia


On December, 17, 2014, Obama gave a speech on his intentions to normalized the diplomatic relations between Cuba and the USA, by supporting the Cuban people, and making changes to facilitate authorized travel for Americans to Cuba; as well as certain commerce and financial activities and the flow of communications to and from the island as well.
The Department of Treasury's Office of Foreign Assets Control (OFAC) amended the Cuban Assets Control Regulations toward Cuba. And yes indeed it's a step forward for the Americans and the Cuban people, but the restrictions (Embargo) remain. So, what has really been changed?
OFAC has issued *general licenses within the 12 categories of authorized travel for many travel-related to or from Cuba that previously required a **specific license (i.e., an application and a case-by-case determination). Which means anyone who meets the requirements doesn't need to apply for a license to travel to Cuba.
Family visits; official business of the U.S. government, foreign governments, and certain intergovernmental organizations; journalistic activity; professional research and professional meetings; educational activities; religious activities; public performances, clinics, workshops, athletic and other competitions, and exhibitions; support for the Cuban people; humanitarian projects; activities of private foundations or research or educational institutes; exportation, importation, or transmission of information or information materials; and certain authorized export transactions.
You can import goods from Cuba into the US which it's total value doesn't exceed $400. Take a note, you can't import more than $100 of alcohol or tobacco. (This policy applies also to foreign persons entering the United States from travel that included Cuba). Be aware that you can't buy alcohol or cigars of Cuban origin to import into US through a third country or via Internet.
The new rule allows US travellers to use their credit or debit cards issued by an American financial institution.
Diem rate no longer applies, and there is not limit dollars on expenses.
Remittances have been raised from $500 per quarter to $2,000 per quarter.
Visitors may carry up to $10,000 in cash to Cuba for family remittances per authorized trip.
Certain remittances to Cuban nationals for humanitarian projects, support for the Cuban people, or development of private business are now generally authorized.
Travellers will be able to purchase their ticket directly from the airline offering the service. Airlines and travelers are responsible for maintaining records of their Cuba-related transactions for at least five years.
Humanitarian projects include medical and health-related projects; construction projects intended to benefit legitimately independent civil society groups; environmental projects; projects involving formal or non-formal educational training, within Cuba or off-island, on the following topics: Entrepreneurship and business, civil education, journalism, advocacy and organizing, adult literacy, or vocational skills; community-based grass-roots projects; projects suitable to the development of small-scale private enterprise; projects that are related to agricultural and rural development that promote independent activity; micro-financing projects, except for loans, extensions of credit, or other financing prohibited and projects to meet basic human needs.
A license is an authorization from OFAC to engage in a transaction that otherwise would be prohibited. There are two types of licenses: general licenses and specific licenses. *A general license authorizes a particular type of transaction for a class of persons without the need to apply for a license.**A specific license is a written document issued by OFAC to a particular person or entity, authorizing a particular transaction in response to a written license application.
A friend once told me: … All the goods I have in stock are needed in Cuba…to which I replied: From your point of view; I agree, in fact it's not a bad assessment of the whole panorama. I'm afraid to disappoint you; but Cuba is not a consumer society yet. Therefore we have to take into account the difference between lacking and needing, which differ from one another.
There are specific regulations for foreign investment, regardless of contribution to the economy of the country; the investment also has to produce a social welfare to which Cuba is no exception. Even though the foreign investment law states that one may invest in all sectors -with the exception of education, health and military forces-; is important to acknowledge where the real possibilities of investment are.
Last November, Havana held two of its major events -FIHAV/Business fair and The International Ballet Festival of Havana, which attracted many visitors. I tried to book a hotel on the second week of October, and all the hotels in Havana were already booked.
Nirint Shipping: Established in Cuba since 1992.
Indiana Finance BV (formerly Fondel Finance BV): One of the world's leading metal trading companies; with more than four decades trading -nickel- with Cuba. This company conducts 70 percent of trade between the Netherlands and Cuba.


text:
- eSchool News - https://www.eschoolnews.com -
Why allow ed-tech access? 'We owe it to our students'
Posted By From staff reports On November 12, 2012 @ 10:48 pm In IT Management,Teaching & Learning,Top News | Comments Disabled
Unfettered access to technology can be frightening, Sheninger says—but 'find a way to make it work.'
Technology can be intimidating, and even frightening—but Eric Sheninger, principal of New Jersey's New Milford High School (NMHS), says that educators must overcome their fear of putting technology into students' hands.
Through efforts to create a paperless environment, a bring-your-own-device initiative, and the use of social media, NMHS teachers and administrators are integrating technology into all aspects of the school day—and these efforts are paying off. For these reasons and others, we've chosen NMHS as our "eSchool of the Month" for November/December.
Here, Sheninger describes the school's ed-tech accomplishments and the keys to its success. (Editor's note: To nominate your school or district for this award, and to read about past winners, go to http://www.eschoolnews.com/eschool-of-the-month [2].)
How does your school use technology to advance learning?
Technology has been embraced as an essential tool in enhancing the learning experience. Teachers have enjoyed the resources, flexibility, autonomy, and professional development to effectively integrate a variety of tools.
With the support of central office and the IT department, the entire district has recently adopted Google Apps to improve collaboration and communication between all stakeholder groups. In math, document cameras record the teacher solving equations. Those mini-lessons are then uploaded to YouTube or Google Sites, where students can refer to them. Students in graphic arts use iMacs and Adobe software tools to create digital magazines, newsletters, and edit photos taken during our newly formed and extremely popular Digital Photography course.
Our Holocaust/Genocide Studies students routinely Skype with Holocaust survivors from around the world and with historians in Israel. When the students participate in the 10-day European Holocaust Study Tour, they actively reflect and share their experiences through a daily blog.
Our teachers in all disciplines regularly integrate a variety of Web 2.0 tools, such as Voicethread, Glogster, Poll Everywhere, Edmodo, Wordle, and Animoto. Over the past two years, we have moved from limited access to interactive whiteboards in classrooms to 15. The latest addition is a wireless IWB solution consisting of Apple TV, an HDMI projector, and an iPad.
Have you noticed an increase in student performance and/or motivation as a result?
New Milford High has become a role model for schools across the country on how to innovate with technology. We routinely host visitors who want to learn more about our use of social media, as well as our Bring Your Own Device (BYOD) initiative.
Since beginning to transform our school, we have experienced steady gains in standardized test scores (although they dropped a bit this year). Student motivation to learn has greatly increased, and students have referred to our BYOD initiative and our use of social media in the teaching and learning process as a privilege. Unleashing student creativity increases motivation.
NMHS students are afforded the opportunity to demonstrate conceptual understanding and apply what they have learned through the use of an array of tools. In essence, we are creating a learning culture that is more closely aligned with the 21st-century workforce than that of an industrial-aged school house. Achievement can be measured quantitatively, and we have seen gains in state and national testing results such as the SAT and AP test scores. But our commitment to improving access to technology for faculty and students also makes for a better working and learning environment. That improves climate and culture, and that improves the world.
How do you use technology to streamline school administration and aid in decision-making? What have been the results?
The administrative team uses iPads to conduct classroom walkthroughs, and data are recorded and analyzed using Teachscape Walk. The district also promotes paperless environments. We do our very best to take notes, communicate with stakeholders, and store and access files in the cloud. NMHS is in the process of becoming paperless through the targeted use of ZippSlip. Other apps used to streamline administration include Evernote, Dropbox, Classlink Launchpad, DocuSign Ink, PocketCloud, Delicious, and Diigo.
Social media also has become an indispensable administrative tool to enhance communications, improve public relations, and establish a positive brand presence. This has been accomplished through the creation and consistent use of a Twitter account (@NewMilfordHS) and Facebook page [3]. The school also has established YouTube and Flickr accounts. The use of social media has allowed us to form strategic partnerships with companies and stay abreast of the latest trends and developments in the world of educational technology. As principal, I regularly use social media to advance my knowledge through professional growth and the formation of a Personal Learning Network (PLN). I can be found on Twitter (@NMHS_Principal), Google+, LinkedIn, Blogger, Flickr, Pinterest, etc.
Through the formation of a Professional Growth Period, staff members now have the time during the day to pursue innovation and integrate technology. The result has been an increase in effective use of technology without the use of mandates or directives. Some teachers have used this time to work on the flipped approach to learning with Adobe Captivate, while others have learned how to blog—with a full rollout to all classes anticipated this year. We've also saved precious funds by implementing a BYOD initiative and by using Skype to hold video conferences for a fraction of the cost.
What ed-tech project are you most proud of, and why?
I am most proud of the BYOD initiative that is now in its second year. This is not only a cost-effective means to integrate technology, but also a critical factor in teaching students about digital responsibility, citizenship, and productive ways to use their devices. We have been able to overcome many of the challenges of implementing a program like this through professional development, self-directed education, partnerships, and a common vision for use.
What's your best ed-tech advice to colleagues?
Look for solutions instead of excuses when it comes to integrating technology to transform school culture. Stop fighting the idea of putting technology in the hands of students. The hallways, classrooms, and lunchrooms should be places where students have access to learning opportunities.
Instead of keeping technology at bay, commit to finding a way to bring it safely into the harbor. Fear is very powerful and often inhibits change and innovation. Eleanor Roosevelt once said, "Do something every day that frightens you." Unlimited access to technology can be a frightening endeavor. Find a way to make it work. We owe it to our students to allow them to function and learn in the technology-rich world they know. We owe it to them to provide them with the opportunity to create more, research responsibly, behave ethically, think divergently, and evolve consistently. We need to learn and evolve with them.
Article printed from eSchool News: https://www.eschoolnews.com
URL to article: https://www.eschoolnews.com/2012/11/12/why-allow-ed-tech-access-we-owe-it-to-our-students/
[1] Image: http://www.eschoolnews.com/2012/11/12/why-allow-ed-tech-access-we-owe-it-to-our-students/tablet-using-girl-2/
[2] http://www.eschoolnews.com/eschool-of-the-month: http://www.eschoolnews.com/eschool-of-the-month
[3] Facebook page: http://www.facebook.com/pages/New-Milford-High-School/114382501908040
Copyright © 2021 eSchool News. All rights reserved.


Free Apps to Download TODAY ONLY 03/21/2019. Mid Atlantic Consulting APP review today.
Easy Loan Payoff Calculator ( $0.99 → Free ) Matthew King With Loan Calculator, you don't need to have a bunch of reference help files or documentation. Finding out how much your monthly payments will be is as easy as filling out three open fields. Loan Calculator provides you with the option to add additional monthly and yearly payments, helping determine how much faster your loan can be paid off and how much money you'll save. The total lifetime cost of your loan including interest is calculated as well.
3rd Grade Vocabulary Prep will help your child learn new words and their definitions. They're able to take vocabulary quizzes that feature a word at the top of the screen and four possible definitions at the bottom. Whether they guess right or wrong, your child will be able to see the correct definition along with the word used in an example sentence. 3rd Grade Vocabulary Prep includes hundreds of vocabulary words to learn, and adheres to common core standards for 3rd grade.
Magic Launcher has the ability to launch over 100,000 different apps and actions right from your lock screen. Upon launch it'll need to determine which apps are supported. After it has finished you'll able to add quick access icons for your favorite apps, contacts, settings, and services to your lock screen. The app also includes 3D Touch support.


All professionals at Providence Foot and Ankle Center maintain the highest levels of accreditation and pursue ongoing education to stay abreast of the latest trends in the medical field. Read on to learn more about our staff's experience and training.
Dr. Peter Park has been in practice since 1996 following the completion of his studies at the Scholl College of Podiatric Medicine and Podiatric Surgical Residency at Midway Hospital in Los Angeles. Dr. Park is known for providing his patients with high quality service, compassion and great chairside manner. He is board certified by the American Board of Podiatric Surgery and a member of the American College of Foot and Ankle Surgeons as well as the American Society of Podiatric Surgeons.
Dr. Park is affiliated with the following hospitals: Hollywood Presbyterian Hospital in Los Angeles, Orange Coast Memorial Hospital in Fountain Valley, and Good Samaritan Hospital in Los Angeles. At Orange Coast Memorial Hospital, he has served as the Chief of the Department of Podiatric Surgery and the Director of Podiatric Surgical Residency. In 2006, he received the President's Choice Award from the Talbert Medical Group.


Legal offices have specific challenges with their documents using Microsoft Word, especially if they have transitioned from WordPerfect. There is a reason for this; the two programs are engineered very differently. Having a basic understanding of these differences will clarify many of the mysteries around Word. There are also challenges of creating legal documents in a hurry with required style settings. This webinar by Mitzi Gibson Katz will offer solutions to these issues and others that are drawn from common support calls and requested training topics.
This webinar is for people using Microsoft Word to create and format professional documents in a time crunch. This webinar will be presented using Word 2010 but is appropriate for 2007 and 2013.


text:
<?php declare(strict_types=1);

namespace Aviat\AnimeClient;

use Aviat\AnimeClient\Enum\EventType;
use Aviat\Ion\Event;
use Aviat\Ion\Json;
use Aura\Router\{
	Map,
	Matcher,
	Route,
	Rule,
};
use Aviat\AnimeClient\API\FailedResponseException;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\Friend;
use Aviat\Ion\Type\StringType;
use LogicException;
use ReflectionException;

use function Aviat\Ion\_dir;

/**
 * Basic routing/ dispatch
 */
final class Dispatcher extends RoutingBase {

	/**
	 * The route-matching object
	 * @var Map $router
	 */
	protected Map $router;

	/**
	 * The route matcher
	 * @var Matcher $matcher
	 */
	protected Matcher $matcher;

	/**
	 * Routing array
	 * @var array
	 */
	protected array $routes;

	/**
	 * Routes added to router
	 * @var array $outputRoutes
	 */
	protected array $outputRoutes;

	/**
	 * Constructor
	 *
	 * @param ContainerInterface $container
	 */
	public function __construct(ContainerInterface $container)
	{
		parent::__construct($container);
		$router = $this->container->get('aura-router');
		$this->router = $router->getMap();
		$this->matcher = $router->getMatcher();
		$this->routes = $this->config->get('routes');
		$this->outputRoutes = $this->setupRoutes();
	}

	/**
	 * Get the current route object, if one matches
	 *
	 * @return Route|false
	 */
	public function getRoute(): Route | false
	{
		$logger = $this->container->getLogger();

		$rawRoute = $this->request->getUri()->getPath();
		$routePath = '/' . trim($rawRoute, '/');

		if ($logger !== NULL)
		{
			$logger->info('Dispatcher - Routing data from get_route method');
			$logger->info(print_r([
				'route_path' => $routePath
			], TRUE));
		}

		return $this->matcher->match($this->request);
	}

	/**
	 * Get list of routes applied
	 *
	 * @return array
	 */
	public function getOutputRoutes(): array
	{
		return $this->outputRoutes;
	}

	/**
	 * Handle the current route
	 *
	 * @param object|null $route
	 * @return void
	 * @throws ReflectionException
	 */
	public function __invoke(object $route = NULL): void
	{
		$logger = $this->container->getLogger();

		if ($route === NULL)
		{
			$route = $this->getRoute();

			if ($logger !== NULL)
			{
				$logger->info('Dispatcher - Route invoke arguments');
				$logger->info(print_r($route, TRUE));
			}
		}

		if ( ! $route)
		{
			// If not route was matched, return an appropriate http
			// error message
			$errorRoute = $this->getErrorParams();
			$controllerName = DEFAULT_CONTROLLER;
			$actionMethod = $errorRoute['action_method'];
			$params = $errorRoute['params'];
			$this->call($controllerName, $actionMethod, $params);
			return;
		}

		$parsed = $this->processRoute(new Friend($route));
		$controllerName = $parsed['controller_name'];
		$actionMethod = $parsed['action_method'];
		$params = $parsed['params'];

		$this->call($controllerName, $actionMethod, $params);
	}

	/**
	 * Parse out the arguments for the appropriate controller for
	 * the current route
	 *
	 * @param Friend $route
	 * @throws LogicException
	 * @return array
	 */
	protected function processRoute(Friend $route): array
	{
		if ( ! array_key_exists('controller', $route->attributes))
		{
			throw new LogicException('Missing controller');
		}

		$controllerName = $route->attributes['controller'];

		// Get the full namespace for a controller if a short name is given
		if ( ! str_contains($controllerName, '\\'))
		{
			$map = $this->getControllerList();
			$controllerName = $map[$controllerName];
		}

		$actionMethod = array_key_exists('action', $route->attributes)
			? $route->attributes['action']
			: NOT_FOUND_METHOD;

		$params = [];
		if ( ! empty($route->__get('tokens')))
		{
			$tokens = array_keys($route->__get('tokens'));
			foreach ($tokens as $param)
			{
				if (array_key_exists($param, $route->attributes))
				{
					$params[$param] = $route->attributes[$param];
				}
			}
		}
		$logger = $this->container->getLogger();
		if ($logger !== NULL)
		{
			$logger->info(Json::encode($params));
		}

		return [
			'controller_name' => $controllerName,
			'action_method' => $actionMethod,
			'params' => $params
		];
	}

	/**
	 * Get the type of route, to select the current controller
	 *
	 * @return string
	 */
	public function getController(): string
	{
		$routeType = $this->config->get('default_list');
		$requestUri = $this->request->getUri()->getPath();
		$path = trim($requestUri, '/');

		$segments = explode('/', $path);
		$controller = reset($segments);

		$logger = $this->container->getLogger();
		if ($logger !== NULL)
		{
			$logger->info('Controller: ' . $controller);
		}

		if (empty($controller))
		{
			$controller = $routeType;
		}

		return $controller ?? '';
	}

	/**
	 * Get the list of controllers in the default namespace
	 *
	 * @return array
	 */
	public function getControllerList(): array
	{
		$defaultNamespace = DEFAULT_CONTROLLER_NAMESPACE;
		$find = ['\\', 'Aviat/AnimeClient/'];
		$replace = ['/', ''];

		$path = str_replace($find, $replace, $defaultNamespace);
		$path = trim($path, '/');
		$actualPath = realpath(_dir(SRC_DIR, $path));
		$classFiles = glob("{$actualPath}/*.php");
		if ($classFiles === FALSE)
		{
			return [];
		}

		$controllers = [];

		foreach ($classFiles as $file)
		{
			$rawClassName = basename(str_replace('.php', '', $file));
			$path = (string)StringType::from($rawClassName)->dasherize();
			$className = trim($defaultNamespace . '\\' . $rawClassName, '\\');

			$controllers[$path] = $className;
		}

		return $controllers;
	}

	/**
	 * Create the controller object and call the appropriate
	 * method
	 *
	 * @param  string $controllerName - The full namespace of the controller class
	 * @param  string $method
	 * @param  array  $params
	 * @return void
	 */
	protected function call(string $controllerName, string $method, array $params): void
	{
		$logger = $this->container->getLogger();

		try
		{
			$controller = new $controllerName($this->container);

			// Run the appropriate controller method
			$logger?->debug('Dispatcher - controller arguments', $params);

			$params = array_values($params);
			$controller->$method(...$params);
		}
		catch (FailedResponseException)
		{
			$controllerName = DEFAULT_CONTROLLER;
			$controller = new $controllerName($this->container);
			$controller->errorPage(500,
				'API request timed out',
				'Failed to retrieve data from API (╯°□°)╯︵ ┻━┻');
		}
		/* finally
		{
			// Log out on session/api token expiration
			Event::on(EventType::UNAUTHORIZED, static function () {
				$controllerName = DEFAULT_CONTROLLER;
				(new $controllerName($this->container))->logout();
			});
		} */
	}

	/**
	 * Get the appropriate params for the error page
	 * passed on the failed route
	 */
	protected function getErrorParams(): array
	{
		$logger = $this->container->getLogger();
		$failure = $this->matcher->getFailedRoute();

		if ($logger !== NULL)
		{
			$logger->info('Dispatcher - failed route');
			$logger->info(print_r($failure, TRUE));
		}

		$actionMethod = ERROR_MESSAGE_METHOD;

		$params = [];

		switch($failure->failedRule) {
			case Rule\Allows::class:
				$params = [
					'http_code' => 405,
					'title' => '405 Method Not Allowed',
					'message' => 'Invalid HTTP Verb'
				];
			break;

			case Rule\Accepts::class:
				$params = [
					'http_code' => 406,
					'title' => '406 Not Acceptable',
					'message' => 'Unacceptable content type'
				];
			break;

			default:
				// Fall back to a 404 message
				$actionMethod = NOT_FOUND_METHOD;
			break;
		}

		return [
			'params' => $params,
			'action_method' => $actionMethod
		];
	}

	/**
	 * Select controller based on the current url, and apply its relevant routes
	 *
	 * @return array
	 */
	protected function setupRoutes(): array
	{
		$routeType = $this->getController();

		// Add routes
		$routes = [];
		foreach ($this->routes as $name => &$route)
		{
			$path = $route['path'];
			unset($route['path']);

			$controllerMap = $this->getControllerList();
			$controllerClass = array_key_exists($routeType, $controllerMap)
				? $controllerMap[$routeType]
				: DEFAULT_CONTROLLER;

			// If there's an explicit controller, try to find
			// the full namespaced class name
			if (array_key_exists('controller', $route))
			{
				$controllerKey = $route['controller'];
				if (array_key_exists($controllerKey, $controllerMap))
				{
					$controllerClass = $controllerMap[$controllerKey];
				}
			}

			// Prepend the controller to the route parameters
			$route['controller'] = $controllerClass;

			// Select the appropriate router method based on the http verb
			$verb = array_key_exists('verb', $route)
				? strtolower($route['verb'])
				: 'get';

			// Add the route to the router object
			if ( ! array_key_exists('tokens', $route))
			{
				$routes[] = $this->router->$verb($name, $path)->defaults($route);
				continue;
			}

			$tokens = $route['tokens'];
			unset($route['tokens']);

			$routes[] = $this->router->$verb($name, $path)
				->defaults($route)
				->tokens($tokens);
		}

		return $routes;
	}
}
// End of Dispatcher.php

redpajama_set_name: RedPajamaGithub