== Function Specifiers ==

Both the NIS Server and Schema Compatibility plugins use format
specifiers given in their respective configurations to control how they
massage the contents of entries which are already in the directory
server into entries in either NIS maps or the compatibility area of the
directory.

=== Basics ===

Format specifiers can reference values of attributes in the entry which
is currently being examined like so:
  %{attribute}

The values of multiple attributes can be mixed together like so:
  %{one_attribute}:%{another_attribute}

Like shell variables, a reference to an attribute can specify a default
value to be used in cases where the entry has no value for the specified
attribute:
  %{gecos:-%{cn:-}}

Again, like shell variables, a reference can also specify an alternate
value to be provided when the entry has a value (though this is not
expected to be used as frequently) which should be overridden:
  %{cn:+%{cn},,,}%{cn:-%{gecos}}

Or to use a more concrete (but still simplified) example:
  %{uid}:*:%{uidNumber}:%{gidNumber}:%{gecos:-%{cn:-}}:%{homeDirectory:-/}:%{loginShell:-/bin/sh}

Additional operators include "#", "##", "%", "%%", "/", "//", which
operate in ways similar to their shell counterparts (with one notable
exception: patterns for the "/" operator can not currently be anchored
to the beginning or end of the string).

Strictly speaking, references to attributes return ''lists'' of values,
or produce an evaluation error if no values are found.  This helps
prevent invalid entries from showing up in NIS maps and in the
directory.

=== Functions ===

Additionally, several built-in "function"s are available for importing
values from other entries and combining them with data from the current
entry.  Generally, function invocations look like this:

  %function(ARG[,...])

A function invocation uses a comma-separated list of double-quoted
arguments.  Any arguments which themselves contain a double-quote need
to escape the double-quote using a '\' character.  Naturally, the '\'
this character itself also needs to be escaped whenever it appears.
Some functions can take expressions as arguments, allowing functions to
operate on data after it has been acted on by other functions.

== Implemented Functions ==

=== first ===

  first(''EXPRESSION''[,''DEFAULT''])

