{= include("docs.util"); start_docs_page("The Configuration Language"); =}

Salang is the programming $PRODUCT_NAME language, which is used throughout $PRODUCT_NAME for many purposes from displaying pages like this one, including administrative or statistics pages, to writing log filters.

Salang is similar in some ways to perl, and borrows syntactic elements from perl, C, and other languages. Because Salang code is intended to be written inline, rather than as a stand-alone program, it deviates from perl and C in various ways, including the use of parentheses instead of brackets to group statements and expressions. Programmers acquainted with perl or C will find themselves in familiar territory with Salang.

Operators

OperatorPurpose
==Compares two numbers; true if they are equal; e.g. 1 == 1 is true.
!=Compares two numbers; true if they are not equal; e.g. 1 != 1 is false.
<=Compares two numbers; true if the left number is less than or equal to the right; e.g. 1 <= 2 is true, and so is 1 <= 1.
>=Compares two numbers; true if the left number is greater than or equal to the right; e.g. 2 >= 1 is true, and so is 1 >= 1.
<Compares two numbers; true if the left number is less than the right; e.g. 1 < 2 is true, but 1 < 1 is false.
>Compares two numbers; true if the left number is greater than the right; e.g. 2 > 1 is true, but 1 > 1 is false.
eqCompares two strings; true if they are equal; e.g. "a" eq "a" is true.
neCompares two strings; true if they are not equal; e.g. "a" ne "a" is false.
leCompares two strings; true if the left string is lexically less than or equal to the right; e.g. "a" le "b" is true, and so is "a" le "a".
geCompares two strings; true if the left string is lexically greater than or equal to the right; e.g. "b" ge "a" is true, and so is "a" ge "a".
ltCompares two strings; true if the left string is lexically less than the right; e.g. "a" lt "b" is true, but "a" lt "a" is false.
gtCompares two strings; true if the left string is lexically greater than the right; e.g. "b" gt "a" is true, but "a" gt "a" is false.
orTrue if either left or right values, or both, are true; e.g. true or true is true; true or false is true.
andTrue if both left and right values are true; e.g. true and true is true; true and false is false.
+Adds the right value to the left value; e.g. 1+2 is 3.
-Subtracts the right value from the left value; e.g. 2-1 is 1.
*Multiplies the right value and the left value; e.g. 2*3 is 6.
%Modulo (division remainder) operation on the left value by the right value; e.g. 5%2 is 1 and 6%2 is 0.
/Divides the left value by the right value; e.g. 12/4 is 3.
+=Adds the right value numerically to the left variable; e.g. x += 1 adds 1 to x.
-=Subtracts the right value numerically from the left variable; e.g. x -= 1 subtracts 1 from x.
++Adds 1 numerically to the left variable; e.g. x++ adds 1 to x.
--Subtracts 1 numerically from the left variable; e.g. x-- subtracts 1 from x.
.Concatenates the right string to the end of the left string; e.g. "a"."b" is "ab".
.=Concatenates the right value to the left variable; e.g. x .= "X" concatenates "X" to the end of x.
=Assigns the right hand side to the left hand side; e.g. x = 1 assigns a value of 1 to the variable x.
!Performs a boolean negation ("not" operation) of its unary parameter; e.g. !true is false, and !false is true.
notSame as !
matchesTrue if the left value matches the wildcard pattern specified by the right value.
matches_regexpTrue if the left value matches the regular expression specified by the right value.
withinUsed in report filtering; true if the value of the database field specified by the left value is within the value specified by the right value; e.g. page within '/directory/'. The value must exactly match; if you want to match a substring, or use wildcards, use "matches" instead.
session containsUsed in report filtering; selects only sessions where the right value matches the session page field of some event in that session (as a wildcard expression). There is no left value, so the syntax is e.g., "(session contains '*/thanks.html')".
%24Treats its unary string parameter as a variable name, and evaluates the value of the variable; e.g. if the value of the variable named "somevariable" is 1, then the value of the expression %24("somevariable") is 1. Important: This uses the value of the expression immediately after it as the name of the variable, so if variable x has value "valueX" then %24x means the same as %24("valueX"); i.e. it is the value of the variable valueX, not the value of the variable x. To get the value of the variable x, just use x, not %24x.

Quotes and escapes

Single quotes ('), double quotes (") and backticks (`) can be used as quotes. Quotes are "smart"--you can use one type of quotes within another type of quotes, e.g. v = "someone's value". If you need to use the same types of quotes, you can use a backslash, e.g. v = 'someone\'s value'. Backslashes can be used to escape other characters, including dollars signs in values, to prevent them from being treated as variable evaluation operators.

The Configuration Hierarchy, and Configuration Nodes

Variables and other data are stored in the configuration hierarchy, a collection of data which exists on disk and partially in memory. The configuration hierarchy is a group of "nodes", each with a single value. Any node can also contain other nodes (subnodes). Each node has a type ("bool", "int", "float", "string", or "node"), and a value. Salang expressions can get values from the hierarchy (e.g. "internal.verbose" is an expression which gets the value of the "verbose" subnode of the "internal" node), or set them (e.g. "internal.verbose = 1;").

Configuration nodes are referenced using a dot-separated (.) sequence of names, which functions like a pathname. Configuration nodes are grouped hierchically either using sub$lang_stats.directories, or by using configuration group files (.cfg files). The .cfg files have additional hierarchical groupings within them, specified by curly-bracketed groups. For instance, the node name "rewrite_rules" refers to the rewrite_rules $lang_stats.directory of LogAnalysisInfo, and the node name "rewrite_rules.server_responses" refers to the server_responses.cfg file, in the rewrite_rules $lang_stats.directory of LogAnalysisInfo. The server_responses.cfg file looks like this:

  server_responses = {
    100 = {
      regexp = "100"
      result = "Continue"
    }
    ...
    200 = {
      regexp = "200"
      result = "Successful Transfer"
    }
    ...
  } # server_response

The series of three of dots (...) as shown above are ellipses and they indicate places where part of the file has been omitted for brevity, so there is a "100" node inside the server_responses node, and there is also a "200" node there. Therefore, the node name "rewrite_rules.server_responses.100" refers to the 100 node, and "rewrite_rules.server_responses.200" refers to the 200 node. Within the 100 node, there are two nodes, "regexp" and "result"; so "rewrite_rules.server_responses.100.regexp" refers to the regexp node in 100, and "rewrite_rules.server_responses.200.regexp" refers to the regexp node in 200.

Directories are treated equivalently to .cfg files in the configuration hierarchy. In both cases, they represent nodes, and can contain other nodes. In the case of directories, subnodes appear as subdirectories, or as .cfg files in the directory, or as configuration value files (.cfv files) in the directory; in the case of .cfg files, subnodes appear within the file using curly-bracket syntax, as above.

As mentioned above, every node has a value. In the example above, the value of rewrite_rules.server_responses.200.regexp is "100", and the value of rewrite_rules.server_responses.100.result is "Continue". Even group nodes like rewrite_rules.server_responses.100, rewrite_rules.server_responses, and rewrite_rules can have values, but their values are typically empty (""); a node has either a value, or subnodes.

Nodes can be used like variables, since they are variables. Nodes can be referred to by name, in log filters or other Salang code; for instance this:

  echo(rewrite_rules.server_responses.100.result)

will print the value "Continue" to the standard output stream. They can also be assigned values:

  rewrite_rules.server_responses.100.result = "Something Else"

Nodes are read from disk into memory automatically when they are accessed, and the in-memory version is used for the remainder of the process, and then discarded. Therefore, it is not generally necessary to read data from disk explicitly; instead, put it in a node and access it by name. In the example above, the value of rewrite_rules.server_responses.100.result will be temporarily changed to "Something Else"; only the in-memory version of rewrite_rules.server_responses.100.result will change, and the change will not be propagated to disk unless {=docs_function_link('save_node')=} is used to write out the changes back out to disk.

Configuration files are on-disk representations of nodes. They are rooted in the LogAnalysisInfo directory, so the contents of LogAnalysisInfo is the top of the node hierarchy. Not all files are nodes, however, only these are:

1. A directory is a node; the name of the node is the directory name, and its parent node is the node corresponding to its parent directory. For instance, LogAnalysisInfo/templates/admin is the node templates.admin.

2. A file whose name ends with .cfg ("ConFiguration Group") is a node; the name of the node is the filename without the .cfg extension, and it's parent node is the node corresponding to its parent directory. For instance, LogAnalysisInfo/profiles/myprofile.cfg is the node profiles.myprofile. The contents of a CFG file is parsed as CFG syntax (nested name=value pairs, with {} as the nesting characters), so a CFG file described a whole chunk of the hierarchy beneath the node it represents.

3. A file whose name ends with .cfga ("ConFiguration Group Addition") is a node addition. This is very similar to a .cfg file, but it is an add-on to a .cfg file. A .cfga file, which only has an effect if there is a corresponding .cfg file with the same name, is layered automatically over the .cfg file, effectively providing a way to add "diffs" to a CFG file without changing the original file. For instance, the file LogAnalysisInfo/preferences.cfg, which represents the node preferences, can be coupled with a file preferences.cfga which contains only certain changes (like a change to the server port); these changes then cause Sawmill to act as though they were in the original .cfg file.

4. A file whose name ends with.cfv ("ConFiguration Value") is a node; the name of the node is the filename without the .cfv extension, and its parent node is the node corresponding to its parent directory. Unlike a CFG file, whose content describes a hierarchy of nodes below the main node represented by the file, a CFV file contains the literal value of the node, and does not describe subnodes. So the value of a CFV file is not parsed, but is simply treated as a string value for the node it describes.

Configuration value files (.cfv files) are straight text files whose entire content is considered to be the string value of the node. For example, the file LogAnalysisInfo/somedir/somefile.cfv whose contents is "HELLO" could be referred to as somedir.somefile; the value of that node is "HELLO".

Salang has a built-in type of node that refers to a configuration node; it is a pointer to that node. You can get the value of the node using {=docs_function_link('node_value')=}. You can also get a particular subnode using {=docs_function_link('subnode_by_name')=}. You can check for the existence of a subnode using {=docs_function_link('subnode_exists')=} or use {=docs_function_link('node_exists')=} with the full nodename as the parameter.

When nodes are accessed by name, from Salang, and they are not found in memory, the directory structure described above is searched to see if they are present. If they are, they are automatically loaded into memory. It is therefore possible to treat on-disk nodes as though they were in memory, and refer to them without any explicit loading operation.

In addition to the LogAnalysisInfo directory, Salang also looks in other "search paths" to find nodes specified by name:

Report Filter Syntax

Filters used in reports take a special variant syntax that allows only certain operations. Subroutines as described below are not allowed, and only database field names are allowed as variables. Only strings are allowed as constants. The <, >, <=, and => operators are permitted for the date_time field only. The "within", "matches", and "matches_regexp" operators are permitted for any field. Expressions can be combined using "and", "or", and "not"; arbitrary parentheses are permitted to allow any combinations. No other syntax is permitted.

Log Filter Syntax

Log filters can use all syntax described on this page, and also support a few extra variables. Specifically, log filters can refer to log fields by name, so a reference to date_time in a log filter is a reference to the value of the date_time field in the log entry that is currently being processed. This can be used either to get or set values; e.g. "if (page eq '/index.html') then 'reject'" checks the current log entry's page field to see if it's "/index.html", and rejects the log entry if it is; and "page = '/index.html'" sets the page field of the current log entry to "/index.html".

Log filters can also use the special function current_log_line(), whose value is the entire current line of log data.

Types

Each configuration node has a type, which specifies how its data is stored internally. Possible types are:

Types are primarily useful for performance optimization. Salang is a weakly typed language, so it will allow any value to be assigned to any variable without warning or complaint; for instance assigning 134 to a string variable will result in "134" in the string variable, or assigning "134.5" to a float variable will result in the floating point value of 134.5 in the float variable. However, these types of conversions can be slow if they are performed many times, so if you know a variable is only going to hold and manipulate floating point values, you should use a float type rather than a string type.

The node type is particularly useful for performance optimization, since it prevents the need for node lookups; e.g. setting the value of a node explicitly with "a.b.c.d = 0;" requires a series of expensive lookups, as node "a" is looked up in the configuration root, and then "b" inside that, and then "c", and then "d"-- but if a node variable N already points to a.b.c.d, then "N = 0;" is a very fast operation that does the same thing. node variables are particularly useful for foreach loops, and functions like subnode_value(), where they can be used to iterate over all subnodes without requiring any performance intensive string-to-node lookups.

Statements

if A then B else C
This statement evaluates the A section; if the value of A is true, then the value of the entire statement is the B section; otherwise, the value of the statement is the C section.

subroutine(A(param1, param2, ..., paramN), B)
This statement defines a subroutine A with N parameters. The subroutine can be called with the statement "A(p1, p2, ..., pN)". The value of the subroutine call will be the value of B, with the parameters p1...pN plugged into the variables %24param1..%24paramN before B is evaluated. The value of a subroutine declaration is empty.

foreach I N B
This statement is used to iterate over the subnodes of a particular node in the Salang hierarchy. It iterates over all values of node N, setting I to the full nodepath each iteration, and then evaluating expression B. The value of this expression is the concatenation of the values of all the evaluations of B.

for (I; C; E)
This repeats an expression 0 or more times. The expression I is evaluated to initialize the loop. The expression C is evaluated, before each iteration, and if the result is false, then the iteration does not complete. If C is true, the E is evaluated. The value of this expression is the concatenation of the E values.

while (C) E;
This repeats an expression 0 or more times. The expression C is evaluated before each iteration, and if the result is true, then E is evaluated. This continues until C is false. The value of this expression is the concatenation of the E values.

next
This statement goes immediately to the next iteration of the immediately enclosing loop.

last
This statement immediately terminates execution of the enclosing loop, and continues execution after the end of the loop.

Built-in subroutines

get_file_type_from_url(string U)
The value of this statement is the file extension of the URL U; for example, get_file_type_from_url("http://something/file.gif") is "GIF".

get_log_field(string N)
Returns the value of the log field N, of the current log entry.

node_exists(string N)
Returns true if the node specified by the nodename N exists; false if the node does not exist.

node_name(node N)
Returns the name of the node N.

num_subnodes(node N)
Returns the number of subnodes of the node specified by nodename N.

subnode_by_number(node N, int I)
Returns the Ith subnode of node N.

subnode_by_name(node N, string M)
Returns the subnode of node N whose name is M. If there is no subnode by that name, it creates one.

subnode_exists(node N, string M)
Returns true if there is a subnode named M in node N, false otherwise.

set_subnode_value(node N, string M, anytype V)
Sets the subnode named M of node N to the value V. Creates the subnode if it does not exist.

node_value(node N)
Returns the value of node N.

set_node_value(node N, anytype V)
Sets the value of node N to V; no value is returned.

node_type(node N)
Returns the type of node N as a string : e.g., "int", "float", "bool", "string", or "node".

set_node_type(node N, string T)
Sets the type of node N to T: e.g., "int", "float", "bool", "string", or "node".

delete_node(node N)
Deletes the node specified by nodename N. If the node is a profile, this also deletes the profile file.

insert_node(node P, string N, int I)
Inserts the node specified by nodename N into the node specified by nodename P, so that N ends up as the Ith subnode of P (i.e., it inserts N into P at position I). N is removed from its current location, and moved to the new one, so for instance if N is a subnode of O before this is called, it will no longer be a subnode of O afterwards ; i.e., it moves N, rather than copying it.

clone_node(node original_node, string clone_nodepath)
This makes a clone of the node original_node, and puts it at the node specified by clone_nodepath (which is created if it doesn't exist). The original node is unchanged, and after completion, the clone is an exact replicate of the original, except the name (which is computed from clone_nodepath).

sort(node N, string M, bool E)
Sorts the subnodes of the node specified by nodename N. The subnodes are sorted in an order specified by method M. M is a string of the format "field:F,T,A", where F is a field name (the name of a subnode found in each subnode of N; use "value" to use the value of each subnode of N directly), T is "integer", "float", "alphabetical", or "chronological" (which determines the sort type), and A is "ascending" or "descending" (which determine the direction of sort). If E is true, then variables are expanded before sorting; if E is false, then the sort is done on the literal values, without variable expansion.

create_profile(string N)
This creates a new profile with name N. It pulls various variables from the node hierarchy to set up the profile, the nodes are set by the "create profile" interview.

format(anytype V, string T)
This formats the value V according to the format type T. T can be "integer", "page", "float", "two_digit_fixed", "bandwidth", "date_time", "hostname", "duration", "duration_compact", "duration_hhmmss", or "duration_milliseconds". If the first character of T is a % character, the result will be formatted as a double-precision floating point number using printf-style formatting, for example, '%.1f' will print the value in floating point format with one digit after the decimal place. If the format is "!Encryption:algorithm:direction", then return value will be an encrypted, if direction is "encrypt" or decrypted if direction is "decrypt" version of V, encrypted or decrypted using the algorithm specified by algorithm. Some possible algorithms are "SEA1", which stands for the Sawmill Encryption Algorithm 1, and Base64 (which supports only decryption, not encryption). If the format is "!encrypt" or "!decrypt", it uses an internal encryption algorithm to encrypt or decrypt a value, with the extra characteristic that if "!decrypt" is used on a string which was not encrypted with "!encrypt", it will return V.

image(string N, int W, int H)
The value of this subroutine is an HTML image tag which displays the image specified by filename N, with width W and height H. The image file should be in the temporary directory in CGI mode, or in the picts folder, which is in the WebServerRoot folder of the LogAnalysisInfo folder, in web server mode.

create_image(int width, int height)
This creates a new image canvas of the specified image and height, and returns the image ID for use in drawing functions. It returns the image ID for use in drawing functions.

allocate_image_color(string imageid, int red, int green, int blue)
This allocates a new color for use in drawing on image imageid (created by create_image()), with the specified red, green, and blue components (0 to 255). It returns the color value for use in drawing functions.

add_text_to_image(string imageid, string color, int x, int y, string text, string direction)
This draws text on image imageid (created by create_image()), with the specified color (created with allocate_image_color()) at the specified x and y position, in the directory specified by direction ("up" or "right"). The upper left corner of the text will be anchored at (x, y) when drawing horizontally; the lower left corner will be anchored there when drawing vertically.

add_line_to_image(string imageid, string color, int x1, int y1, int x2, int y2)
This draws a line on image imageid (created by create_image()), with the specified color (created with allocate_image_color()) from the point (x1, y1) to the point (x2, y2).

add_polygon_to_image(string imageid, string color, node points, bool filled)
This draws a polygon on image imageid (created by create_image()), with the specified color (created with allocate_image_color()). The vertices of the polygon are specified in order in the subnodes of points; each subnode must have an x and a y value specifying the location of that point. The polygon will be a single pixel border if filled is false, or filled if it is true.

add_rectangle_to_image(string imageid, string color, int xmin, int ymin, int xmax, int ymax, bool filled)
This draws a rectangle on image imageid (created by create_image()), with the specified color (created with allocate_image_color()) and minimum x/y dimensions. The rectangle will be a single pixel border if filled is false, or filled if it is true.

write_image_to_disk(string imageid, string pathname)
This writes the image imageid (created by create_image()) to disk as a GIF file, to the location specified by pathname.

img_src(string N)
The value of this subroutine is the value of an HTML source (src)r section of an image tag; i.e. it's intended to be used right after src= in an image tag. The image file should be in the temporary directory in CGI mode, or in the picts folder, which is in the WebServerRoot folder of the LogAnalysisInfo folder, in web server mode.

fileref(string F)
This converts a local filename from the WebServerRoot directory to a URL that refers to that file. The filename should be in partial URL syntax, e.g. "picts/somefile.gif" to refer to the file somefile.gif in the picts directory of WebServerRoot. If necessary, it may copy the file; e.g., in CGI mode to the temporary directory. All references to files in WebServerRoot should be done through this function to ensure correct functioning in CGI mode.

save_changes()
This causes any changes made to the configuration hierarchy since the last save to be saved to the disk version of the hierarchy.

save_node(node N)
This saves node N to disk. For instance, if a N is "somenode.somechild.somevar", this will be saved to a file called somevar.cfg, in the somechild $lang_stats.directory of the somenode $lang_stats.directory of the LogAnalysisInfo $lang_stats.directory. The format used is normal configuration format. This makes the node persistent, because any future access to that node; e.g., referring to somenode.somechild.somevar in an expression, even from a later process after the current one has exited, will automatically load the node value from disk. So by using save_node(), you can ensure that any changes made to that node will be a permanent part of the configuration hierarchy, and any values you have set will be available for use to all future processes and threads.

logout()
This logs the current user out, clearing the cookies that maintained their login, and takes them back to the login page, or the logout URL if one is specified.

expand(string M)
This expands variables and expressions in M, and returns the expanded value. For instance, expand("%24x") will return the value of the variable x, and expand("%24x > %24y") will return "12 > 10", if x is 12 and y is 10.

convert_escapes(string M)
This converts percent-sign escape sequences in M, converting %2520 to a space, and returns the converted value. For instance, expand("some%2520string") will return "some string".

hex_escape(string str)
This returns an escaped version of str where all "unusual" characters have been converted to "__HexEsc__XX", where XX is the hexadecimal code of the character. "Unusual" characters are all characters which are not letters, numbers, underbar (_), dash (-), period (.), forward slash (/), or colon (:).

set_active_profile(string profile_name)
This sets the active profile to profile_name. The specified profile will be searched when looking up variables; e.g. the variable database.fields.date_time.label, which would normally be invalid, if no profile were active, will be treated as a local nodename within the specified profile, and be resolved as though it were profiles. profile_name. database.fields.date_time.label.

debug_message(string M)
This converts variables and expressions in M, and displays the resulting value to the standard debug stream; i.e., to the console. This is useful for debugging Salang code.

echo(string M)
This outputs the string M, without any conversion, directly to the standard output stream. This is useful for debugging Salang code.

error(string M)
This throws an error exception, with error message M. The error will be reported through the standard $PRODUCT_NAME error reporting mechanism.

node_as_string(node N)
This converts the node N as a string value as you would see it in a profile file. The value of this expression is the string value of N.

node string_to_node(string s)
This converts the string s to a node, and returns the node. This is similar to writing the string to a .cfg file, and reading it in as a node. For instance, string_to_node("x = { a = 1 b = 2 }") will return a node whose name is x, which has two subnodes a and b, with values 1 and 2.

autodetect_log_format(node log_source, string result, string id)
This autodetects the log format from the log source log_source, and puts the result in the node specified by result. id is an identifier which appears in the task info node for this process, allowing another process to tap into the progress for this process. This is used internally by the profile creation wizard.

get_directory_contents(string P, string R)
This gets the contents of the directory at pathname P, and puts the result in the node specified by R. R will contain a subnode for each file or subdirectory, and within each subnode there will be a name node listing the name of the file or directory and an is_directory node which is true for a directory or false for a file; there will also be a size node if it's a file, listing the file size in bytes.

get_file_info(string P, string R)
This gets information about the file or directory at pathname P, and puts the result in the node specified by R. R.exists will be true or false depending on whether P exists; R.parent will be the full pathname of the parent of P; R.type will be "file" or "directory" depending on whether P is a file or directory; R.filename will be the filename or directory name of P.

file_exists(string P)
The value of this is true or false, depending on whether the file or directory at pathname P exists.

delete_file(string pathname)
This deletes the file whose location is specified by pathname.

delete_directory(string pathname)
This deletes the directory whose location is specified by pathname, including all subdirectories and files.

get_files_matching_log_source(node L, string R)
This gets a list of files matching the log source in node L, and puts the result in the node specified by R. R will contain a subnode for each matching file; the value of the subnode will be the filename. This is used by the interface to implement Show Matching Files.

length(string S)
The value of this expression is the length of the string S.

substr(string V, int S, int L)
The value of this expression is the substring of the string V, starting at index S and of length L. The L parameter is optional, and if it is omitted, the value of the expression is the substring of V starting at S and continuing to the end of V.

split(string s, string divider, string resultnode)
This splits the string s on the divider specified in divider, and puts the resulting sections into the node specified by resultnode. For instance, split("Hello,you,there", ",", "volatile.splitresult") will set volatile.splitresult.0 to "Hello", volatile.splitresult.1 to "you", and volatile.splitresult.2 to "there".

starts_with(string S, string T)
The value of this expression is true if the string S starts with the value of the string T.

ends_with(string S, string T)
The value of this expression is true if the string S ends with the value of the string T.

contains(string S, string T)
The value of this expression is true if the string S contains the value of the string T.

replace_all(string S, string T, string R)
The value of this expression is the value of S after all occurrences of T have been replaced with R.

replace_first(string S, string T, string R)
The value of this expression is the value of S after the first occurrence of T has been replaced with R. If T does not occur in S, the value of this expression is S.

replace_last(string S, string T, string R)
The value of this expression is the value of S after the last occurrence of T has been replaced with R. If T does not occur in S, the value of this expression is S.

lowercase(string S)
The value of this expression is the value of S after all uppercase letters have been converted to lowercase.

uppercase(string S)
The value of this expression is the value of S after all lowercase letters have been converted to uppercase.

convert_charset(string value, string fromcharset, string tocharset)
This converts the string value from the charset fromcharset to the charset tocharset. It returns the converted version of value. Charset names are as documented for the GNU iconv conversion utility.

convert_local_code_page_to_utf8(string value)
This converts the string value from the local code page (local operating system charset) to the UTF-8 charset. It returns the converted version of value.

convert_utf8_to_local_code_page(string value)
This converts the string value from the UTF-8 charset to the local code page (local operating system charset). It returns the converted version of value.

get_search_engine_info(string url)
This examines url to see if it matches any of the search engine rules in the search_engines.cfg file in LogAnalysisInfo. If it does, it sets volatile.search_engine and volatile.search_phrase to the matching search engine and search phrase.

get_user_agent_info(string user_agent)
This examines user_agent, which is an HTTP User-Agent field, to compute the web browser, operating system and spider. It puts these in volatile.web_browser, volatile.operating_system, and volatile.spider.

matches_regular_expression(string S, string R)
The value of this expression is true if the string S matches the regular expression R. If it matches, the variable %240 is set to the portion of S which was matched by R. Variables %241, %242, ... are set to the substrings of S which match the parenthesized subexpressions of R.

matches_wildcard_expression(string str, string wildcardexp)
The value of this expression is true if the string str matches the wildcard expression wildcardexp.

index(string S, string T, int start)
The value of this expression is the index (character position) of the substring T in the string S. The start parameter is optional, and if it is omitted, this searches from the beginning of the string; if start is specified, this searches from the specified character position in S. If T is not a substring of S, the value of this expression is -1.

last_index(string S, string T)
The value of this expression is the index (character position) of the final occurrence of substring T in the string S. If T is not a substring of S, the value of this expression is -1.

md5_digest(string S)
The value of this expression is the MD5 digest of the string S, as a 32-digit hexadecimal number.

get_license_info(node licensenode, node resultnode)
This looks at the $PRODUCT_NAME license key contained in licensenode, and extracts information about it, which it puts in the node pointed to by resultnode. The subnodes of the result are type, valid, valid64, valid65, addon, expiration, profiles, and users.

string create_trial_license()
This creates and returns a 30-day trial license key. Only one trial key can be generated per installation; after the first time, it will return an empty string.

node license_string_to_license_node(string license_string)
This converts a string format license key (license_string) into a node format license key. It returns if the node key is returned. If the string is not valid, the node will not contain a checksum, and will have valid=false as a subnode.

display_statistics_filters(node F, string D)
The value of this expression is HTML which describes the Filters in node F, for database field D. This is used internally by the statistics interface to display Filters.

query_one_db_item(node F, string R)
This queries the numerical database field totals for the Filters F, and puts the result in the node R.

query_db_for_view(node V, string R)
This queries the database for statistics view V, and puts the result in R. The format of R depends on the type of the view V. Also, if volatile.csv_export is true, this computes a CSV version of the query result, and puts it in volatile.csv_export_result.

query_db_calendar(string R)
This queries the database to compute calendar information (which days are in the database), and puts the result in R. R contains a numerical year node for each year, and within that a numerical month node for each month, and within that a numerical day node for each day in the database.

query_db_field_hierarchy(string dbfieldname, string nodename)
This queries the database to compute the hierarchy of the database field dbfieldname, and puts the result in nodename. The values of nodes in nodename match the values of items for that database field, and the hierarchical structure of nodename exactly matches the structure of the field.

dababase_itemnum_to_item(int dbfieldnum, int itemnum)
This returns the item value (item name) of item number itemnum in the database field dbfieldnum.

dababase_item_to_itemnum(int dbfieldnum, string item)
This returns the item number for the item item in the database field dbfieldnum.

db_item_has_subitems(string D, string I)
This checks whether item I of database field D has subitems in the current database. It returns true if it does, and false if it isn't (i.e. if it's a bottom-level item).

uniques(string filter, string dbfieldname, node result)
This gets the list of all values of the unique field specified by dbfieldname, for the filter set specified by the expression in filter, and sets a subnode of result to the value of the unique field, for each unique value. For instance, uniques("page within '/dir/'", "visitors", "v.uniques") will add a subnode of v.uniques for each visitor to the directory /dir/, and the value of the subnode is the IP or host of the visitor.

set_log_field(string N, string V)
This sets the value of the log field N of the current log entry to V.

include(node N)
This loads and processes the Salang code in the node specified by nodename N.

discard(anytype V)
The value of this expression is always empty (""). This is useful if you want to evaluate an expression V, but not use its value.

capitalize(string V)
This capitalizes the value V, using the capitalization rules in the language module.

pluralizes(string V)
This pluralizes the value V, using the pluralization rules in the language module.

char_to_ascii(string c)
This returns the integer ASCII code of the character c. c should be a one-character string.

ascii_to_char(int i)
This returns a one-character string containing the ASCII character whose code is i.

ip_address_to_int(string ipaddr)
This converts the IP address ipaddr to an integer (a 32-bit representation with 8 bits per octet).

convert_field_map(string F, string M)
This converts the value of the log field F in the current log line we're processing, using the map M. M is a |-divided list of A->B mappings; e.g., if M is "1->cat|2->dog|3->ferret", then a field value "2" will be converted to "dog".

collect_fields_using_regexp(string R, string F)
This matches the current log line we're processing against the regular expression R, and if it matches, it extracts the parenthesized values in R and puts them in the fields specified by F. F is a comma-separated list of fields, and should include *KEY*, which specifies the key of the collected log entry to modify. For example, if F is "page,*KEY*,date_time,host", the second parenthesized subexpression will be used as the key, and that key will specify which collected entry to modify; then the page, date_time, and host fields of that collected entry will be set to the first, third, and fourth parenthesized sections.

collect_listed_fields_using_regexp(string regexp, string divider, string separator, string field_names_map)
This matches the current log line we're processing against the regular expression regexp, and if it matches, it uses the first parenthesized section in regexp and uses that as the key, and uses the second parenthesized section and uses it as the name/values list. Then it uses that, and its other parameters, to do the same as collected_listed_fields().

collect_listed_fields(string key, string name_values_list, string divider, string separator, string field_names_map)

This extracts log field values from name_values_list, which is a list of name/values pairs, and puts them in the collected entry specified by key. Names and values are listed in name_value_list the format "name1separatorvalue1dividername2separatorvalue2dividername3separatorvalue3", e.g. pairs are separate from each other by the value of divider, and each name is separate from its value by the value of separator. In addition, field_names_map can be used to convert field names; field_names_map is a pipe-separated (|-separated) list of values like "fieldname=newfieldname", and if any extracted field matches a fieldname value from field_names_map, it will be put in the newfieldname log field. If there is no map, or if nothing matches, then values will be put in the log field specified by the field name.

accept_collected_entry_using_regexp(string R)
This matches the current log line we're processing against the regular expression R, and if it matches, it extracts the first parenthesized value in R; using that value as a key field, it accepts the log entry corresponding to that key (as created by collect_fields_using_regexp()) into the database.

set_collected_field(string key, string log_field_name, string set_value)
This sets the collected field log_field_name, in the collected log entry specified by key, to the value set_value.

get_collected_field(string key, string log_field_name)
This returns the value of the collected field log_field_name, in the collected log entry specified by key.

rekey_collected_entry(string F, string T)
This changes the key of the collected entry with key F so its key is T instead.

generate_report_id(string P, string A)
This creates a new report ID, which can be used to generate a report with generate_report(). P is the profile name, and A is a list of any configuration options that need to be changed from the defaults in the report. The value of this function is the generated report ID.

generate_report(string P, string I)
This generates the report with id I, for profile P. Generation occurs in a separate task, so this returns immediately, while report generation continues in the background. Calls to the functions below can tap into the status of the report, and the final generated report. The value of this function is empty.

get_progress_info(string taskid, string resultnode)
This acquires progress information for a running task with task ID taskid. It populates the node specified by resultnode with detailed progress information. If taskid is not a valid task or there is no progress available for that task, resultnode will have a subnode exist with value false. The value of this function is empty.

cached_report_exists(string P, string I)
This checks if the report from profile P with id I has been completely generated, and is now cached. The value of this function is true if the report is cached, and false if it is not cached (never generated or generation is in progress).

display_cached_report(string P, string I)
This displays the cached report from profile P with id I. This will fail if the report is not cached-- call cached_report_exists() to check. The value of this function is the complete HTML page of the report.

delete_cached_report(string P, string I)
This deletes a cached report from profile P with id I. Future calls to cached_report_exists() will be false until this is regenerated with generate_report(). The value of this expression is empty.

verify_mysql_connection(string H, string S, string U, string P)
This verifies that a connection can be made to the MySQL server with hostname H, socket S, username U, and password P.

get_database_info(string P, string N)
This gets various information about the database for profile P, and puts it in node N.

build_database(string P, string R)
This builds the database for profile P. If the database build is occurring as part of an attempt to view a report, the report ID should go in R; otherwise, R should be empty ("").

update_database(string P, string R)
This updates the database for profile P. If the database build is occurring as part of an attempt to view a report, the report ID should go in R; otherwise, R should be empty ("").

exec(string executable, node options, bool wait)
This runs the command-line program specified by executable, from the command line. If the executable option is an empty string, the main program executable is run (e.g. $PRODUCT_NAME runs itself from the command line). The options node contains the command line options; e.g., for each subnode of the options node, the value of that option (not the name, which is ignored) is a command line option. The wait option specified whether to wait for completion; if it is true, exec() will not return until the command is done; if it is false, exec() will return immediately, leaving the command running in the background. This returns the Process ID (PID) of the process if wait is false, or zero otherwise.

get_task_info(string N)
This gets various information about currently active tasks, and puts it in node N.

cancel_task(string task_id)
This cancels an active task with ID task_id. When this returns, the task has been cancelled.

sleep_milliseconds(int M)
This delays for M milliseconds before evaluating the next expression.

query_geoip(string ip, string parameter)
This looks up an IP address ip in the GeoIP database(s), and returns a parameter associated with it. Currently only the "range" parameter is supported, which returns the IP address range from the GeoIP Organization database.

save_session_changes()
This saves any changes to the configuration hierarchy to the sessions file, so they will carry over to future pages.

write_file(P, S)
This writes string S to a file at pathname P. If the file does not exist, it is created; if it exists, it is overwritten. P is a local pathname in LogAnalysisInfo, using / as pathname dividers; for instance, to write to a file test.html in WebServerRoot in LogAnalysisInfo, use "WebServerRoot/test.html".

read_file(P)
This reads the contents of the file at pathname P. It returns the contents of the file as a string. P is either a full pathname, or a pathname local to the $PRODUCT_NAME executable location, using / as pathname dividers; for instance, if your LogAnalysisInfo folder is in the same $lang_stats.directory as the $PRODUCT_NAME binary (which is typical), then to read from a file test.html in WebServerRoot in LogAnalysisInfo, use "LogAnalysisInfo/WebServerRoot/test.html".

send_email(string sender, string recipient, string message, string smtp_server)
This sends an email to sender from recipient, using SMTP server smtp_server. The "message" variable is the entire contents of the message, including mail headers.

now()
This returns the current time, as the number of seconds since January 1, 1970, Coordinated Universal Time, without including leap seconds.

normalize_date(string date, string format)
This computes the "normal" format (dd/mmm/yyyy) of the date in date, and returns the date in normal format. date is in the format specified by format, which may be any of the date formats in the list at {=docs_option_link('ldf')=}.

normalize_time(string time, string format)
This computes the "normal" format (dd/mmm/yyyy) of the time in time, and returns the time in normal format. time is in the format specified by format, which may be any of the time formats in the list at {=docs_option_link('ltf')=}.

date_time_to_epoc(string D)
This converts the string D, which is a date/time of the format 'dd/mmm/yyyy hh:mm:ss' (GMT) to an epoc time (seconds since January 1, 1970). It returns the epoc time.

epoc_to_date_time(int E)
This converts the integer E, which is a date/time value in EPOC format (seconds since January 1, 1970 GMT) to a string of the format 'dd/mmm/yyyy hh:mm:ss', in GMT. It returns string date/time.

epoc_to_local_date_time(int E)
This converts the integer E, which is a date/time value in EPOC format (seconds since January 1, 1970 GMT) to a string of the format 'dd/mmm/yyyy hh:mm:ss', in the local time zone. It returns string date/time.

date_time_duration(string D)
This computes the duration in minutes of the date_time unit value D, which is of the format 'dd/mmm/yyyy hh:mm:ss', where any value may be replaced by underbars to indicate the unit size, e.g. '01/Feb/2005 12:34:56' indicates a single second (so the value returned will be 1); '01/Feb/2005 12:34:__' indicates a single minute (so the value returned will be 60); '01/Feb/2005 12:__:__' indicates a single hour (so the value returned will be 3600); '01/Feb/2005 __:__:__' indicates a single day (so the value returned will be 86400); '__/Feb/2005 __:__:__' indicates a single month (so the value returned will be the duration of the month, in seconds); and '__/___/2005 __:__:__' indicates a single year (so the value returned will be the duration of the year, in seconds). The value of this format is used frequently in report filters; this function is useful for computing durations of filtered data.

clear_session_changes()
This clears any changes to the configuration hierarchy saved using save_session_changes(). I.e. this reverts to the last saved version of the configuration hierarchy saved using save_changes().

start_progress_meter_step(string O)
This starts a progress meter step with operation name O.

finish_progress_meter_step(string O)
This finishes a progress meter step with operation name O.

set_progress_meter_maximum(float M)
This sets the maximum progress meter value to M.

set_progress_meter_position(float M)
This sets the current position of the progress meter to P (out of a total value specified by set_progress_meter_maximum()). This also causes the progress meter to be updated if necessary; call this function regularly during long operations to ensure that progress occurs properly.

current_log_pathname()
Returns the pathname of the log file currently being processed (for log filters).

current_log_line()
Returns the entire current line of log data being processed (for log filters).

sin(float X)
This returns the sine of X.

cos(float X)
This returns the cosine of X.

tan(float X)
This returns the tangent of X.

asin(float X)
This returns the arc sine of X.

acos(float X)
This returns the arc cosine of X.

atan(float X)
This returns the arc tangent of X.

atan2(float X, float Y)
This returns the arc tangent of X/Y, using the signs of X and Y to compute the quadrant of the result.

compile(string expression)
This compiles the Salang expression expression, and returns a compiled version of it as a node.

evaluate(node compiled_node)
This evaluates a compiled node (return by compile()), and returns the value of the expression value.

{= end_docs_page() =}