Desc: Information for developers
File: developers.txt
Date: 6 October 2002
Auth: Russell Kroll <rkroll@exploits.org>

As the code base grows, it gets harder to remember all of the nifty 
and useful things that are hiding in the tree.  This document is the
first attempt at explaining how things should be done when working on
this software.

General stuff - common subdirectory
===================================

String handling
---------------

Use snprintf.  It's even provided with a compatability module if the
target host doesn't have it natively.

strlcpy is also available to replace strcpy.

Don't use strcat.  We have a neat wrapper for snprintf called snprintfcat
that allows you to append to char * with a format string and all the usual
string length checking of snprintf.

Error reporting
---------------

Don't call syslog() directly.  Use upslog and upslogx.  They may write
to the syslog, stderr, or both as appropriate.  This means you don't
have to worry about whether you're running in the background or not.

upslog prints your message plus the string expansion of errno.  upslogx
just prints the message.

fatal and fatalx work the same way, but they exit(1) afterwards.

Debugging information
---------------------

upsdebug() uses the global nut_debug_level so you don't have to mess
around with printfs yourself.  Use it.

Memory allocation
-----------------

xmalloc, xcalloc, xrealloc and xstrdup all check the results of the base
calls before continuing, so you don't have to.  Don't use the raw calls
directly.

Config file parsing
-------------------

The configuration parsing is now in its third revision, and is called
parseconf.  You give it the filename and pointers to your functions that
will handle arguments and errors.  It will call you back when it has
something for you to do.  Use this for all config files so there is a
consistent scheme throughout.

Note: this does not apply to drivers.  Driver authors should use the
upsdrv_makevartable() scheme to pick up values from ups.conf.  Drivers
should not have their own config files.

<time.h> vs. <sys/time.h>
-------------------------

This is already handled by autoconf, so just include "timehead.h" and you
will get the right headers on every system.

UPS drivers - main.c and upscommon.c
====================================

All real drivers (i.e., not dummycons and dummyups) use main.c as their
core.  You don't write a main function.  Rather, you write a series of
support functions that work on your specific hardware, and main will
call you as it needs to do things.

Serial ports
------------

Unless you need to do something really bizarre, use open_serial or
open_serial_simple.  These functions take care of all the gory locking
stuff that happens on different systems.

upsrecv() will bring data back from the UPS, up to <endchar>.  It also
handles timeouts for you, and will abort if nothing happens in 3
seconds.  

To send just one byte, upssendchar() will get the job done.

upssend() takes a format string and variables for more complex
transmissions.

upsflushin() will clear out the input buffer.  This should be used if
your UPS hardware has a history of spewing odd things between polls.

Data storage
------------

All variables must be added with addinfo().  This takes an initial value
and flags, in case you know what it will be at the outset.

To set a value in the shared info array, use setinfo().

To set a flag (meta-data) in that same array, use setflag().  This is
only used for read-write types right now, and then for ENUMs and
STRINGs.  The possible values for ENUMs are added with addenum().

If you need to get data back out of the info array, use getdata().

Status setting
--------------

Rather than building the status string by hand, use these common
functions to do it for you cleanly:

	status_init() - before doing anything else

	status_set(val) - add a status word (OB, OL, etc)

	status_commit() - copy it to the info array

main.c specifics
----------------

Set experimental_driver to 1 if your driver is potentially broken.  This
will trigger a warning when it starts so the user doesn't take it for
granted.

Coding style
============

This is how I do things.

int open_subspace(char *ship, int privacy)
{
	if (privacy == 1) {
		if (!init_privacy(ship))
			fatal("Can't open secure channel");

		return secure_channel(ship);
	} else {
		return insecure_channel(ship);
	}
}

Note that "if (privacy)" and putting the insecure_channel() call
outside the if block is also used at times. 

The point to take away from this is that tabs are used, not spaces.
This lets everyone have their personal tab-width setting without
inflicting much pain on other developers.  If you use a space, then
you've fixed the spacing in stone and have really annoyed half of the
people out there.

If you write something that uses spaces, you may get away with it in a
driver that's relatively secluded.  However, if I have to work on that
code, expect it to get reformatted according to the above.

Patches to existing code that don't conform to the coding style being
used in that file will probably be dropped.  If it's something we really
need, it will be grudgingly reformatted before being included.

When in doubt, have a look at Linus's take on this topic in the Linux
kernel - Documentation/CodingStyle.  He's done a far better job of
explaining this.

If you use a goto, expect me to drop it when my head stops spinning.
Sorry, I wrote too much bad spaghetti code in BASIC on the 8 bit
systems of the 80s to treat a goto with anything but scorn.  There are
very few cases where a goto is called for, and you probably don't need
it.

There are parts of the source tree that do not yet conform to these
specs.  Part of this is due to the fact that the coding style has been
evolving slightly over the course of the project.  Some of the code you
see in these directories is 4 years old, and things have gotten cleaner
since then.  Don't worry - it'll get cleaned up the next time something
in the vicinity gets a visit.

In summary: please be kind to my eyes.  There's a lot of stuff in here.

Submitting patches
==================

Patches that arrive in unified format (diff -u) as plain text with no
HTML, no attachments and a brief summary at the top are the easiest to
handle. They show the context, explain what's going on, and get saved as
one message.  Everything stays together until it's time to merge.

Patches that arrive as attachments have to be moved around as separate
files - the body of the message is one, and the patch is in another.
This is not my preferred mode of operation.

If you send a patch to one of the mailing lists and want to be sure I
see it, just add me on the To: line.  Otherwise it might not get 
noticed.