Evaluates ''EXPRESSION'', and if one or more values is produced,
provides only the first value.  (Here, ''first'' refers to the first
value in the list of values after they've been sorted.)  If no values
result, then ''DEFAULT'' is evaluated as an expression and its result is
provided.  Nothing is done to ensure that ''DEFAULT'' provides only one
value, however.

=== match ===

  match(''EXPRESSION'',''PATTERN''[,''DEFAULT''])

Selects the single value of ''EXPRESSION'' which matches the globbing
pattern ''PATTERN''.  If no value matches, and a ''DEFAULT'' was
specified, then ''DEFAULT'' is evaluated as an expression and its value
is provided.

Here's an example entry:

  dn: cn=group
  member: bob
  member: dave

And here's how it evaluates out:

  %match("%{member}","b*")        -> bob
  %match("%{member}","d*")        -> dave
  %match("%{member}","e*")        no value
  %match("%{member}","*e*")       -> dave
  %match("%{member}","e*","jim")  -> jim
  %match("%{member}","*","%{cn}") -> group

=== regmatch ===

  regmatch(''EXPRESSION'',''PATTERN''[,''DEFAULT''])

Selects the value of ''EXPRESSION'' which matches the extended regular
expression ''PATTERN''.  If no values match, and a ''DEFAULT'' was
specified, then ''DEFAULT'' is evaluated as an expression and its value
is produced.

Here's an example entry:

  dn: cn=group
  member: bob
  member: dave

And here's how it evaluates out:

  %regmatch("%{member}","^b.*")       -> bob
  %regmatch("%{member}","^d.*")       -> dave
  %regmatch("%{member}","e")          -> dave
  %regmatch("%{member}","^e")         no value
  %regmatch("%{member}","^e.*","jim") -> jim
  %regmatch("%{member}",".*","%{cn}") -> group

=== regsub ===

  regsub(''EXPRESSION'',''PATTERN'',''TEMPLATE''[,''DEFAULT''])

Selects the value of ''EXPRESSION'' which matches the extended regular
expression ''PATTERN'' and uses ''TEMPLATE'' to build the result.  If no
values match, and a ''DEFAULT'' was specified, then ''DEFAULT'' is
evaluated as an expression and its value is produced, otherwise an error
occurs.

The template is treated as a literal value, but is allowed to
incorporate the n'th substring (one of the first nine) from the matched
value by including the sequence "%n" in the template.

Here's an example entry:

  dn: cn=group
  member: bob
  member: dave

And here's how it evaluates out:

  %regsub("%{member}","o","%0")          -> bob
  %regsub("%{member}","o","%1")          -> 
  %regsub("%{member}","^o","%0")         no value
  %regsub("%{member}","^d(.).*","%1")    -> a
  %regsub("%{member}","^(.*)e","t%1y")   -> tdavy
  %regsub("%{member}","^o","%0","jim")   -> jim
  %regsub("%{member}","^o","%0","%{cn}") -> group

=== deref ===

  deref(''THISATTRIBUTE'',''THATATTRIBUTE'')

Returns a list of the values of ''THATATTRIBUTE'' for directory entries
named by this entry's ''THISATTRIBUTE''.  Its function is similar in
principle to the indirect CoS functionality provided by the directory
server.

Here are some example entries:

  dn: cn=group
  member: uid=bob
  member: uid=pete

  dn: uid=bob
  uid: bob

  dn: uid=pete
  uid: pete

And here's how various expressions evaluate for ''cn=group'':

  %deref("member","foo") -> no values
  %deref("member","uid") -> (bob,pete)

Because the plugin attempts to track updates, the ''THISATTRIBUTE''
attribute should be indexed.

=== deref_r ===

  deref_r(''ATTRIBUTE''[,''OTHERATTRIBUTE''[...]],''VALUEATTRIBUTE'')

Looks for entries named by this entry's ''ATTRIBUTE'', and then by those
entries' ''ATTRIBUTE'', repeating the search until there are no more
entries to find named by ''ATTRIBUTE'' in the set of entries seen.

Taking that set as a new starting point, searches for entries named by
that set's ''OTHERATTRIBUTE'' values, similarly repeating until a new
complete set of entries is determined.  The process continues to be
repeated for each listed attribute except the last.

When the final set of entries is determined, their ''VALUEATTRIBUTE''
values will be used to construct the result list.

Here are some example entries:

  dn: cn=group
  member: cn=othergroup
  member: uid=bob
  includedgroup: clan=macleod

  dn: cn=othergroup
  member: uid=pete
  uid: bogus

  dn: uid=bob
  uid: bob

  dn: uid=pete
  uid: pete

  dn: clan=macleod
  includedgroup: cn=foundlings

  dn: cn=foundlings
  member: uid=cmacleod
  member: uid=dmacleod

  dn: uid=cmacleod
  uid: cmacleod

  dn: uid=dmacleod
  uid: dmacleod

And here's how various expressions evaluate for ''cn=group'':

  %deref_r("member","foo") -> no values
  %deref_r("member","uid") -> (bogus,bob,pete)

When evaluating the first attribute, the ''member'' attribute of
''cn=group'' produces this set of entries:

    * cn=group (the original entry)
    * cn=othergroup (added because it was named by ''cn=group'')
    * uid=bob (added because it was named by ''cn=group'')
    * uid=pete (added because it was named by ''cn=othergroup'')

The result list is pulled from this set of entries.

Here's another example:

  %deref_r("includedgroup","member","uid") -> (bogus,bob,cmacleod,dmacleod,pete)

When evaluating the first attribute, the ''includedgroup'' attribute
of ''cn=group'' leads to this set of entries:

    * cn=group (the original entry)
    * clan=macleod (named by cn=group)
    * cn=foundlings (named by clan=macleod)

When evaluating the second attribute, the ''member'' attribute values
for the previous set of entries produces this set of entries:

    * cn=othergroup (named by cn=group)
    * uid=bob (named by cn=group)
    * uid=cmacleod (named by cn=foundlings)
    * uid=dmacleod (named by cn=foundlings)
    * uid=pete (named by cn=othergroup)

The result list is pulled from this set of entries.

Because the plugin attempts to track updates, every attribute used here
(except for ''VALUEATTRIBUTE'') should be indexed.

=== referred ===

  referred(''SET'',''THATATTRIBUTE'',''THATOTHERATTRIBUTE'')

Creates a separated list of the values of ''THATOTHERATTRIBUTE'' stored
in directory entries which have entries in the current group in the
named ''SET'' and which also have this entry's name as a value for
''THATATTRIBUTE''.

Here are some example entries:

  dn: cn=group

  dn: uid=bob
  memberof: cn=group
  uid: bob

  dn: uid=pete
  memberof: cn=group
  uid: pete

And here's how various expressions evaluate for ''cn=group'', if
''uid=bob'' and ''uid=pete'' are both part of a set of entries named
''SET'':

  %referred("SET","memberof","foo") -> no values
  %referred("SET","memberof","uid") -> (bob,pete)

Because the plugin performs searches internally, ''THATATTRIBUTE''
should be indexed.

=== referred_r ===

  referred_r(''SET'',''ATTRIBUTE''[,''OTHERSET'',''OTHERATTRIBUTE''[,...],''VALUEATTRIBUTE'')

Searches for entries in the current group in both the current set and
''SET'' which refer to this entry using ''ATTRIBUTE'', then searches for
entries which refer to any of the just-found entries, repeating the
process until a complete set of referring entries is formed.

Then, searches for entries in ''SET'' and ''OTHERSET'' which refer to
entries in the just-found set, repeating the process until a new
complete set is formed.

The value of ''VALUEATTRIBUTE'' for every entry encountered along the
way will be returned.

Here are some example entries:

  dn: cn=group

  dn: cn=othergroup
  memberOf: cn=group

  dn: uid=bob
  uid: bob
  memberOf: cn=group

  dn: uid=pete
  uid: pete
  memberOf: cn=othergroup

And here's how various expressions evaluate for ''cn=group'', if all of
the entries with ''uid'' values are in a set named ''people'':

  %referred("people","memberof","foo") -> no values
  %referred("people","memberof","uid") -> (bob,pete)

Because the plugin performs searches internally, every attribute used
here (except for ''VALUEATTRIBUTE'') should be indexed.

=== merge ===

  merge(''SEPARATOR'',''EXPRESSION''[,...])

Evaluates and then creates a single value using multiple expressions
which may evaluate to either single values or lists.  Any expressions
which cannot be evaluated are ignored.

Here are some example entries:

  dn: cn=group
  membername: jim
  member: uid=bob
  member: uid=pete

  dn: uid=bob
  uid: bob

  dn: uid=pete
  uid: pete

And here's how an example expression evaluates for ''cn=group'':

  %merge(":","%{membername}","%deref(\"member\",\"uid\")") -> jim:bob:pete
  %merge(":","%{madeup}") -> (empty string)

=== collect ===

  collect(''EXPRESSION''[,...])

Evaluates each ''EXPRESSION'' in turn, creating one large list by
appending to it all of the values which are produced by evaluating every
expression.  Any expressions which cannot be evaluated are ignored.

Here's an example entry:

  dn: cn=group
  cn: group
  membername: jim
  member: uid=bob
  member: uid=pete

And here's how some example expressions evaluate for ''cn=group'':

  %collect("%{bogus}","%{member}","%{membername}") -> (uid=bob,uid=pete,jim)

=== link ===

  link(''EXPRESSION'',''PAD''[,''SEPARATOR'',''EXPRESSION2'',''PAD2''[,...])

Evaluates each ''EXPRESSION'' in turn to produce a list of values.  If
the lists produced by each of the expressions are not of the same
length, then each ''EXPRESSION'''s corresponding ''PAD'' value is
appended to each list, padding them out until they are all of equal
length (i.e., the length of the longest list).

Then, one list of values is produced by using the first value from each
list (separated by the corresponding SEPARATOR), then using the second
values from each list, continuing until all lists have been exhausted.

Here's an example entry:

  dn: cn=group
  cn: group
  membername: jim
  member: uid=bob
  member: uid=pete

And here's how an example expression evaluates for ''cn=group'':

  %link("%{member}","?","/","%{membername}","?") -> (uid=bob/jim,uid=pete/?)

=== ifeq ===

  ifeq(''ATTRIBUTE'',''EXPRESSION'',''MATCH-EXPRESSION'',''NONMATCH-EXPRESSION'')

Evaluates ''EXPRESSION'', and if the entry's ''ATTRIBUTE'' attribute
matches the value provided by the expression (as determined by the
server's matching rules for the attribute), evaluates and returns
''MATCH-EXPRESSION'', otherwise ''NONMATCH-EXPRESSION'' will be
evaluated and returned.

Here's an example entry:

  dn: cn=group
  cn: group
  membername: jim
  member: uid=bob
  member: uid=pete

And here's how an example expression evaluates for ''cn=group'':

  %ifeq("member","jim","","%{membername}") -> (jim)
