THE REGISTRY OBJECT FOR MAX Nick Rothwell, 06/Jan/1998. [Updated 22/Jul/2001: compiled as fat binary, added support for floats as data. ] [Note: the README in the registry.hqx file is newer; this one is out-of-date. ] INTRODUCTION REGISTRY is a MAX external for storing configuration data in a structured manner. The concept came from the Windows 95/NT registry mechanism, but hopefully this external shares none of the latter's drawbacks. A registry resembles a heirarchical "coll". Registry objects are attached to text files. Several registry objects may be attached to the same file, in which case they operate over a shared, common data structure. The registry data object may be manipulated by messages passed to a registry, or the text file may be edited using an external text editor: the file format is quite simple. A registry file's data structure closely resembles a heirarchical file system or directory tree. Every registry object has its own "working directory" within this structure, and different working directories can be selected dynamically. The chief motivation for implementing the registry object was to provide a structured and disciplined mechanism for storing the configuration data of MAX patchers which in general can be complex, heirarchical, and feature many nested sub-patchers and bpatchers (in which the MAX preset objects do not operate). The heirarchical structure of a registry is intended to mirror the heirarchical structure of a MAX patcher, and the registry directory structure and syntax is such that components of a MAX patcher can attach themselves to registry subdirectories specified by parameter substitution (#1 and so on). A patcher might instance a number of library sub-patchers or bpatchers which themselves contain registry objects, but if the registries all reference the same file then the entire patcher's state can be saved and recalled from a single file which is simple and well-structured in format. INSTALLATION Place the object "registry" into MAX's search path. Since registries can only be created over files which already exist (see below), it is also useful to create an empty text file named "#1.reg" somewhere in the search path. (This assumes an extension of ".reg" for registry files.) This is so that library patchers whose first argument is the body of the registry filename will work, and can be tested on this file. USAGE The first, mandatory, argument to "registry" is the name of an existing text file. At present, the file must exist before a registry object can be created, and a registry object cannot be instructed to read a different file, or to write its data to a different file or location. When a registry object is created, it attaches to the data structure associated with that filename if any other registries over that file already exist; otherwise, it opens and parses the file. When the last registry over a file is deleted, the registry data structure is automatically written to the file (if it has changed since last saved). Further optional arguments, which can be symbols or integers, denote a _path_. A path is a sequence of symbols or numbers which are names of directories. The argument path, if any, denotes a chain of subdirectories from the registry's root directory. If the argument path does not exist, it is created by means of empty subdirectories. (Thus, typos in the argument list will result in junk directories being formed; these are best removed from the file using a text editor at a later stage.) A path may contain some special symbols. "/" denotes the root directory of the registry. "." means current directory. ".." means parent directory. "~" denotes the "home directory" of the registry instance: the directory described in the object arguments. These special symbols are not particularly useful in arguments (and "~" does not have any meaning) but are useful in messages. Paths are _relative_ to the current directory of the registry. An absolute path is one which begins with the symbol "/" or "~". (The argument path is also relative, to the registry's root directory, so a leading "/" has no effect.) Aside: "[" and "]" are also reserved symbols and should not be used as directory or entry names, for reasons which will soon become clear. DATA ENTRIES A registry directory is a mapping from keys (either symbols or integers) to lists of atoms. We do not support the kind of plural namespace exhibited by coll objects, where an entry can be keyed on both an integer and a symbol. A registry directory can also contain subdirectories, with the same key naming convention as entries (i.e. symbols or integers). The namespace for subdirectories is distinct from that for entries, so an entry and a subdirectory may have the same name. At present, registry entries are not ordered in any implicit sense (although, since keys may be integers, they may be considered ordered explicitly). INLETS AND OUTLETS A registry has two inlets. The rightmost takes numerical item selections from a menu. The leftmost takes commands. A registry has three outlets. The rightmost may be attached to a menu. The centre outlet outputs a result status (0 or 1) on all commands except "read", "write" and "print" (which are deferred). The leftmost is for data output. MESSAGES This is the registry command set. Since most of these commands manipulate the registry data structure, they effect all registries sharing the same file. store Store (a list of atoms, which must be symbols or integers) into the current directory with key . Any existing entry with that key will be deleted. Fail with zero status if the current directory is invalid (see below). retrieve Retrieve and output an entry to the leftmost outlet, prefixed by the symbol "data". An optional relative path specifies a directory other than the current directory of the registry. Fail with zero status if the directory specified by the path does not exist, or if the key is not found. delete Delete an entry. The path is treated as for "retrieve". Result status as for "retrieve". goto Change the current directory to , or fail (with zero status) if the directory path does not exist. makedir Create all directories to constitute ; existing directories are left untouched. Fail with zero status for an invalid path (such as ".." at root). deletedir Delete the directory tree whose root is the destination directory of . Will fail if attempting to delete the registry root, or if the path is invalid. read Read the registry file and reload the directory tree, losing any changes since the last "write". write Write out the directory tree to the text file. DIRECTORY STATUS A registry's current directory (and similarly, its home directory) can become _invalid_ at any time. This can happen if: * A "read" command is executed and the current directory no longer exists in the file; * A "deletedir" command is executed on a parent of the current directory. When this happens, the registry's current directory is marked as _invalid_. This renders most messages ineffective unless they specify an absolute path (starting with "/", or with "~" if the home directory is still valid). However, a registry maintains a record of its current and home directories, retaining each as a path. An invalid directory can be _restored_ by means of an appropriate "makedir" or a subsequent "read" which restores the appropriate directory structure. MENU DRIVING Registry objects contain facilities for driving a menu. The rationale for menu driving arises from a common application of presets (which registries are intended to replace). A preset is an array of settings for a collection of MAX objects; each entry in the preset restores a set of object settings. It is useful to use a menu to select preset entries; this allows preset entries to be named by text items in the menu itself. When a menu is attached to a registry, its entries are mapped to numeric keys in the registry's current directory, starting at zero. Keys which are symbols are ignored. Keys which are negative integers are ignored. Numerical keys greater than or equal to the menu's size are ignored. This numbering scheme (using entry keys) ensures that, if new entries are added or existing ones removed, any remaining entries keep their menu position. When a registry is driving a menu, it maintains a "current selection" (an integer) used implicitly by the menu-oriented commands. The registry will also refresh the menu when it considers it necessary, such as on a "read" or "goto" command; in such cases, the current selection is maintained if this makes sense. (It does for "read" if the entry is still there; it doesn't for "goto".) The entry _names_ (which appear in the menu) are not held in the data entries; instead, they are held in a subdirectory of the current directory (called ".names"). An entry with no name will appear in the menu as "". MENU COMMANDS m_setup Start driving a menu, of size , for the entries with keys from <0> to . The command "m_setup 0" disables menu driving. m_store Store into the current directory, keyed by the current menu selection. The name is unchanged. m_delete Delete the current menu selection and name. m_rename Use as the name of the selected item. In rightmost inlet: select an item from the menu, output as for the "retrieve" command. SHORT PATH SYNTAX In sub-patchers, it is likely to be inconvenient to parameterise over directory paths composed of multiple atoms. Registries therefore support a "condensed" syntax for specifying paths, resembling the UNIX syntax. The path components can be written as a single symbol, with the atoms separated by a delimiter. (Such a path *must* be a single symbol; multiple-atom paths are considered to present discrete directory names.) The delimiter "/" allows paths to be written in the expected UNIX-like syntax. As an example, the following paths are identical: a b c d a/b/c/d So are these: / a b /a/b This path syntax (left-to-right) allows a sub-patcher to be parameterised over the top portion of a directory. Thus a sub-patcher might contain registries with arguments #1/inputs #1/outputs so that it can be called with (for example) the argument #1 instanced as /projectName/omsInfo yielding the paths / projectName omsInfo inputs / projectName omsInfo outputs There is a second path syntax, using "!" instead of "/", which reads right-to-left. The following paths are identical: a b c d d!c!b!a So are these: / a b b!a! This path syntax allows a sub-patcher to be parameterised over the bottom portion of a directory. A sub-patcher might contain registries with arguments #1!inputs #1!outputs which can be called with #1 instanced as omsInfo!projectName! yielding the paths / inputs omsInfo projectName / outputs omsInfo projectName The choice of notation is dictated by the desired directory structure: whether related instances of sub-patchers should share a root directory structure (in which case, use "!") or whether they should work over identically-shaped leaf directory structures (in which case, use "/"). It is illegal to mix "/" and "!" in the same condensed path expression: such an expression would be ambiguous. In all cases, commands which take multiple-atom paths will also take a single condensed path symbol. There are some subtleties: the "store" message, which expects a single atom as the entry key, will take a condensed path as first argument instead. Both "store" and "delete" assume that the last element of the path is a key, not a subdirectory name. THE REGISTRY FILE FORMAT Registry text files are read and written using binbufs, so the lexical conventions should be familiar. Each entry is written as its key, followed by a comma, followed by the data atoms, followed by a semicolon. A subdirectory is written as the symbol "[", followed by an atom for the subdirectory name, then semicolon. All following items are interpreted as the contents of that subdirectory until an unmatched "]" symbol and semicolon. A cosmetic wrinkle: when a registry is written to its file, subdirectory lines are "indented" with "." symbols, so that the nesting of subdirectories is apparent by inspection. The dots are cosmetic only; leading dots on any line are ignored when the file is read in.