General Rules of Thumb:

- Be wary of minor tweaks just to bring it into compliance with
  these guidelines. If it's new code you just wrote, no problem.
  But if it's older code you'll likely create large numbers of
  conflicts that will need to be resolved in upcoming merges.
  You'll also make the output of "cvs annotate" less useful;
  you'll be listed as the last person to touch the code.  In
  particular, don't mess with switching spaces to tabs and back.
  Unless the code is deeply disfunctional, let the old warts
  persist. That said, feel free to tweak the formatting of a line
  you're editting anyway for some other purpose.

- When in doubt, match the style of the file you're working on.
  Condor's style is heavily inconsistant; at least try to
  minimize inconsistancies within a single file.

- If you find yourself using types that might not be in the C/++
  standard, such as long long, you should look for a typedef
  equivalent, such as int64_t and uint64_t.

- If you are writing code that depends on an external be sure your
  code is wrapped in a pre-processor check for HAVE_EXT_<name>, where
  <name> is the name of the external you depend on.

Memory ownership

If a function takes a pointer, then in the comment for the prototype, note
whether or not the function:
	1) assumes ownership of the pointer with no memory copy and how
		this memory ultimately gets freed.
	2) Copies the contents of the pointer and cleans it up itself.
	3) Just inspects the pointer.

Forward Declarations

If a C++ header file uses a struct or object in a way that it doesn't
need to know the size of the object (e.g. that object is passed by
reference or pointer), then you have the choice of either forward
declaring the type, or fully declaring the type.  The latter is done
by #include'ing the appropriate header file.  For example:

class MyString; // forward declaration   OR
#include "MyString.h"    // Full declaration by inclusion

class Foo {
	Foo(const MyString &s);
};

If you have the option, you should _always_ chose the former, and add
the #include only into the implementation file for that class.
Forward declaration has the following benefits:

1) You minimize the number of unintended dependencies.  If MyString.h
includes or declares other headers, those symbols now pollute your
class and every class that has ever #include'd it.

2) Forward declaration creates faster compiles because the compiler
doesn't need to open and reparse all the header files over and over
again.  Because we've done a bad job of this in the past in Condor, it
is common for a 1 kloc source file to cause the compiler to parse over
100klines of headers.

3) Forward declaration creates smaller debug sections in the
executables.  Our current schedd is about 5 Mb of code and 27Mb of
symbols.  Full inclusions causes a lot of redundant debug information,
which means it is hard for us to ship debugable binaries by default.
It also slows down link time, and generally makes our build process
unwieldy.

Forward declaration is also possible for templated types, though the
syntax is a bit of a mouthful.  For example:

class MyString;
template <class Key, class Value> class HashTable;
class Foo {
	HashTable<MyString,MyString> *m_someTable;
};

This is especially beneficial, as template .h files usually include
the full implementation.



Class Members:

- Class member variables should be prefixed with m_. This is the
  only bit of Hungarian notation we specifically use.



Pre-Processor Usage:

- When checking a given preprocessor define, use "#if FOO", not
  "#ifdef FOO".  when FOO is undefined, "#if FOO" is still false, not
  an error.  However, if FOO is defined to 0, FALSE, etc, then "#ifdef
  FOO" is true, which is not what you'd expect.  Also, anything
  defined by autoconf (which will be more and more of our preprocessor
  definitions as time goes on) will always be defined, and when
  disabled, will be defined to 0, so #ifdef is just not safe.



The remainder of this file is the original CODING_GUIDELINES from
1994.  Much of it is still relevant, but it's 11 years old and
hasn't been reviewed.  For example, you can probably ignore
everything referring to coding with non-ANSI standard C
compilers.  So give it a read, but take it with a grain of salt.




Dear Colleagues,

Following is a brief [sic] set of coding guidelines for Condor.  The
primary goal is to generate code which is easily readable and
maintainable by all of us.  A uniform apperarnce is a secondary goal.
All of these items are open to discussion.

-- mike

-------------------------------------------------------------
0. General
Programming is an art.  Artists sometimes break rules.  This is
OK if you have a good reason.  Readability is a good reason.
Not knowing the rules is not a good reason.

Condor has been in continuous development since about 1985.  Some of
the code is quite old, and does not follow these guidelines.  New code
and major re-writes should follow them.

1. Intended environment
In general we are building code for a POSIX.1 conforming environment
which supports UNIX job control, and has ANSI C compilers and
ANSI style library routines available.

We also assume C++ is available - but don't expect the newest and most
esoteric constructs of this language to be supported.  We do use
templates, but not exceptions.  The use of "tricky" language constructs
such a "diamond inheritance" will surely be non-portable, and are not
to be used in Condor code.

Not all functions necessary to systems programming are supported by
POSIX/ANSI, therefore some of our code will not follow those
standards.  The idea is to
	1. Follow the standards when that makes sense.
	2. Encapsulate the exceptions as well as possible.

Since we want to conform to POSIX, most every C or C++ file should
have
#define _POSIX_SOURCE
as the first non-comment line.


2. Tabs
Condor programs are all written with a tab size of 4.  Variable declarations
should have the names indented so that they line up (within reason).  Example:

int		foo;
char	*bar;
FILE	*glarch;
struct longname *glorp;

If you need to print Condor code, you can use the "expand" UNIX utility
to convert the tabs to spaces.

	expand -4 foo.c | lpr

3. Line Folding
Please keep your lines to a maximum of 80 columns.  Fold long lines fairly
far to the right to keep the folds from looking like indentation.  Example:

while( some condition which is very long and ugly, and probably contains
										numerous pointers and subscripts ) {
	body;
}

4. Comments describing variables.
Please place these to the right of the variable:
	int		foo;	/* some dumb variable */
	int		bar;	// some dumb variable

5. Block comments - comments proeceeding function definitions or other
large "block" comments should be set started by "/*" on a line by itself,
and ended by "*/" on a line by itself.  The text which goes in between
the two comment delimiters should be started with white space (two
spaces looks nice).  Don't start every line with "**" or something like
that - this makes the comments hard to edit later.  If you start only
with white space, then after editing, you can adjust all the line
lengths easily with the UNIX command "fmt".  This can be easily invoked
from within the "vi" editor - I don't know about emacs.

/*
  This is a block comment.  Do it the same way in C++ as in C.  Short
  comments in C++ are best done with the "//" comment marker as that
  is less error prone for that situation, but long ones are best done
  like this.
*/

6. Comments describing a block of code should preceede that block, and be
indented on tab stop.  Examples:

		/* This part is really tricky */
	stmt_a;
	stmt_b;
	stmt_c;

		// This part is really tricky
	stmt_a;
	stmt_b;
	stmt_c;

7. Function Headers
Unless you are working on an old piece of Condor code which will get
compiled with a non-ANSI compiler, please use ANSI style function
headers.  (Ask me if you're not sure whether your code will need to
use non-ANSI headers.)

The function name should start in column 1, with any associated
function type on the previous line.  Following the parameters should be
a single '{' in column 1.  The closing '}' should also be in column 1.
Local parameters should be indented 1 tab stop, and followed by a blank
line.  Also the body of the function should be indented one tab stop.

void
foo( int arg1, const char *arg2, char *arg3 )
{
	int		local_1;
	char	*local_2;
	FILE	*local_3

	/* body of foo */
}

For C++ member functions, please put the type information on a separate
line, and the fully qualified function name starting in column one
of its own line

void
foo::bar( int arg1, const char *arg2, char *arg3 )
{
	// body of foo::bar
}

Putting the function name always in column one makes finding it in the
code easy.  Just search for /^foo/ (C case) or /^foo::bar/ (C++ case).
Invocations will never be in column 1, since they occur in the bodies
of functions.

8. Function declarations

You can put the whole declaration on a single line.  If the names of local
variable will be useful to somebody reading the declaration, it is a good
idea to include them.

char *lookup_macro( const char *name );


