# Part of the A-A-P recipe executive: handling of a dictlist
# Copyright (C) 2002 Stichting NLnet Labs
# Permission to copy and use this file is specified in the file COPYING.
# If this file is missing you can find it here:
http://www.a-a-p.org/COPYING
# A Dictlist is a list of dictonaries, used for a parsed variable.
# The dictionary contains the "name" key for the item itself, and other keys
# for attributes of that item. Attributes starting with an underscore are for
# internal use (e.g., "_node").
import string
from Util import *
from Process import recipe_error
def get_attrval(line, idx):
"""Get items starting at line[idx] and ending at a '}' character.
Items are white separated.
Quotes are used to include {} characters in an item.
Returns the string for list of items and the index of the next
character."""
line_len = len(line)
res = '' # result collected so far
i = idx
inquote = '' # inside quotes
nesting = 0 # nested {}
while 1:
if i >= line_len: # end of line
break
# End of quoted string?
if inquote:
if line[i] == inquote:
inquote = ''
# Start of quoted string?
elif line[i] == '"' or line[i] == "'":
inquote = line[i]
# Stop character found?
else:
if line[i] == '}':
if nesting == 0:
break
nesting = nesting - 1
elif line[i] == '{':
nesting = nesting + 1
res = res + line[i]
i = i + 1
return res, i
def get_attrdict(rpstack, globals, arg, idx, expand):
"""Obtain attributes {name = val} from arg[idx:].
Returns a dictionary with the attributes and the index of the character
after the last "}"
When there is no attribute return {} and idx.
When "expand" is non-zero, expand $VAR things.
When "expand" is zero "globals" isn't used.
"""
from Commands import aap_eval
arglen = len(arg)
res = {}
while 1:
i = skip_white(arg, idx)
if i >= arglen or arg[i] != '{':
break
i = skip_white(arg, i + 1)
e = i
while 1:
if e >= arglen:
recipe_error(rpstack, _("Syntax error after {"))
if not varchar(arg[e]):
break
e = e + 1
if e == i:
recipe_error(rpstack, _("Missing name after {"))
name = arg[i:e]
i = skip_white(arg, e)
if i < arglen and arg[i] == '}':
# No "= value", use one.
val = 1
else:
if i >= arglen or arg[i] != '=':
recipe_error(rpstack, _("Missing = after {"))
i = skip_white(arg, i + 1)
val, i = get_attrval(arg, i)
if i >= arglen or arg[i] != '}':
recipe_error(rpstack, _("Missing } after {"))
idx = i + 1
# May need to expand $VAR things.
if expand and val != 1:
val = aap_eval(rpstack, globals, val, Expand(1, Expand.quote_aap))
res[name] = val
return res, idx
def string2dictlist(rpstack, var, startquote = ''):
"""Create a Dictlist from a variable string. The variable has to
be evaluated and white-separated items isolated.
When "startquote" isn't empty, behave like "var" was preceded by it.
"""
result = []
# TODO: handle parenthesis: "(foo bar) {attr = val}"
varlen = len(var)
inquote = startquote
i = 0
while i < varlen:
# Separate one item, removing quotes.
item = ''
while 1:
# Quoted string: check for its end.
if inquote:
if i >= varlen:
break # Missing quote! error message below.
if var[i] == inquote:
inquote = '' # End of quoted text.
else:
item = item + var[i]
i = i + 1
continue
# An item ends at the end of the line, at white space or at '{'.
if i >= varlen or var[i] == '\n' \
or var[i] == ' ' or var[i] == '\t' or var[i] == '{':
if item:
# Found one item, add it.
# Parse {attr = value} zero or more times.
adddict, i = get_attrdict(rpstack, None, var, i, 0)
adddict["name"] = item
result.append(adddict)
item = ''
else:
i = i + 1
if i >= varlen: # end of var
break
continue
# Start of quoted string?
if var[i] == '"' or var[i] == "'":
inquote = var[i]
i = i + 1
continue
item = item + var[i]
i = i + 1
if inquote != '':
recipe_error(rpstack, _("Missing quote: ") + inquote)
return result
def var2dictlist(globals, varname):
"""Get the value of $"varname" as a dictlist.
Should only be called when $"varname" exists and isn't empty."""
try:
dictlist = string2dictlist([], get_var_val(0, globals, varname))
except UserError, e:
raise UserError, _("Error in parsing $%s") % varname
if not dictlist:
raise UserError, _("$%s evaluates to nothing") % varname
return dictlist
def listitem2str(item):
"""Turn an item of a list into a string, making sure special characters are
escaped such that concatenated items are white-separatable."""
# First check which quote would be most appropriate to start with. It
# looks a lot better when it's not halfway the item.
quote = ''
item_str = str(item)
for c in item_str:
if c == "'":
quote = '"'
break
if c == '"':
quote = "'"
break
if is_white(c):
quote = '"'
res = quote
for c in item_str:
if string.find("'\" \t", c) >= 0:
if c == quote:
res = res + quote
quote = ''
if not quote:
if c == '"':
quote = "'"
else:
quote = '"'
res = res + quote
res = res + c
return res + quote
def dictlistattr2str(dl):
"""Print the attributes in dictlist "dl"."""
str = ''
for k in dl.keys():
if k != "name" and k[0] != "_":
str = str + ('{%s=%s}' % (k, listitem2str(dl[k])))
return str
def dictlist2str(list, expand = None):
"""Turn a dictlist into a string that can be printed.
Don't use backslashes to escape special characters.
Do expanding according to "expand"."""
if not expand:
expand = Expand(1, Expand.quote_aap)
str = ''
for i in list:
if str:
str = str + ' '
str = str + expand_item(i, expand, "name")
return str
# vim: set sw=4 sts=4 tw=79 fo+=l: