IRC Services Manual
6. Adding to and modifying Services
6-1. Writing modules
6-1-1. Overview
6-1-2. Module loading and
unloading
6-1-3. Inter-module dependencies
6-1-4. Callbacks
6-1-5. Handling messages from the network
6-1-6. Special types of modules
6-1-7. Compilation and installation
6-2. Callbacks provided by Services and standard modules
6-2-1. Services core callbacks
6-2-2. OperServ callbacks
6-2-3. NickServ callbacks
6-2-4. ChanServ callbacks
6-2-5. MemoServ callbacks
6-2-6. StatServ callbacks
6-2-7. HTTP server callbacks
6-3. Submitting additions or changes to Services
6-4. Coding guidelines for Services
Table of Contents
6-1. Writing modules
IRC Services 5.0 introduces a new "module" system allowing features to
be added to and removed from Services at will with no changes to the core
Services code.
Note that this section is not a complete "how-to" for writing modules;
please read the source code for existing modules for detailed information.
In particular, the "protocol/unreal" module
(modules/protocol/unreal.c) is extensively documented and is
intended to serve as an example of how modules should be written.
6-1-1. Overview
Services modules are based on, and in fact use the same system as,
standard dynamic-link libraries. Upon startup or reloading of the
configuration file, Services will load specified modules into memory and
execute each module's initialization function. This function should set up
any data structures necessary and register all callback functions needed
(see below). At shutdown, or when the module is otherwise unloaded, its
cleanup function will be called; this function should free all resources in
use and perform other necessary cleanup functions, or return an error if
the module cannot unload itself for some reason.
Module interaction with Services takes place primarily through the use
of callbacks. These are functions which the module registers with Services
to be called when a particular event occurs, such as a message arriving
from the remote server or a certain amount of time passing. Typically, a
module will register callbacks for events it is interested in in its
initialization routine, and remove them when the module is unloaded.
Modules are manipulated using the functions provided by
modules.c in the Services core. They are as followed:
-
Module *load_module(const char *modulename)
- Loads the given module and returns a pointer to a data structure
(Module *) for the module. The contents of this data
structure are private, and the pointer should only be considered as
a "handle" that indicates a particular module. If the module could
not be loaded, returns NULL. See section
6-1-2 below for details on the module loading process.
-
int unload_module(Module *module)
- Unloads the given module. Returns nonzero if the module was
successfully unloaded, zero if not. See section
6-1-2 below for details on the module unloading process.
-
Module *find_module(const char *modulename)
- Returns a handle for the module whose name is given by
"modulename". If no such module has been loaded,
returns NULL.
-
void use_module(Module *module)
- Increments the "use count" for the given module. A module whose
use count is nonzero is not permitted to be unloaded. This
function is typically called by modules which depend on other
modules to function properly; see section 6-1-3
for details on inter-module dependencies. A module may call
use_module() on itself only if the global variable
modules_allow_use_self is nonzero; otherwise, a "BUG"
message will be logged and the call will fail. Modules may never
call use_module() in such a way as to create a dependency
loop of two or more modules, regardless of the setting of
modules_allow_use_self (doing so will likewise cause a
"BUG" message to be logged).
-
void unuse_module(Module *module)
- The opposite of use_module(): decrements the "use count"
for the given module. As with use_module(), a module may
not call unuse_module() on itself unless the global
variable modules_allow_use_self is nonzero.
-
void *get_module_symbol(Module *module,
const char *symname)
- Looks up the value of the symbol "symname" in the given
module, and returns its value; if the symbol cannot be found,
returns NULL. If the symbol is not defined in the given
module but is defined by another module, the return value of this
function is undefined. If "module" is NULL, then
the main program and all modules are searched for the symbol, and
NULL is only returned if the symbol does not exist (if the
symbol is defined more than once, then a random one will be
returned).
Note that the "value" of a symbol as returned by this function
is the address of the symbol. In particular, using this function
to look up a variable in another module will return the address of
the variable, not not the value it actually contains; thus,
if a module declares
int intvar;
then code similar to the following should be used to access it:
int *p_intvar = get_module_symbol(mod, "intvar");
if (p_intvar)
printf("Value of `intvar' is %d\n", *p_intvar);
else
printf("Unable to resolve symbol `intvar'\n");
-
const char *get_module_name(Module *module)
- Returns the name of the given module. This is usully the path
used to load the module (passed to load_module()), but may
be different if the module defines a module_name symbol.
If NULL is passed for the "module" parameter,
returns the string "core".
-
MODULE_NAME
- This preprocessor macro is a shortcut for
get_module_name(module) (where "module" is
assumed to be a variable pointing to the current module's handle).
It can be used just like any other string constant or macro, except
that it cannot be inserted into literal string constants; in other
words,
printf("Module name: " MODULE_NAME "\n");
will not compile.
-
int register_callback(Module *module, const char *name)
- Registers a new callback list. The calling module should pass
its own module information structure (as passed to
init_module() or obtained from find_module()).
Returns an ID value, which is a non-negative integer.
-
int call_callback(Module *module, int id)
int call_callback_1(Module *module, int id, void *arg1)
int call_callback_2(Module *module, int id, void *arg1,
void *arg2)
int call_callback_3(Module *module, int id, void *arg1,
void *arg2, void *arg3)
int call_callback_4(Module *module, int id, void *arg1,
void *arg2, void *arg3, void *arg4)
int call_callback_5(Module *module, int id, void *arg1,
void *arg2, void *arg3, void *arg4, void *arg5)
- Calls each function in the given callback list, optionally
passing additional arguments to the callback function. If a
function in the list returns a nonzero value, that value is
returned immediately to the caller without calling any additional
callback functions. If all callback functions return zero (or
there are no functions to call), returns 0; returns -1 on error
(the given callback ID does not exist for the given module).
-
int unregister_callback(Module *module, int id)
- Deletes a callback list. Returns nonzero on success, zero on
failure (given callback does not exist).
-
int add_callback_pri(Module *module, const char *name,
callback_t callback, int priority)
- Adds the given callback function to the given list with the given
priority (a higher priority value means the function is called
sooner). The priority must be between CBPRI_MIN and CBPRI_MAX
inclusive. Returns nonzero on success, zero on failure (given
callback does not exist or priority out of range). An error will
not be returned if the same function is registered more than once,
but this behavior is not supported and may change in the future.
-
int add_callback(Module *module, const char *name,
callback_t callback)
- Adds the given callback function to the given list with priority
zero. This is a shortcut for
add_callback_pri(module,name,callback,0), and the two
forms are exactly equivalent.
-
int remove_callback(Module *module, const char *name,
callback_t callback)
- Removes the given callback function from the given list. Returns
nonzero on success, zero on failure (given callback does not
exist, or given function is not on callback list).
6-1-2. Module loading and unloading
Modules are loaded through the
load_module() function in
modules.c, which is called for each module listed in
ircservices.conf at Services startup (and when an OperServ
REHASH command is given after adding a new module to
ircservices.conf). load_module() takes one parameter,
the name of the module to load, and performs the following steps (assuming
dynamically-compiled modules on a system with the dlXXX() family
of functions):
- Calls dlopen() for the module (the filename is the module name
passed to load_module() with ".so" appended). If
dlopen() fails, load_module() returns
NULL.
- Creates a module information structure (Module *) for the
module and initializes it.
- Checks the module's version code against the version code of the
Services core; if the two do not match or a version number is not
present in the module, load_module() frees the module
information structure, calls dlclose() on the module and
returns NULL. The version code is declared as
int32 module_version = MODULE_VERSION_CODE;
and must be exported by every module.
- Parses the section of the modules.conf file for the module (if
any), using the configuration directives in
module_config[]. module_config[] must be defined
as an array of ConfigDirective elements, terminated by an
element with name == NULL. If any errors occur during
parsing, load_module() frees the module information
structure, calls dlclose() on the module and returns
NULL.
- Calls the module's init_module() function, passing the
allocated module information structure. This function should
return nonzero on success and zero on failure. If the function
returns failure, load_module() frees the module
information structure, calls dlclose() on the module and
returns NULL.
- Calls the load module callback,
passing the module pointer to each function in the callback list.
- Links the module information structure into the global module
list and returns it.
The unload_module()
function takes a pointer to the module structure (handle) for the module to
unload, and performs the following steps:
- Calls the module's exit_module() function. This function
takes a single parameter, `int shutdown', which is 0 for a
regular module unload and 1 if the program is shutting down. The
function should return nonzero on success and zero on failure; if
it returns failure, unload_module() returns zero (failure).
- Calls the unload module
callback, passing the module pointer to each function in the
callback list.
- Calls dlclose() for the module.
- Unlinks the module information structure from the global list and
returns nonzero (success).
6-1-3. Inter-module dependencies
Some modules may require functions from other modules to work correctly;
for example, a module adding a new command to NickServ would obviously
require NickServ itself to be loaded first. Such modules can use the
find_module() function from
modules.c to check whether the module has been loaded. An example
might look like this:
static Module *module_nickserv;
...
int init_module(Module *me)
{
module_nickserv = find_module("nickserv/main");
if (!module_nickserv)
return 0;
...
return 1;
}
find_module() returns a pointer to the module found, so this can
be saved and used in other function calls as needed.
Note that modules should not attempt to load other modules
themselves, as the administrator may have deliberately chosen not to
include such modules. Instead, the module should fail to load, optionally
writing a log message explaining the problem.
If symbols, such as exported functions, from another module are needed,
they can be looked up using the
get_module_symbol
function. This function takes a module pointer and a symbol name and
returns the address of the symbol (see the function documentation
for details). For example, to access the variable s_NickServ,
which contains the NickServ pseudoclient's nickname, from the
nickserv/main module, code such as the following could be used:
static char **s_NickServ_ptr;
#define s_NickServ (*s_NickServ_ptr)
...
int init_module(Module *me)
{
...
s_NickServ_ptr = get_module_symbol(module_nickserv, "s_NickServ");
if (!s_NickServ_ptr) {
module_log("Unable to resolve symbol `s_NickServ'");
return 0;
}
...
}
...
static void send_message(User *user)
{
notice(s_NickServ, user->nick, "message");
}
This code would retrieve the address of the s_NickServ variable
during module initialization, then dereference it to obtain the contents of
the variable (the nickname itself), using a macro to make the code easier
to read. (This macro would have to be defined after any #include
directives to avoid errors in header declarations.) If the symbol was not
found, the module would fail to load.
When using variables from other modules, be aware that while the
address of the variable will always remain constant, the
value of the variable may change at any time, so you should always
access the value by dereferencing a pointer to it, rather than by saving
the value locally. The macro used in the example above provides a way to
do this which still allows the symbol to be used as if it was an ordinary
variable. (However, in the example above the pointer variable is
guaranteed not to be NULL, because the module returns failure from
init_module() if the symbol is not found. If the module does not
do this or take other steps to ensure that the pointer is valid, the macro
would need to be modified to provide a default value in case the pointer is
NULL.)
A second, though not recommended, method of importing symbols from
other modules is to simply reference them as you would ordinary external
symbols. This will create an unresolved reference to the symbol in the
module object file, which will be resolved at load time. This approach
is slightly more efficient than using get_module_symbol(), since
there is no need to store or retrieve the symbol's address separately.
However, if the symbol is not found at load time, then load_module() will
log an error message and return failure without calling the module's
init_module() function, so there is no way for the module to
log a more informative error message or take other action. This method
should only be used when it can be assumed that the module being used will
always be loaded first; for example, the NickServ sub-modules (such as
nickserv/autojoin and nickserv/link) take this approach
with respect to the main NickServ module (nickserv/main). It is
still necessary to retrieve the module pointer using find_module()
if it is needed for other module-related function calls.
One thing to keep in mind, whichever method is used, is that modules can
only reference symbols in modules loaded earlier. If your module depends
on symbols from another, make certain you document that so that people who
use the module put it in the right place in ircservices.conf.
In certain cases, circular references may arise—a case where module
A depends on a symbol from module B, while module B depends on a symbol
from module A. In such a case, at least one of the modules must use
get_module_symbol() to access the other module's symbol, and must
do it after the module has been loaded (i.e. not failing in
init_module()), so that the other module has a chance to load as
well. This can be accomplished using the
load module callback in the
Services core, which allows the module to take action when subsequent
modules are loaded.
Finally, modules which export symbols to be used with other modules must
explicitly declare them to be exported, using the EXPORT_VAR(),
EXPORT_ARRAY(), or EXPORT_FUNC() macros in modules.h; for
example:
EXPORT_VAR(int,variable)
int variable = 123;
EXPORT_ARRAY(char,string)
char string[] = "Hello world";
EXPORT_FUNC(function)
int function(int x) { return x*x+x; }
These macros do not have any actual effect on compilation, but they are
used by a script which prepares the modules for static linking. Without
these declarations, modules will be unable to access any symbols in the
module when compiled statically. Note, however, that the
init_module() and exit_module() functions and the
module_version and module_config variables are
automatically exported, and should not be explicitly listed this way (in
fact, listing them will cause an error when compiling statically).
6-1-4. Callbacks
modules.c provides an interface for registering and calling
callback functions; these are functions which are called from a
separate module or function when a certain event occurs, such as a module
being loaded or a user connecting to IRC. Callbacks are referred to by a
combination of module and callback name or ID (callbacks in the Services
core use a module pointer of NULL).
The type of a callback function is defined by callback_t:
int (*)(...). The parameters passed to the function depend on the
particular callback. If a callback function desires to terminate processing
of the callback list, it should return a nonzero value; returning zero
will allow the next function in the list to be called. Any pointer
parameters should be considered "const" (unmodifiable) unless the
function processes the event and returns nonzero.
To add a callback function to a callback list registered by another
module (or a callback list defined by the Services core), use the
add_callback() function (or
add_callback_pri() if you
want to use a priority other than zero; priorities are used to determine
the order in which the functions are called, as described below). To
remove a function previously added to a callback list, use the
remove_callback() function.
It is legal to attempt to remove a callback function which was not added in
the first place, or to attempt to remove a callback function from a
nonexistent callback; remove_callback() will return failure in
these cases. Warning: if you forget to remove callbacks before or
when your module exits, Services will crash!
To create your own callback list, use the
register_callback()
function; to delete it, call the
unregister_callback()
function, passing in the callback ID returned from
register_callback(). To call a list of callback functions, use
the call_callback_X()
set of functions, where X is the number of parameters
passed to each callback function (the _X is omitted if no
parameters are passed). This syntax is used to help module creators
double-check that the right number of parameters are being passed to
callback functions, since such functions cannot be checked against
prototypes.
Callback functions are called in descending order of priority (so, for
example, a function with a priority of 1 is called before one with a
priority of 0); functions with the same priority are called in the same
order they were added to the callback list.
See section 6-2 below for a list of available
callbacks.
6-1-5. Handling messages from the network
Most modules will need to be able to process messages received from the
IRC network to perform any useful functions. There are four main ways of
doing this:
- Through one of the pseudoclients' "command"
callbacks. If your module adds functionality to one of the existing
pseudoclients (NickServ, ChanServ, etc.), you can hook into the
"command" callback provided by that pseudoclient to receive each
command (other than a CTCP PING) sent to the pseudoclient, and process the
commands that apply to you. You will probably also want to hook into the
"HELP" and "HELP COMMANDS" callbacks to provide help
information about the commands your module implements. See the section
below on the particular pseudoclient's callbacks for details.
- Through the m_privmsg
callback. This callback is designed for handling user interaction with
pseudoclients; if you hook into this callback, you will be able to check
each PRIVMSG (/msg) message received by Services and take
appropriate action. (Naturally, you should leave alone any PRIVMSG
addressed to a nickname your module doesn't use.)
- By registering a message list. This method is intended for
use by protocol modules to implement protocol-specific features, but can be
used by any module. Set up an array of Message structures (see
the messages.h file) terminated by an entry with name ==
NULL, then call register_messages(table) (where
table is the array of Message structures). When a
message with a name listed in your message table arrives, the routine given
in the func member associated with that name will be called with
the message source (char *) and an argument count (int)
and array (char **); if you specify NULL instead of a
function pointer, the message will be silently ignored. As with callbacks,
make certain you call unregister_messages() on your message table
when your module is unloaded, or Services will crash on the next message it
receives when it tries to access your (nonexistent) table.
Note that if a particular message is listed in more than one message
table, only the function in the most recently registered table will be
called; this allows you to override the default behavior for a particular
message. If you do this, however, be aware that there is no way to call
the previously registered function--if you want to allow the previous
handler to run, you will need to use the receive message callback,
discussed below.
- Through the receive
message callback. This is the lowest-level method of handling
messages; your callback function will be called for each message received
from the remote server, and you can decide whether to process the message
or pass it on.
6-1-6. Special types of modules
While callbacks are used for most inter-module interactions, to improve
performance, some types of modules are integrated more tightly with
Services. These include protocol modules, database modules, and mail
modules.
Protocol modules
Protocol modules allow Services to communicate with different types of
IRC servers. Services calls several routines in these modules directly;
each protocol module must set the following function pointers to point to
appropriate routines:
- void (*send_nick)(const char *nick, const char *user,
const char *host, const char *server,
const char *name, const char *modes)
- Sends an appropriate combination of
NICK/USER/MODE messages (or their
equivalents) to introduce a new nickname onto the network.
- void (*send_nickchange)(const char *nick,
const char *newnick)
- Sends a NICK message to change an existing user's nickname.
- void (*send_namechange)(const char *nick,
const char *newname)
- Sends a message to change an existing user's "real name", if
possible. (If the protocol does not support it, this can be a
do-nothing routine, but it must be defined.)
- void (*send_server)(void)
- Sends the initial server registration message (typically a
SERVER message) for Services itself.
- void (*send_server_remote)(const char *server,
const char *reason)
- Sends a server registration message for a "remote" server (used
for jupes).
- void (*wallops)(const char *source, const char *fmt,
...)
- Sends a message to "all IRC operators" (the definition of which
may vary between protocols). This is implemented as a
WALLOPS for RFC1459, but as a GLOBOPS for
DALnet-compatible servers, for example. This function takes
printf()-style parameters.
- void (*notice_all)(const char *source, const char *fmt,
...)
- Sends a notice to all users on the IRC network, typically using
"NOTICE $*" or equivalent (protocols which do not support
$* by itself may require a common domain name to be set in
the configuration file, as the RFC1459 and most other current
protocols do). This function takes printf()-style
parameters.
- void (*send_channel_cmd)(const char *source, const char *fmt,
...)
- Sends a command which modifies channel status. This is used
where "source" is a nickname introduced by Services (such
as "ChanServ"); some protocols do not allow arbitrary clients, even
those from Services, to change channel commands, so those protocols
would need to change "source" to "ServerName" or
take other measures to get the command recognized. If possible,
the command should be sent on as is, for example:
va_list args;
va_start(args, fmt);
vsend_cmd(source, fmt, args);
va_end(args);
This command takes printf()-style parameters.
- void (*send_nickchange_remote)(const char *nick,
const char *newnick)
- Forcibly changes a remote client's nickname. The client's current
nickname is given by "nick", and the nickname to change to
by "newnick". Note: This routine must not
be called if the PF_CHANGENICK flag is not set in
protocol_features (see below).
The two strings "pseudoclient_modes" and
"enforcer_modes" should also be set to the user modes which
pseudoclients and enforcers, respectively, should receive.
Furthermore, the module must set the following four variables (defined
in send.c) to appropriate values:
- const char *protocol_name
- The name of the protocol (e.g. RFC1459, Dreamforge, etc.)
- const char *protocol_version
- A string which identifies what versions of the protocol are
supported. If the protocol does not have "versions", the
variable should be set to the empty string ("").
- uint32 protocol_features
- A bitmask indicating what features (PF_* constants defined
in services.h) are supported by the protocol; if none of
the listed features are supported, the value should be 0.
- int protocol_nickmax
- The actual maximum length of a nickname, not including the
trailing null byte used when storing the string internally; for
example, RFC1459-compliant servers would use a value of 9 here.
The module should check that NICKMAX (defined in
defs.h) is strictly greater than this value, and refuse to
load if it is less or equal. (Remember that NICKMAX
includes the trailing null byte, so it must be at least 1 byte
greater than protocol_nickmax, which does not include the
trailing null.)
For examples of how protocol modules should be designed, see the Unreal
module (modules/protocol/unreal.c), which is fairly well
documented.
Database modules
Database modules provide the means for Services to store non-volatile data
(data which will be saved between runs), such as the nickname and channel
databases. These modules must provide the functions to open, read, write,
and close databases for each of the major Services module groups (OperServ,
NickServ, ChanServ, MemoServ, and StatServ).
Details of database modules are still in flux; to be defined later.
Mail modules
Mail modules provide the low-level functionality to actually send E-mail,
and are called by the high-level mail module (mail/main) when mail
is to be sent. When loaded (i.e. in the init_module() function),
mail modules must set the "low_send" symbol (exported from the
mail/main module) to point to a function with the following
prototype:
int low_send(const char *from, const char *fromname, const char *to,
const char *subject, const char *body)
"from" and "fromname" are the address and name,
respectively, to be used in the From: line; "to" is the
address for the To: line, "subject" is the text for the
Subject: line, and "body" is the body text. Except for
"body", none of the strings will contain newlines, and the
"from" and "to" strings will be valid E-mail addresses
(as determined by valid_email()). The function should return 0 on
success, -1 on failure.
6-1-7. Compilation and installation
In order to compile and install a new module, you should do the
following:
- Place your module in a new subdirectory under the modules
directory.
- Create a Makefile for your new subdirectory.
- Run make and make install (or gmake and
gmake install) as usual.
The Makefile in the modules directory will
automatically detect the presence of your new subdirectory and call its
Makefile as appropriate. Additionally, the
modules/Makerules file contains rules for compiling modules both
dynamically and statically, so in most cases you only need to set the
variables it needs (see the comments at the top of that file) and add
"include ../Makerules" at the end of your Makefile. See
modules/mail/Makefile for a simple example of a module
Makefile, or modules/protocol/Makefile for a more
complex example.
Once your module has been successfully compiled and installed, you can
load it into Services with a LoadModule directive in
ircservices.conf, just as with any of the standard modules, and
you can add configuration directives for your new module in
modules.conf by enclosing them inside Module and
EndModule:
Module directory/module-name
(configuration directives go here)
EndModule
Back to top
6-2. Callbacks provided by Services and standard modules
The callbacks provided by Services are listed below under the names used
to access them (e.g., with
add_callback()). When using
Services core callbacks, use a module parameter of NULL.
Note that where the descriptions below refer to "processing" a message,
they refer to a message that is meant specifically for the callback in
question and which is not to be passed on to other modules or the Services
core.
Except as otherwise specified, callbacks which return zero must leave
all arguments unchanged, and callbacks which return nonzero may modify any
arguments which are not specified as const.
6-2-1. Services core callbacks
-
channel create
Parameters: Channel *channel, User *user,
int32 mode
- Called when a channel is created (i.e. a user joins a nonexistent
channel). The user that created the channel and that user's modes
(CUMODE_*) are also passed to the callback (note that the user is
not yet in the channel's user list). The callback routine should
always return 0.
-
channel delete
Parameters: Channel *channel
- Called when a channel is about to be deleted (i.e. the last user
has left the channel). The callback routine should always return 0.
-
channel JOIN
Parameters: Channel *channel,
struct c_userlist *u
- Called when a user has joined a channel. The callback routine
should always return 0.
-
channel JOIN check
Parameters: const char *channel, User *user
- Called when a user is about to join a channel. The callback
routine should return 1 to refuse the user access to the channel
or 0 to allow the JOIN to proceed normally.
-
channel KICK
Parameters: Channel *channel, User *user,
const char *reason
- Called when a user has been kicked from a channel. The callback
routine should always return 0.
-
channel MODE
Parameters: const char *source,
Channel *channel, int modechar,
int add, char **av
- Called for each valid mode character in a MODE message,
except for channel user modes (+o, +v, etc.).
add is nonzero if adding modes, zero if removing
them. av" points to the first argument for the
mode; there are guaranteed to be enough arguments for the mode
character (provided the number of parameters in the mode definition
is set correctly). The callback routine should return 1 if it
processes the mode, 0 otherwise.
-
channel mode change
Parameters: Channel *channel
- Called when a channel's modes have changed. The callback routine
should always return 0.
-
channel umode change
Parameters: Channel *channel,
struct c_userlist *user
- Called when a user's modes on a channel have changed. The callback
routine should always return 0.
-
channel PART
Parameters: Channel *channel, User *user,
const char *reason
- Called when a user parts from a channel. The callback routine
should always return 0.
-
channel TOPIC
Parameters: Channel *channel, const char *topic,
const char *setter
time_t topic_time
- Called when a TOPIC command is received. The callback
routine should return 0 to permit the topic change or 1 to refuse
it. Note that setter may be in the form
"nick!user@host" instead of just a
nickname, depending on the remote server type; however, Services
currently stores only the nickname, and discards the username and
hostname if present.
-
clear channel
Parameters: const char *source,
Channel *channel, int what,
const void *param
- Called whenever the clear_channel() function (in
actions.c) is called, before any other action is taken.
The callback routine should return 1 to terminate
clear_channel() processing or 0 to continue. Note that
even if the callback routine returns 1, clear_channel()
will still flush out any channel mode changes, so the routine does
not need to do this itself. See the comments above
clear_channel() in actions.c for the meaning of
the parameters.
-
command line
Parameters: char *option, char *value
- Called at program startup for each command-line option.
option is the name of the option (the part of the
option string after the initial "-", up to and not
including any "="), and value" is the text
after the "=" (NULL if no "=" is present).
option is guaranteed to be non-NULL. The
callback routine should return 0 if it does not recognize the
option, 1 if it successfully processes the option, 2 if there is an
error processing the option, or 3 if no error occurred but Services
should terminate successfully.
-
connect
Parameters: none
- Called when a connection to the remote server is established.
Modules that want to send data to the remote server on startup
should use this callback; attempting to send data to the server
before the connection is established will cause the data to be
lost. The callback routine should always return 0.
-
expire
Parameters: none
- Called whenever the expire timeout (for expiring nicknames, etc.)
runs out, or another action, such as OperServ UPDATE,
explicitly causes expiration to occur. The callback routine should
always return 0.
-
introduce_user
Parameters: char *nick
- Called whenever Services may need to introduce a pseudoclient (at
startup and when receiving a KILL message). The
nick parameter is the nickname of the pseudoclient
which should be introduced (if it exists), or NULL when
introducing all pseudoclients. The callback routine should return
1 if nick is not NULL and the routine
introduces a pseudoclient, 0 otherwise. (If nick is
NULL, the routine should introduce any pseudoclients
necessary and return 0.)
-
load module
Parameters: Module *module,
const char *modname
- Called when a module is loaded, after its init_module()
routine has been called. modname is the name of
the module, either specified by the module (char
module_name[]) or copied from the module's path. The callback
routine should always return 0. This callback may be used, for
example, to add callback routines to a new module's callbacks when
the module is loaded.
-
m_privmsg
Parameters: char *source, char *target,
char *message
- Called for every PRIVMSG which is not ignored by Services.
The callback routine should return 1 if it processes the message,
0 otherwise.
-
m_whois
Parameters: char *source, char *target,
char *target_server
- Called for every WHOIS command. The callback routine
should return 1 if it processes the message, 0 otherwise.
-
receive message
Parameters: char *source, char *cmd,
int ac, char **av
- Called for every message received from the uplink server. The
callback routine should return 1 if it processes the message, 0
otherwise. Note that it is acceptable for a callback routine to
modify the parameters passed and return 0 to allow the next routine
to continue, subject to the following constraints:
- The number of message parameters (ac) is not
changed.
- None of the strings (source, cmd,
or any element of av) are lengthened.
(Shortening strings is allowed.)
-
reconfigure
Parameters: int after_configure
- Called twice when Services is signalled to re-read its
configuration files. The first call is made before any module
configuration data is changed (but after the main Services
configuration settings have been updated), with
after_configure set to 0; the second call is made after
all configuration data has been successfully read and configuration
variables have been updated, with after_configure set to
1. Callback routines should always return 0.
-
save data
Parameters: none
- Called whenever the save-data timeout runs out, or another action,
such as OperServ UPDATE, explicitly causes data saving to
occur. The callback routine should always return 0.
-
save data complete
Parameters: int successful
- Called when a save-data operation completes. The
successful parameter indicates whether the
operation succeeded or not. The callback routine should always
return 0.
-
server create
Parameters: Server *server
- Called whenever a new server joins the network, after the server
record is created. The callback routine should always return 0.
-
server delete
Parameters: Server *server,
const char *reason
- Called whenever a server splits from the network, before the server
record is deleted. The callback routine should always return 0.
-
set topic
Parameters: const char *source, Channel *c,
const char *topic, const char *setter,
time_t t
- Called twice whenever set_topic() (in actions.c)
is called to set the topic of a channel from Services. Parameters
are as for set_topic() (in actions.c), except as
noted below; source will always be non-NULL. The
first call is done before the channel structure is modified, and
all callback routines must return 0 in this case. The second call
is done after the channel structure is modified, but before
c->topic_time is changed; in this call,
topic and setter are both
NULL. If the callback routine returns 1, no further
processing is done. This is currently used to ensure that the
topic time is set so that the IRC servers will acknowledge the
change under certain protocols.
-
unload module
Parameters: Module *module
- Called when a module is unloaded, after its exit_module()
routine has been called. The callback routine should always return
0. Note that it is not necessary to use this callback to remove
callbacks from a module which is being unloaded, since those
callback entries will be removed automatically.
-
user check
Parameters: int ac, char **av
- Called whenever a user joins the network, before the user record
is created. ac and av are the
argument list to the NICK (or USER) command, as
passed to users.c/do_nick(). The callback routine should
return 0 to allow the user to connect or 1 to disallow (in this
case the routine should take action to remove the user from the
network, such as sending a KILL message).
-
user create
Parameters: User *user, int ac,
char **av
- Called whenever a user joins the network, after the user record
is created. ac and av are the
argument list to the NICK (or USER) command, as
passed to users.c/do_nick(). The callback routine should
always return 0.
-
user delete
Parameters: User *user, char *reason,
int is_kill
- Called whenever a user leaves the network (whether by QUIT
or KILL), before the user record is deleted.
is_kill is 1 if the user is quitting because of a
KILL, 0 if because of a QUIT. The callback
routine should always return 0.
-
user MODE
Parameters: User *user, int modechar,
int add, char **av
- Called for every mode character given in a user MODE
message. add is nonzero when adding modes and zero
when subtracting. av points to the first argument
for the mode; there are guaranteed to be enough arguments for the
mode character (provided the number of parameters in the mode
definition is set correctly). The callback routine should return 1
if it processes the mode, 0 otherwise.
-
user nickchange (after)
Parameters: User *user, const char *oldnick
- Called whenever a user changes nicks, after the nickname is
actually changed. The callback routine should return 1 if it
kills the user, 0 otherwise.
-
user nickchange (before)
Parameters: User *user, const char *newnick
- Called whenever a user changes nicks, before the nickname is
actually changed. The callback routine should always return 0.
Note that users should never be killed within this callback; use
the "user nickchange (after)" callback for that.
-
user servicestamp change
Parameters: User *user
- Called when a user's Services stamp (the servicestamp
field) is altered and the network should be informed of it. The
callback routine should always return 0.
6-2-2. OperServ callbacks
-
command
Parameters: User *user, char *command
- Called for every message (other than a CTCP PING or empty
message) received by OperServ. command is the
first word in the message (where words are separated by spaces);
the remaining text can be obtained through repeated calls to
strtok() with a NULL first parameter. The
callback routine should return 1 if it processes the message, 0
otherwise. If the routine does not process the message, it
must not call strtok().
-
expire maskdata
Parameters: uint32 type, MaskData *md
- Called when a MaskData entry is about to be expired, just
before it is actually deleted. The callback routine should always
return 0.
-
HELP
Parameters: User *user, char *param
- Called when the HELP command is used with a parameter.
The callback should return 1 if it processes the message, 0
otherwise.
-
HELP COMMANDS
Parameters: User *user, int which
- Called when the HELP COMMANDS command is used. The
which parameter is 0 when called after the list of
unprivileged (IRC operator) commands, 1 after the Services operator
commands, 2 after the Services administrator commands, and 3 after
the Services root commands. The callback routine should always
return 0.
-
SET
Parameters: User *u, char *option,
char *param
- Called when the SET command is used. All parameters are
guaranteed to be valid (non-NULL); param
may contain multiple words. The callback routine should return 1
if it processes the given option, 0 otherwise.
-
STATS
Parameters: User *user, char *extra
- Called for every STATS command that is not plain
"STATS", "STATS ALL", or "STATS RESET".
"extra" contains the remainder of the message (after
STATS and any following whitespace). The callback routine
should return 1 if it processes the command, 0 otherwise.
-
STATS ALL
Parameters: User *user,
const char *s_OperServ
- Called for every STATS ALL command.
s_OperServ is the OperServ pseudoclient nickname;
the callback routine should send messages using this nick. The
callback routine should always return 0.
-
cancel_akill ("operserv/akill" module)
Parameters: const char *username,
const char *host
- Called when an autokill is to be removed from the uplink server.
The callback routine should return 1 if it removes the autokill, 0
otherwise.
-
send_akill ("operserv/akill" module)
Parameters: const char *username, const char *host,
time_t expires
const char *who, const char *reason
- Called when an autokill is to be sent to the uplink server. The
callback routine should return 1 if it sends the autokill, 0
otherwise.
6-2-3. NickServ callbacks
-
authed (module nickserv/mail-auth)
Parameters: User *u, NickInfo *ni,
NickGroupInfo *ngi, int authreason
- Called when a user successfully authenticates their nickname with
the AUTH command, after all normal processing has been
performed. authreason is the value that the
ngi->authreason field had before being cleared (one of
the NICKAUTH_* constants). The callback routine should
always return 0.
-
cancel_user
Parameters: User *u, int old_status,
int old_authstat
- Called whenever the cancel_user() routine is called for a
user with a registered nick. old_status is the
value of the user's nick's status field while the
nick was in use (when the callback is called, all temporary flags
will have been cleared); old_authstat is likewise
the value before clearing of the nick group's
authstat field. The callback routine should always
return 0.
-
check_expire
Parameters: NickInfo *ni, NickGroupInfo *ngi
- Called when a nickname is being checked for expiration, after the
nickname's last-used time has been updated (if appropriate). The
callback routine should return 1 if the nick should be expired, 0
otherwise. If returning 1, the callback routine should log an
appropriate message.
-
check recognized
Parameters: const User *u
- Called whenever Services needs to check whether a user is
recognized as being the owner of the nick they are using
(e.g., when the user connects to the network). The
User structure is guaranteed to have valid
ni and ngi pointers. The callback
routine should return:
- 0, to indicate that the user was not recognized;
- 1, to indicate that the user was recognized as the owner of the
nick; or
- 2, to indicate that the user should not be recognized as
the owner of the nick, regardless of any subsequent checks.
-
collide
Parameters: User *u
- Called whenever NickServ determines that a user should be
"collided". The callback should return 1 if it handles colliding
the user (for example, by forcibly changing the user's nick), or 0
to allow normal collide processing to continue.
-
command
Parameters: User *user, char *command
- Called for every message (other than a CTCP PING or empty
message) received by NickServ. See the
OperServ "command" callback for
details.
-
HELP
Parameters: User *user, char *param
- Called when the HELP command is used with a parameter.
The callback should return 1 if it processes the messages, 0
otherwise.
-
HELP COMMANDS
Parameters: User *user, int which
- Called when the HELP COMMANDS command is used. The
which parameter is 0 when called after the list of
unprivileged commands and 1 after the list of privileged commands
(those shown to IRC operators). The callback routine should always
return 0.
-
identified
Parameters: User *u, int old_authstat
- Called when a user identifies for their nick.
old_authstat is the authorization status of the nick
before the identify took place. The callback routine should always
return 0.
-
IDENTIFY check
Parameters: User *u, char *pass
- Called when a user attempts to identify for their nick, after the
standard checks (whether the nick exists, etc.) are done but before
the password itself is checked. The callback routine should return
1 to disallow identification or 0 to allow processing to continue
normally.
-
nick delete
Parameters: NickInfo *ni
- Called when a nick's registration is deleted. The callback routine
should always return 0.
-
nickgroup delete
Parameters: NickGroupInfo *ngi,
const char *oldnick
- Called when a nick group is deleted (i.e. when the last nick for
the group is deleted), after the nick
delete callback is called. oldnick is the
nickname whose deletion caused this nickgroup to be deleted. The
callback routine should always return 0.
-
REGISTER/LINK check
Parameters: User *u, const char *nick,
char *password, char *email
- Called when a user attempts to register their nickname, after the
validity of the parameters has been checked but before checking
whether the nickname exists or the password is strong enough.
u is the user who sent the REGISTER
command, and nick is their nickname (the same as
u->nick). email may be
NULL (if the user did not provide an E-mail address), but
password will always be non-NULL. The
callback routine should return 0 to allow the registration to
continue or 1 to disallow it.
Also called by the nickserv/link module when a user
attempts to link a new nickname to their own. In this case
nick is the requested target nickname (different
from u->nick), and password and
email are both NULL. The callback is
called after the nickname is checked for validity (length and
invalid characters), but before checking whether the user has
registered their current nickname. The callback routine should
return 0 to allow the link to be made or 1 to disallow it.
-
registered
Parameters: User *u, NickInfo *ni,
NickGroupInfo *ngi,
int *replied
- Called when a user registers their nickname, after the data
structures have been set up. The integer pointed to by
replied can be set to 1 to disable the standard
"your nick has been registered" reply. The callback need not call
put_xxx() even if it modifies ni or
ngi, as the caller will do so afterwards. The
callback routine should always return 0.
-
SET
Parameters: User *u, NickInfo *ni,
NickGroupInfo *ngi,
char *option, char *param
- Called when the SET command is used. All parameters are
guaranteed to be valid (non-NULL), and standard permission
checks will have been performed (so u either is
identified for ni/ngi or is a Services
admin). The callback routine should return 1 if it processes the
given option, 0 otherwise.
-
SET EMAIL
Parameters: User *u, NickGroupInfo *ngi
- Called when the E-mail address associated with a nick group is set,
changed, or unset. u is the user making the change;
this may not necessarily be the owner of the nick if, for example,
a Services admin is making the change. The new address is stored
in ngi->email. The callback routine should
always return 0.
-
UNSET
Parameters: User *u, NickInfo *ni,
NickGroupInfo *ngi,
char *option
- Called when the UNSET command is used. All parameters are
guaranteed to be valid (non-NULL), and standard permission
checks will have been performed (so u either is
identified for ni/ngi or is a Services
admin). The callback routine should return 1 if it processes the
given option, 0 otherwise.
6-2-4. ChanServ callbacks
-
check_chan_user_modes
Parameters: const char *source, User *user,
Channel *c, int32 modes
- Called when checking a user's modes on a channel, before any
standard checks are performed. The mode change is being performed
by source (NULL for a channel join) on
user on channel c, and the user's
current modes on the channel are modes. The
callback routine should return 0 to allow normal checking to
continue or 1 to abort further checks.
Note: source may be the empty string; this
indicates an internal call to verify a user's current modes. In
this case callback routines should not make any decisions about
modes based on the value of source.
-
check_kick
Parameters: User *user, const char *chan,
ChannelInfo *ci, char **mask_ret,
const char **reason_ret
- Called when checking whether a user should be kicked from a
channel, before any standard checks are performed. All parameters
are guaranteed to be non-NULL, except for
ci, which will be NULL when the channel is
not registered. The callback routine may return:
- 0, to allow normal processing to continue.
- 1, to cause the user to be kicked from the channel. In this
case, the callback routine must set *mask_ret
to a newly malloc()/smalloc()'d memory
area containing the mask to be banned (create_mask()
or sstrdup() may also be used) and
*reason_ret to a string for use as the
reason in the kick.
- 2, to abort further processing and allow the user to enter the
channel.
-
check_modes
Parameters: Channel *channel,
ChannelInfo *ci, int add,
int32 flag
- Called when checking a channel's modes against its mode lock for
every mode locked either on (add nonzero) or off
(add zero). The callback routine should return 1 if it
processes the mode, 0 if not. If a mode is not processed by any
callback and is further not specially handled by Services
(+k and +l modes), the default action is to
simply modify the channel's current modes to match the mode lock
(by calling set_cmode(...,"+X") or
set_cmode(...,"-X") for mode X), so
it is not necessary to add a callback for modes for which this
behavior is sufficient.
-
CLEAR
Parameters: User *user, Channel *channel,
char *what
- Called when a valid CLEAR command is received (i.e. from a
user with permission to use the command on the given channel). The
callback routine should return 1 if it processes the command, 0 if
not.
-
command
Parameters: User *user, char *command
- Called for every message (other than a CTCP PING or empty
message) received by ChanServ. See the
OperServ "command" callback for
details.
-
HELP
Parameters: User *user, char *param
- Called when the HELP command is used with a parameter.
The callback should return 1 if it processes the message, 0
otherwise.
-
HELP COMMANDS
Parameters: User *user, int which
- Called when the HELP COMMANDS command is used. The
which parameter is 0 when called after the list of
unprivileged commands and 1 after the list of privileged commands
(those shown to IRC operators). The callback routine should always
return 0.
-
INVITE
Parameters: User *u, Channel *c,
ChannelInfo *ci
- Called whenever a valid INVITE command is received (i.e.
the user has permission to use INVITE for the channel).
The callback should return 0 to allow normal processing to continue
or 1 to abort further processing.
-
SET
Parameters: User *u, ChannelInfo *ci,
char *option, char *param
- Called when the SET command is used. All parameters are
guaranteed to be valid (non-NULL), and standard permission
checks will have been performed (so `u' either is
identified for ci or is a Services admin). The
callback routine should return 1 if it processes the given option,
0 otherwise.
-
SET MLOCK
Parameters: User *u, ChannelInfo *ci,
int modechar, int add,
char **av
- Called for each valid mode character in a SET MLOCK
command. add is nonzero if adding modes, zero if
removing them. av points to the first argument for
the mode; there are guaranteed to be enough arguments for the mode
character (provided the number of arguments in the mode definition
is set correctly. The new mode lock (currently being constructed)
is stored in the ci->mlock_XXX fields. The
callback routine should return 1 if it encounters an invalid
parameter for a mode, 0 otherwise.
Also called with modechar set to zero when all
modes have been processed. In this case, add and
av are undefined, and the callback routine should
return 1 if there is a problem with the mode lock, 0 otherwise.
-
UNBAN
Parameters: User *u, Channel *c,
ChannelInfo *ci
- Called whenever a valid UNBAN command is received (i.e.
the user has permission to use UNBAN for the channel).
The callback should return 0 to allow normal processing to continue
or 1 to abort further processing.
-
UNSET
Parameters: User *u, ChannelInfo *ci,
char *option
- Called when the UNSET command is used. All parameters are
guaranteed to be valid (non-NULL), and standard permission
checks will have been performed (so `u' either is
identified for ci or is a Services admin). The
callback routine should return 1 if it processes the given option,
0 otherwise.
6-2-5. MemoServ callbacks
-
command
Parameters: User *user, char *command
- Called for every message (other than a CTCP PING or empty
message) received by MemoServ. See the
OperServ "command" callback for
details.
-
HELP
Parameters: User *user, char *param
- Called when the HELP command is used with a parameter.
The callback should return 1 if it processes the message, 0
otherwise.
-
HELP COMMANDS
Parameters: User *user, int which
- Called when the HELP COMMANDS command is used. The
which parameter is 0 when called after the list of
unprivileged commands and 1 after the list of privileged commands
(those shown to IRC operators). The callback routine should always
return 0.
-
receive memo
Parameters: int ischan, void *who,
const char *whoname, const User *sender,
char *text
- Called when a user or channel receives a memo.
ischan is zero if the recipient is a user, nonzero
if a channel, and who is the NickGroupInfo
* or ChannelInfo * for the recipient, respectively;
whoname is the name corresponding to
who (the one given in the MemoServ SEND
command). The callback should return nonzero to terminate
processing or zero to cause the memo to be received normally.
Callback routines for this callback should use one of the following
two priorities (defined in memoserv.h):
MS_RECEIVE_PRI_CHECK for routines which check whether the
memo is allowed to be sent, or MS_RECEIVE_PRI_DELIVER for
actually sending the memo.
-
SET
Parameters: User *u, MemoInfo *mi,
char *option, char *param
- Called when the SET command is used. All parameters are
guaranteed to be valid (non-NULL), and standard permission
checks will have been performed. mi points to the
MemoInfo for the user's nickname. The callback routine should
return 1 if it handles the given option, 0 otherwise.
6-2-6. StatServ callbacks
-
command
Parameters: User *user, char *command
- Called for every message (other than a CTCP PING or empty
message) received by StatServ. See the
OperServ "command" callback for
details.
-
HELP
Parameters: User *user, char *param
- Called when the HELP command is used with a parameter.
The callback should return 1 if it processes the message, 0
otherwise.
-
HELP COMMANDS
Parameters: User *user, int which
- Called when the HELP COMMANDS command is used. The
which parameter is currently always 0. The
callback routine should always return 0.
6-2-7. HTTP server callbacks
-
auth
Parameters: Client *c, int *close_ptr
- Called for each valid request received by the server.
c is the client from which the request was
received; the request data is stored in the client structure
(see http.h), and at a minimum the method,
url, version_major, and version_minor
fields will be set appropriately. If the request is denied and the
connection should be closed after the reply is sent, the callback
routine should set *close_ptr to a nonzero value
(*close_ptr should not be modified if the request
is not denied). The callback routine should return
HTTP_AUTH_ALLOW to explicitly allow the request to
proceed, HTTP_AUTH_DENY to deny the request (in which case
the module must send an appropriate error message), or
HTTP_AUTH_UNDECIDED to pass the request through to the
next callback; if all authorization callbacks pass the request
through, it is treated as allowed.
-
request
Parameters: Client *c, int *close_ptr
- Called for each valid request received by the server and either
explicitly allowed or passed through by all authorization
callbacks. c is the client from which the request
was received; the request data is stored in the client structure
(see http.h), and at a minimum the method,
url, version_major, and version_minor
fields will be set appropriately. If the connection should be
closed after the reply is sent, the callback routine should set
*close_ptr to a nonzero value. The callback
routine should return 1 if it processes the request, 0 otherwise.
Back to top
6-3. Submitting additions or changes to Services
As a rule, suggestions for improvement are generally preferred to
submissions of source code, for reasons discussed in
FAQ Z.8. However, if you would like to submit a
module you have written for inclusion in the standard Services
distribution, or you have made a modification to Services itself (or a
standard module) that you feel would be useful to others and would like it
included in Services, please observe the following guidelines:
- Your code must follow the coding guidelines
given below (section 6-4).
- Your code should be reasonably well error-checked and robust. For
example, the return value of any function call which can return an
error should be checked, and appropriate action taken if an error
occurs.
- Your code should compile with no errors or warnings using the default
Services compilation options (gcc -O2 -fno-strict-aliasing
-Wall -Wmissing-prototypes).
- You should use comments well, but not overuse them; you do not need to
comment every single line of code, but on the other hand someone
looking at your module should not have to decipher several hundred
lines of code just to figure out what it does.
- All comments and any accompanying documentation should be in English
and use proper grammar, spelling, etc. (Partial sentences are
acceptable for inline comments.)
Any submissions which do not meet the above requirements will be
categorically rejected, although some leeway may be provided in terms of
comment and documentation quality if your native language is not English.
Furthermore, any submissions of modules, code, documentation, or any
other information (collectively "information") become the sole property of
the author of IRC Services, and by submitting such
information, you agree to transfer any and all rights under copyright law
you may have in said information to said author. If you do not agree to
the above, or you are not permitted (by law, contract, or otherwise) to
transfer all of your rights in the information to the author(s) of IRC
Services, then you may not submit the information. (This requirement is
made to avoid the hassles which would accompany the inclusion of source
code or other items owned by multiple people or groups; see also the note
on the copyright page. If you cannot comply
with this paragraph but would still like to submit something, please
contact the author to discuss your situation.)
Back to top
6-4. Coding guidelines for Services
In general, observe the same style as is used in the rest of Services
and you should have no problems. Specific points follow:
Formatting
- Lines must not be longer than 79 columns.
- The basic indentation unit is four columns.
- Tab stops are set at multiples of eight columns (i.e., a tab
character is equivalent to two levels of indentation). Tabs should
be used in place of sequences of space characters, but this is not
required.
- Only one statement is allowed per line. Statements (including
the empty statement ";") may not be written on the same
line as a control keyword (if, for, or
while).
- Spaces are placed between binary operators and their operands,
except for the member-reference operators "->" and
"."; spaces are not placed between unary operators and
their operands. However, spaces may be omitted around binary
operators when it would improve readability. Examples:
i = j * 2;
user->channelcount++;
result += i*60 + (j+59)/60;
- Always use NULL, not 0, in pointer comparisons.
For character comparisons, 0 is preferred to
'\0', but the latter may also be used if it does not go
against the style of the surrounding code. Do not use
'\0' with variables declared as type int8 or
uint8, even though these types are usually equivalent to
char and unsigned char.
- The construct "!!expression" should not be used.
Use "expression != 0" (for characters or integers)
or "expression != NULL" (for pointers) instead.
- Parentheses are required when && and || are
used in the same expression to explicitly indicate order of
evaluation; do not rely on the default order of evaluation. (Not
using parentheses will generate a warning when using gcc
-Wall.)
- Parentheses following the control statements "if",
"for", and "while", as well as the macros
LIST_FOREACH, LIST_FOREACH_SAFE, and
ARRAY_FOREACH, are preceded by a space. Parentheses
following function or macro calls (other than the above) are not
preceded by a space. Example:
if (flag)
function();
- Spaces are placed after the comma of each parameter to a function or
macro; however, they may be omitted in function calls which are
themselves parameters to a function or macro, or when including
them would make the line exceed 79 columns in length. Spaces are
also placed after (but not before) the semicolons in a for
statement. Spaces are not placed after the opening parenthesis or
before the closing parenthesis of a function/macro call or control
statement. Examples:
function(param1, param2);
function(param1, strchr(string,'/'), param3);
for (i = 0; i < count; i++) . . .
- Opening braces of control statement blocks go on the same line as
the control statement (if, for, etc.) associated
with the block; function blocks and "naked blocks" (those not
associated with any control statement or function) have the opening
brace alone on a line, indented to the same level as the
surrounding code. Closing braces are indented to the same level as
the line containing the opening brace. Examples:
if (flag) {
/* indented code goes here */
. . .
}
int function(int param1, char *param2)
{
/* indented code goes here */
. . .
/* start of a naked block */
{
int foo;
. . .
}
}
- If an if statement is longer than one line, it should
be broken at logical operators (&& or ||); the
operator should be placed at the beginning of the next line, and
should be indented to show which term the operator belongs with.
The closing parenthesis for the if statement and the
subsequent open brace should be alone on a line, aligned with the
first line of the if statement. (Braces are required in
this case.) Conditions for for and while
statements should never span multiple lines, though a for
statement may be broken at the semicolons if necessary. Examples:
if (ok && (strcmp(option, "option-name-1") == 0
|| strcmp(option, "option-name-2") == 0)
) {
. . .
}
if (!init_first()
|| !init_second()
|| !init_third()
|| !init_fourth()
) {
/* This example outdents the || by three spaces
* to make the terms line up. */
. . .
}
for (exception = first_maskdata(MD_EXCEPTION); exception != NULL;
exception = next_maskdata(MD_EXCEPTION)
) {
. . .
}
- An else followed by an if is considered to be a
single keyword, "else if". The if part should
always be placed on the same line as and immediately following the
else; the else if should never be split up onto
two lines, nor should braces be used around the if block.
The exception to this is when the else and if
refer to two conceptually distinct sets of conditions, in which
case the if block should be enclosed by braces and
indented. Example:
res = check_password( /* ... */ );
if (res == 0) {
. . .
} else if (res == 1) {
/* "else if" on a single line */
. . .
} else {
if (user->ni != NULL) {
/* "if" condition is different from "else"
* condition, thus separate */
. . .
}
}
- Braces are always placed around the body of a control statement
(if, for, etc.). The one exception, aside from
the case of else if mentioned above, is a statement for
which the body, as well as the bodies of any else if or
else statements for an if statement, are all one
statement in length and do not contain any nested control
statements; in this case braces are optional (but recommended if
any of the statements span multiple lines).
Examples:
for (i = 0; i < count; i++)
function(i);
while (!done) {
/* Braces required because of the nested "if" */
if (do_stuff())
done = 1;
}
if (state == 0) {
a = b;
} else if (state == 1) {
/* Every if/else body gets braces because this body
* has two statements */
b += a;
a = 0;
} else {
state = 0;
}
- When using multiply-nested control statements, or a single control
statement which has a long block, the closing brace of the block
should be followed by a comment indicating what control statement
the block belongs to. This should usually be the content of the
control statement, but may be abbreviated as long as its meaning is
clear. If a long if block is followed by an else
clause, the else should likewise be documented. Examples:
while (i < count) {
. . .
} /* while (i < count) */
if (flag) {
. . .
} else { /* !flag */
. . .
} /* if (flag) */
for (node = first; node != NULL; node = node->next) {
. . .
} /* for all nodes */
- Case labels for a switch should be indented half of a
normal indentation unit (two columns) from the line containing the
switch with which they are associated; statements
associated with a case should be indented a full unit from the line
containing the switch (half a unit from the case).
If a case requires its own block, such as when it declares its own
local variables, the opening brace is placed after the colon on the
case line. Example:
switch (variable) {
case 123: {
int foo;
. . .
break;
} /* case 123 */
default:
. . .
return -1;
}
- When a case in a switch block does not contain a
break (or return) statement and deliberately
"falls through" to the next case, a comment to this effect should
be made at the bottom of the case. Example:
switch (state) {
case 0:
. . .
/* fall through */
case 1:
. . .
break;
}
- Inline comments (comments placed to the right of a line of code)
should be aligned to start on the same column, when this does not
place too much space between the comment and the code or cause the
line to exceed 79 columns in length. Inline comments after
unusually long lines (such as long field declarations in
structures) should be placed alone on the following line, indented
the proper amount.
- The goto statement should not be used except in error
handling situations where it will help avoid multiple levels of
if nesting or other awkward code. Labels for
goto should be outdented half of an indentation unit
from the surrounding code (i.e., indented two columns less
than the surrounding code).
Identifier naming
- Global variables and type names should use mixed upper- and
lower-case, with an upper-case letter at the beginning of each
distinct word in the name.
- Preprocessor constants and macros should use all upper-case
letters, with an underscore between distinct words in the
name.
- Structure names and local variables should use all lower-case
letters, with an underscore between distinct words in the
name.
- Structure names for structures with an associated type name
(i.e., from typedef) should be given the same name
as the type, except with all letters in lowercase and followed by
an underscore. Example:
typedef struct mytype_ MyType;
struct mytype_ {
MyType *next, *prev;
. . .
};
- Names should be descriptive. For global variables,
preprocessor macros and constants, type names, and structure
names, names should generally consist of one or more full words.
For local variables, short names and abbreviations are permitted
as long as it is clear what the variables are used for. In
general, one-letter local variable names should not be used other
than the following:
- c: character
- f: file pointer (FILE *)
- i, j, k: integers (usually
counters)
- n, p, q: integers (p may
also be a pointer variable)
- s: string
- t: string or temporary variable
- x, y, z: integers (usually
position variables)
Two-letter variable names are often formed from initials of type
names, such as NickInfo *ni; see the source code for
examples.
Back to top
Table of Contents |
Previous section: Importing and exporting databases |
Top |
Next section: Contacting the author