9. If, while, for, etc.
These should have an open '{' as the last character on the line
containing the header.  The body should be indented one tab stop.  The
closing '}' should be on a line by itself, and line up with the
header.  Please use '{' and '}' with "if" statements even if the body
is only one line.  (This reduces the chance of an error when adding
printf's during debugging.) Else clauses may be placed on the same line
as the closing '}' of an if.  Multiple nested if's which are logically
equivalent to a switch() may be written in "switch" style (see
example).  Please put the '(' next to the keyword with a space
afterward.  You may omit the space if it reduces the need for line
folding.
Examples:

if( condition ) {
	body;
}

for( initialization; condition; increment ) {
	stmt1;
	stmt2;
	stmt3;
}

if( condition ) {
	body_1;
} else {
	body_2;
}

	/* switch style nested if */
if( a < 500 ) {
	body_1;
} else if( a < 1000 ) {
	body_2;
} else if( a < 2000 ) {
	body_3;
} else {
	body_4;
}

10. Capitolization
Global variables generally begin with capitols.  Local variables
are generally all in lower case.  Macros are generally all uppercase.
You will find some execptions in the current code...  Examples:

int		FooBarGlarch;		/* A global variable */

foo( int foo_bar_glarch )	// A function parameter
{
	int		glorp;		// A local variable

	body;
}

	/* a macro */
MAX_VAL(a,b)  ((a)>(b)?(a):(b))


11. More on ANSI style prototypes
If you need to write code which will work with both ANSI and non-ANSI
compilers, use conditonal compilation to deal with the prototypes.
Since all C++ compilers expect ANSI style prototypes, you need to 
make the condition be "compiling with an ANSI C compiler" or "compiling
with a C++ compiler" - you may be building a header file which will
get included in both C and C++ code.

#if defined(__STDC__) || defined(__cplusplus)
	void	insert_proc( ProcInfo *proc, NODE *tree );
#else
	void	insert_proc();
#endif

12. Repeatativeness
It's very easy to write repeatative code using the "cut and paste"
functions of your favorite editor.  Unfortunately, this makes the code
harder to maintain later.  When you need to make a change, it's too
easy to miss one or more of the places where you should make that
change.

If you are going to invoke a function from more than one source file,
put the prototype into a ".h" file.  Please don't put separate versions
of the prototype in different source files.

Put common blocks of code into a function or macro (preferable a function).

13. Use of CPP directives
ANSI C rules say that CPP directives cannot have extraneous text appended
to them

	#if defined(NEW_PROC) V4 Proc Structure

is illegal!  You can use

	#if defined(NEW_PROC) /* V4 Proc Structure */

but if would be better if you could make things clear without the
comment.  It's controversial whether you can use C++ style comments
in CPP directives, so don't do it.

Use appropriate indentation in CPP directives, but remember that the
'#' must start in column 1.  Also if you are compiling with an ANSI
C compiler or a C++ compiler, you may use the "#elif" directive.

#if defined(ALPHA)
#	define WORD_SIZE 64
#elif defined(POCKET_CALCULATOR)
#	define WORD_SIZE 8
#else
#	define WORD_SIZE 32
#endif

14. POSIX vs BSD
All of the old Condor code was written on BSD or BSD derived systems.  Any
new code (or major re-writes of old code) should try to conform to POSIX
instead.  

The macro MAXPATHLEN is not generally avaiable on POSIX sytems.   Use
_POSIX_PATH_MAX from <limits.h> if you really must.  In most cases, it
is better to use a dynamic buffer (e.g. MyString) rather than a fixed length
buffer, because many platforms support path names much longer than
_POSIX_PATH_MAX.

The wait3() routine, and the structure "union wait" are not generally
available on POSIX sytems.  Use wait() or waitpid(), and the
integer status values specified by POSIX.  Use the macros to decode
the status value - don't do the bitmasking operations yourself.  The
macros are WIFEXITED, WIFSIGNALED, WEXITSTATUS, WTERMSIG, ...

The signal(), and sigvec() routines are not generally available on
POSIX systems.  Use the sigaction() routine instead.  If you have to
call it a lot, encapsulate it along with appropriate error checking
into your own routine.

The sigsetmask(), sigblock(), and sigpause() routines are not generally
available on POSIX systems.  Use sigprocmask() and sigsuspend()
instead.

The rusage structure returned by wait3() and getrusage() is not
generally available on POSIX systems.  Use the information from
the times() call instead.

15. ANSI vs BSD
All of the old Condor code was written on BSD or BSD derived systems.  Any
new code (or major re-writes of old code) should try to use the ANSI
library routines instead.  

The bcopy(), bzero(), and bcmp() routines are non-ANSI.  Use
memcpy(), memset(), and  memcmp() instead.

The index() and rindex() routines are non-ANSI.  Use strchr() and
strrchr() instead.

16. File locking
File locking is done differently on BSD, SYS-V, and POSIX systems.  There
are a number of problems regarding support of file locks across NFS
systems which vary from one NFS implementation to another.

Please avoid any usage which requires locking of byte ranges - this will
not be portable.  You can use advisory "readers/writers" locks on whole
files.  Both blocking and non-blocking forms should work.  This
is encapsulated for you in the condor C and C++ libraries.  

There is a "lock_file()" routine in the condor util library, with a
corresponding header in the "h" directory for use with C programs.  For
C++ programs, you can use the "FileLock" object from the C++ util
library.

