AROS Coding Style ----------------- (C) 1995 AROS - The Amiga Replacement OS Aaron "Optimizer" Digulla digulla@home.lake.de 0. Preface ---------- Since many people work with you, there have to be rules about how to code. This file defines all the standards used in AROS with respect to style of source code. 1. Introduction ---------------- 1.1 General AOS Development Guidelines -------------------------------------- * The language: Write it in ANSI C: Use full prototypes. ANSI C is the most portable standard today and we don't write this only for the Amiga. Always keep in mind that the source you write should compile on PC and UNIX also. Some parts of the OS can't be written in C since C lacks knowledge about the CPU (eg. interrupts and exceptions). This code is CPU dependant and there is no way to circumvent this. For such code, assembler has to be used. Upto now, no assembler has been chosen. GAS is a hot candidate since it is available for all systems but many Amiga users (and especially the hardware hackers) don't know it and therefore we might choose something that is more "native" to the Amiga. Don't expect it to be SAS/C's assembler or any other commercial one, since not everyone can access it. If we choose an assembler, it will be a public domain one so anyone can access it. * The "General Amiga Development Guidelines" apply (see AMIGA ROM Kernal Reference Manual (RKRM): Libraries, third edition, page 13, "Introduction to Amiga System Libraries"). Note: These Guidelines mostly contain "How to code well"-tips and some Amiga-specific things, like how to wait for a signal. Due to lack of time and copyright-issues, I did not sit down and copy it. * The language for all files is ENGLISH until further notice how the format of a multi-lingual file is. * The author of a file has to include his email for fast resonse in case of bugs and problems. * Compatability is an issue. If you can, try to compile the code on as many different platforms and/or compilers as you can. Don't use Amiga-specific constructs like #pragma or __autoexit or the like since they are not portable. * Make your code reentrant (ie. avoid global variables). If you really can't, put all globals into a structure and allocate it like this: Wrong: int I; int Should; struct Not Do; char This[256]; int main (...) { ... I = 1; ... } /* main */ Good: struct GlobalVars { int gv_I; int gv_Should; struct Not gv_Do; char gv_This[256]; } * global; /* This is the _only_ global variable */ #define I global->gv_I #define Should global->gv_Should #define This global->gv_This int main (...) { ... global = AllocMem (sizeof (GlobalVars), MEMF_CLEAR); I = 1; ... } /* main */ This might look unneccessary, but it prevents a couple of problems: * Some machines support different amounts of global memory. Some allow 64K max, others infinite amounts, some have problems with global vars > 32K, others don't. * Programs that use global variables can't be made resident. * Programmer that use global variables tend to loose control about their variables and where they are used and changed. 1.2 AROS Shared Libraries ------------------------- 1.2.1 AROS Parts ---------------- * Put all files for a certain topic in a new directory * Every file implements exactly one function (this is due to the fact that we might need link-libs for some machines). Take a look at the advantage: Compilation is blinding fast :) * The file has the same name as the function it contains. For now, don't care about 8-digit-max filenames. All letters are lower-case. * If a file needs or offers special structures which it wants to share with others, but those into an include-file with the same name but extension "h" in the same directory. * Files which should be offered to the outside (eg. header-files) go into a special public-directory. Since this looks a bit complicated, here is an example. All files and directories go below "AOS:": AOS: source/ exec/ diagnostics/ alert.c docs/ public/ exec/ exec.doc internal/ lists/ obj/ addhead.o addtail.o enqueue.o findname.o insert.o remhead.o remove.o remtail.o test.o obj_debug/ addhead.o addtail.o enqueue.o findname.o insert.o remhead.o remove.o remtail.o test.o public/ examples/ exec/ lists lists.c lists.info lists.lnk lists.o smakefile include/ exec/ lists.h nodes.h src/ addhead.c addtail.c enqueue.c findname.c insert.c remhead.c remove.c remtail.c tests/ Makefile test test.c test.info test.lnk test.o Makefile Makefile Makefile mail/ Amigos_Mailinglist public/ examples/ exec/ lists lists.c lists.info lists.lnk lists.o smakefile include/ aos/ amiga.h system.h exec/ lists.h nodes.h lib/ exec.lib libs/ exec.library Makefile Note: The files in the directories are put behind each other to save space. Otherwise this list whould extend over 4 pages... The AOS:-directory contains "source/", "mail/" and "public/". The directories "source/" and "mail/" are for developers only. The result of the developing step goes to "public/". Everything below this directory is exported in the user-archive when we release something. The "mail/"-directory is developer-specific. It contains mails and bug-reports and the like. The most interesting part is the dir "source/exec/". It contains the following sub-dirs: "diagnostics/", "docs/", "internal/" and "lists/". The directories "docs/" and "internal/" are mandatory for each part of AOS. The former contains the docs for this part of AOS (exec), the latter contains all internal stuff (ie. routines and knowledge which is not neccessary to use this part, only to implement and develop it). The other two dirs "diagnostics/" and "lists/" contain two parts of EXEC. The former contains diagnostic routines like Alert(), the other everything that is linked to EXEC-lists (eg. AddHead()). The "lists/"-dir is quite well developed, so we focus on it. The dir itself contains several other dirs and a couple of files. The dirs "obj/" and "obj_debug/" contain compiled object-files. The files in the former directory are used for the releases, the files in the latter one contain object-files with debugging-information used during developing and testing this part. Everything contained in the "public/"-dir is copied to "AOS:public/" when a release is generated. Most files in the "lists/"-dir contain functions for Exec-Lists, but some don't. The file "Makefile" contains the rules to compile all files (all), to compile all files with debug-options (debug), to compile the test (test), to compile a debug-version of the test (debug_test), to clean the directory and all sub-dirs (clean) and to make a distribution (dist; this mainly copies all files into "AOS:public/"). The directory "tests/" contains a Makefile and a test-program to test all features of this part. Creating and running the test should allow to check every aspect of this part in a automatic way (ie. in the end a great "make test" in the main-dir should yield a YES/NO-answer to "Does everything work ?" and a more specific answer to "If something doesn't work, what is wrong ?"). 1.2.2 AOS Parts Makefile ------------------------ The Makefile defines the following rules: all - Create all files that have changed anew with default options. Object-files are places below "obj/". debug - Like all, but the files are compiled with debug-options and all object-files are put below "obj_debug/". test - Create a program called "test" which tests every feature of this part. Larger tests are done elsewhere. The test should return the information that no errors occurred ("No Errors") or a hint what went wrong. Other output is optional. debug_test - This rule should create test, too, but this time all available debug-information should be included. Test is to be linked with the object-files from "obj_debug/". clean - This rule cleans the directory and all sub-dirs, too. Cleaning means "remove any file that has been created by the Makefile". dist - Make a distribution. This mainly means to copy public files in the appropriate places. If you do this in the main directory, the Makefile will also create a couple of archives with all sources and all binaries. 1.2.3 Looking Into Files: C-Headers ----------------------------------- This is a sample header-file: ------------------------------ cut -------------------------------------------- #ifndef EXEC_LISTS_H #define EXEC_LISTS_H --------------- to be continued... -------------------------------------------- This has to be here in every include-header ! It is meant to prevent hassle for the user of the file. This way, the user (and we) can include this file as often has he wants or has to (through other include-headers). The name of the define can always be derived by converting all characters to upper case and replacing all non-characters by "_". The name of the file is "exec/lists.h", therefore the define is "EXEC_LISTS_H". This is the first thing in the header file since some C preprocessors check this line and omit the whole file if the symbol is defined already. --------------------- continue ------------------------------------------------ /****************************************************************************** FILE $VER: exec/lists.h 1.0 (26.8.95) DESCRIPTION Prototypes and macros for exec-lists. ******************************************************************************/ --------------- to be continued... -------------------------------------------- This the header for exec-lists. It contains the two entries FILE and DESCRIPTION. After FILE follows the version-information. The first entry of the version information is the name of the file. Since include-files are grouped by the part they belong to, the full name is "exec/lists.h". Then comes the version itself (1.0). The first figure gives the version, the latter the revision. The version is incremented, when something important has been done (eg. a release, a things have been renamed, something has been done which might break old software, etc.). The revision is incremented every time something small has been done (bug-fix, etc.) or when the version is not changed :) --------------------- continue ------------------------------------------------ /************************************** Includes **************************************/ #ifndef AOS_SYSTEM_H # include #endif #ifndef EXEC_NODES_H # include #endif --------------- to be continued... -------------------------------------------- The first thing in a include-header is the inclusion of all files that are neccessary to compile it WITHOUT including ANYTHING else before ! If you don't see the reason for this, just think of a first-time user who tries to use a feature of AOS. He includes the header-file for this feature and gets "error in line 15.". What will he think ? "Aren't these ^/§&% able to write correct header-files ???" or "What the hell does this (/§"&%$ include-file need to compile ?". He doesn't know, but the author of the file does and so the author does what belongs to him :) --------------------- continue ------------------------------------------------ /************************************** Structures **************************************/ --------------- to be continued... -------------------------------------------- Notices these nice headers ? They are used to give the file structure :) --------------------- continue ------------------------------------------------ /* Normal list */ struct List { struct Node * lh_Head, * lh_Tail, * lh_TailPred; UBYTE lh_Type; BYTE l_pad; }; --------------- to be continued... -------------------------------------------- Every structure gets it's own short comment. Try to format them if possible; here, we put all types, all pointers and all names above each other. If the structure is complicated, don't hesitate to make large comments before the structure or behind each field. Make sure though, to put them below each other for improved readability. --------------------- continue ------------------------------------------------ /* Minimal list */ struct MinList { struct Node * lh_Head, * lh_Tail, * lh_TailPred; }; /************************************** Makros **************************************/ --------------- to be continued... -------------------------------------------- Another header; now we define all macros for this functions. --------------------- continue ------------------------------------------------ #undef __CAST #ifdef AOS_ALMOST_COMPATIBLE # define CAST(x,y) ((x)y) #else # define CAST(x,y) (y) #endif --------------- to be continued... -------------------------------------------- Throughout the files, the AOS_ALMOST_COMPATIBLE-define is used to mask lines which implement features or enhancements that are not 100% compatible to the original OS but will probably not break old software. For a better example, see below. --------------------- continue ------------------------------------------------ #define IsListEmpty(l) \ ( (CAST(struct List *,l)->lh_TailPred) == (struct Node *)(l) ) #define IsMsgPortEmpty(mp) \ ( (CAST(struct MsgPort *,mp)->mp_MsgList.lh_TailPred) \ == (struct Node *)(&(CAST(struct MsgPort *, mp)->mp_MsgList)) ) --------------- to be continued... -------------------------------------------- These two macros are (almost) original. The difference is that the AOS-version allows to use anything as parameter of the macros. The original version whould just work with real lists or casts to lists. --------------------- continue ------------------------------------------------ #ifdef AOS_ALMOST_COMPATIBLE # define NewList(l) (CAST(struct List *,l)->lh_TailPred \ = (struct Node *)(l), \ CAST(struct List *,l)->lh_Tail = 0, \ CAST(struct List *,l)->lh_Head \ = (struct Node *)\ &(CAST(struct List *,l)->lh_Tail)) --------------- to be continued... -------------------------------------------- And now something which can't be find in the original sources. Here comes a couple of macros which whould be quite useful in the OS, but which are missing. --------------------- continue ------------------------------------------------ # define GetHead(l) (CAST(struct List *,l)->lh_Head->ln_Succ \ ? CAST(struct List *,l)->lh_Head \ : (struct Node *)0) # define GetTail(l) (CAST(struct List *,l)->lh_TailPred->ln_Succ \ ? CAST(struct List *,l)->lh_TailPred \ : (struct Node *)0) # define GetSucc(n) (CAST(struct Node *,n)->ln_Succ->ln_Succ \ ? CAST(struct Node *,n)->ln_Succ \ : (struct Node *)0) # define GetPred(n) (CAST(struct Node *,n)->ln_Pred->ln_Pred \ ? CAST(struct Node *,n)->ln_Pred \ : (struct Node *)0) #endif /* AOS_ALMOST_COMPATIBLE */ /****************************************************************************** ***** ENDE exec/lists.h ******************************************************************************/ #endif /* EXEC_LISTS_H */ ------------------------------ cut -------------------------------------------- This is the end of the file. After the "#endif" follows the comment with the name of the define above. This has to be used for every "#endif" that is "far away" of the corresponding "#if" (ie. more than about 20 lines). 2. Other Files Throughout the directories files with the name "TODO". They contain information about the state of a project or part or whatever. The TODO-file contains a list of what has to be done, yet. The list looks like this: ------------------------------ cut -------------------------------------------- * Write the functions for exec-lists + Write good docs for exec-lists o Write exec-ports o AddPort() o FindPort() o etc. BUGS: o digulla@home.lake.de metioned that not all functions have good comments. (NTH) o the function Alert() doesn't show that nice red frame around the message. ------------------------------ cut -------------------------------------------- ie. every entry in introduced with a "o". If an entry extends over several lines, then the second and all folowing lines are indented. If an entry is in work, the "o" changes to "+". When it is done, change the "+" to "*". If you want to keep records on how long you worked on it, add the time in hours in "()" after the entry. (eg. "(8h)"). Also in this file is a section which is introduced by "BUGS:". In this section, all bugs, reports, etc. are collected. !!! TREAT THIS PART AS READ-ONLY !!! This way the file might get quite long, but that way it is easy to check if a bug has already gone or not or if someone is working on it. After the entry, some information follows in "()". It is a comma-separeted list of these abbreviations: NTH: Nice To Have. Great if someone finds the time to do it, but it doesn't really hurt. TODO: This one was missing in the original TODO. I have to do it some time. BUG: This is a bug but it doesn't need immediate fixing. SEV: A severe bug ! This means that it really hurts and it needs immediate work. #h: # is the number of hours it took to fix the problem (optional) FIX name: Name is the name of the person who fixed the bug. 3. The Source Style ------------------- The information in the next chapters is divided into "Must Have" and "Nice To Have". "Must Have" means that source which doesn't comply to this will be rejected. "Nice To Have" means "If you follow these rules, the code will look nicer and others will understand it better" but if you object to these rules, all that will happen is a deep sigh from me :-) 3.1 The Style "Must Have" ------------------------- The rules in this chapter are neccessary to compile the source with different compilers on different systems. If you don't follow these rules, you make your life easier but more difficult for many others. So think about it lazy lad... 3.1.1 Includes -------------- AROS offers a layer of includes to adopt to the special needs of a certain compiler and/or hardware. All these includes can be found in the "aros" directory in the AROS includes. The meaning of these includes are described in source/aros/docs. These includes contain a great amount of knowledge about systems and hardwares. They define certain hardware independant macros to make your life easier and they define flags for you to check to see whether a certain feature is requested/neccessary/possible or not. 3.1.2 Arguments in Registers ---------------------------- On the Amiga, it is tradition to call OS functions with the arguments in CPU registers, on other systems this is not; either because the CPU has not the same registers, either because the compiler doesn't allow for specifying a certain register for a certain parameter. To avoid breaking code for the Amiga and to avoid that we need two types of source, some very sophisticated macros have been designed to work around this problem. First, if you need a register and your compiler says "to link a parameter with a register use this syntax", DON'T use this. Instead, name registers as "A0", "D3" etc. These are in fact macros which are expanded into something your compiler can understand. This is done automatically; all you have to do is to include . See source/aros/docs/libcall.doc for a description how to do it. Most of the time, you need not worry, since the headers for all library functions are generated with a script and you need only fill in the source for the function. 3.1.3 Autodocs and AROS ----------------------- AROS doesn't use the Autodoc standard for OS functions because Autodocs are too limited for this. Our format, however, can be converted into Autodocs with little effort. Here is an example for a complete header: --------- header of source/exec/kernel/src/rawdofmt.c ------------------------ /* $Id$ $Log$ ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- These are for RCS. $Id$ is replaced by the version, the name of the file and the person that changed it the last time. $Log$ is replaced by a list of all changed made to the file with date and name of the person that did it. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- Desc: ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- Here comes the short description. With Autodocs, this would have been in the NAME section. NOTE: This part of the header is subject to change. If it is changed all sources are converted automatically. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- Lang: english ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- The language of the header. This is for future expansion so we can offer Autodocs in different languages. NOTE: This part of the header is subject to change. If it is changed all sources are converted automatically. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- */ #include "exec_intern.h" #include "machine.h" ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- Here is a list of internal includes, ie. includes that are neccessary to compile this file but NOT to use the function ! -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- /***************************************************************************** NAME */ #include #include ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- This is the header of the file. Since every file contains only a single function, there is no special file-header but only the header of the function. By making use of C-comments, we mix source-code and documentation in a way which makes it unneccessary to write code twice. This also helps in writing working code since the documentation is always up-to-date. The first thing are the include-files the user of this function needs. Any internal includes would go in front of the first line. For this function, we need the header-file for lists and the prototypes for exec-functions. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- __AROS_LH4I(APTR, RawDoFmt, ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- Here we use a macro to make the prototype usable for other compilers on other systems. For a complete description of the __AROS_LH-macros, look into source/aros/docs/libcall.doc. Here the macro declares a function that is named "RawDoFmt", returns APTR, has 4 arguments and doesn't need the libbase (see below). -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- /* SYNOPSIS */ __AROS_LA(UBYTE *, formatString, A0), __AROS_LA(APTR , dataStream, A1), __AROS_LA(VOID_FUNC, putChProc, A2), __AROS_LA(APTR , putChData, A3), ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- These are all arguments. Note the use of the C comment. This way, the arguments have to be specified only once! No need to worry if the types and names in the header really match the ones used in the part of the file that the compiler actually sees. Every argument is encapsulated in a __AROS_LA-macro which sorts the order of arguments (some compilers need the register after the type and the like). We specify the type, the name and the CPU register in which the argument is passed. The CPU register is safely ignored if it can't be used (eg. because the CPU has no such register or the compiler doesn't allow to specify in which register a parameter has to be passed in a.s.o.). The parameter putChProc is declared as type "VOID_FUNC" due to the fact that the use of the macro makes it impossible to declare it as "void(*putChProc)()". There are more of these types in include/aros/libcall.h. Note that the types, stars and parameter-names are put below each other to increase readability. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- /* LOCATION */ struct ExecBase *, SysBase, 87, Exec) ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- These are the last four parameters of all __AROS_LH-macros. They declare the type and name of the libbase, the LVO offset of the function and the name of the library for this function (in this order). The first two arguments are ignored if LVOs are not used for this system. The LVO itself is always ignored, but it has to be somewhere and there was no much better place for it. The last argument is used to put a common prefix before all functions of a library so there are no problems with linking the library to code that needs stubs to call library functions (same names for functions and the like). There are other flavours of __AROS_LH-macros which allow to declare a function (prototype in a include file) and to call it (using LVOs if neccessary). -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- /* FUNCTION printf-style formatting function with callback hook. ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- Now we give a complete description of what the function does. This includes all features and usages but excludes return-values and parameters if not absolutely neccessary. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- INPUTS FormatString - Pointer to the format string with any of the following stream formatting options allowed: %[leftalign][minwidth.][maxwidth][size][type] leftalign - '-' means align left. Default: align right. minwidth - minimum width of field. Defaults to 0. maxwidth - maximum width of field (for strings only). Defaults to no limit. size - 'l' means longword. Defaults to word. type - 'b' BCPL string. A BPTR to a one byte byte count followed by the characters. 'c' single character. 'd' signed decimal number. 's' C string. NUL terminated. 'u' unsigned decimal number. 'x' unsigned sedecimal number. dataStream - Array of the data items. putChProc - Callback function. Called for each character, including the NUL terminator. putChData - Data propagated to each call of the callback hook. ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- Here are the parameters. Don't name the type; the name is enough. The parameter is separated from the rest of the description by a "-" (minus). The description is indented, if it doesn't fit in a single line. If you want to group parameters, put them into a comma-separated list. If this list extends over more than one line, it is NOT indented. It is allowed to put each parameter in such a list in his own line. The last parameter in the list is not followed by a comma (just in case...). The description should contain every bit of information to understand what this parameter does. If a parameter is a return value (ie. the functions puts something in it and ignores the old value), you can omit it here and explain it in the next section. If the function modifies the parameter, you should explain it here (at least the part of the explanation which tells with which values in this parameter the function can be called). -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- RESULT Pointer to the rest of the dataStream. ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- Here we explain what the function returns or what it changes. For example the RESULT of Supervisor() is that we are in supervisor mode now. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- NOTES The field size defaults to words which may be different from the default integer size of the compiler. ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- Mention all aspects of the function that the user of it should look out for (eg. unexpected results, problems, special circumstances, if the function breaks on NULL-parameters or if it doesn't). -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- EXAMPLE // build a sprintf style function static void callback (UBYTE chr, UBYTE **data) { *(*data)++ = chr; } void my_sprintf (UBYTE *buffer, UBYTE *format, ...) { RawDoFmt (format, &format+1, callback, &buffer); } ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- This section should contain a most complete example of how to use the function. Since one cannot use normal C-Style comments here, there are a couple of ways to circumvent this: a) Use "EXAMPLE */" instead of "EXAMPLE" and put the whole example in a "#ifdef COMPILE_EXAMPLE"..."#endif" block. b) Use C++-style comments "// This is a comment." c) Use Autodoc-Style C comments: "\* This is a comment *\" All these ways are accepted by the Autodoc compiler. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- BUGS PutChData cannot be modified from the callback hook. ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- Now here for the interesting part: Things that should but don't work. If you can, name a workaround. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- SEE ALSO printf(), sprintf(), vsprintf(), fprintf() ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- Specify function here, which work on the same data (eg. OpenWindow() should mention all other functions working on windows). Always remember: Too much information is less frustrating than not enough information. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- INTERNALS ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- This section is for experts only. For the mortal user, it is omitted. Name things here someone who tries to understand your code should know. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- HISTORY 22-10-95 created by m. fleischer ----- header of source/exec/kernel/src/rawdofmt.c (to be continued) ---------- This part is subject of change, too, since RCS is doing it already. Maybe only major changes will be mentioned here. -------- header of source/exec/kernel/src/rawdofmt.c (continued) ------------- ******************************************************************************/ ----------- header of source/exec/kernel/src/rawdofmt.c (end) ---------------- The terminator of the header. The AROS-Header-To-Autodoc converter stops here. The rest of the file is ignored. 3.2 The Style "Nice To Have" ---------------------------- Now I explain my favourite style to code. Since I like structured code, I insert quite a lot of space in my code. The result looks good but on the other hand you need a lot of resolution on you screen to see more than a small portion of my code. 3.2.1 Indent ------------ One indent level is 4 spaces. Two levels of indentation are sometimes a tab. Tabs are is alway set every 8 spaces. 3.2.2 Comments -------------- Comments are aligned with the rest of the source: if (...) { } /* This is a comment */ a = 1; No space follows a comment, but at least one emtpy line goes before it. If a comment is long, it looks like this: a = 0; /* This is a very long comment (somehow). */ a = 1; I don't use the "*-before-every-line" since that results in code where the comments don't reflect the recent changes since that would mean to remore all *'s, change the comment and the reinsert the *'s again. 3.2.2 Blocks ------------ Blocks look like this: int foo (int a) { if (...) { while (...) { } } /* ... */ } /* foo */ The braces are above each other and the text in a block is indented once. Note the use of comments here. Functions always get an end-comment with the name of the function. Blocks get a short description of the header of the block if the first and the last line are about 20 lines away. 3.2.3 Spaces ------------ I insert a space... - after a variable, function or C keyword - before and after operators Sometimes a insert more than one space to get this: struct RastPort * rp; int count; some->struct = 0; some->even.more.complicated.struct = 1; count = 0; ie. a tabular look. Empty lines are inserted whenever the semantic changes: if (a) { } /* a != 0 */ if (b) { } /* b != 0 */ a = 1; call (a, function); call (another, function); ... ie. the two if()'s aren't separated, then an empty line and an assignment, an empty line and a couple of function calls. 3.2.4 Breaking Long Expressions ------------------------------- Long expressions look like this: if (maybe || sure || (a && this_is_never_true (but, who, knows) && TRUE ) ) This looks unfamiliar, but it shows at a glance what part of the expression belongs to which other part and how the sub-expressions are connected. These are the rules: a) Each part of the expressions get's it's ownline except it is the first part. b) An operator or a "(" are the first things in a line. If there are both, the operator comes first. c) If a subexpression (eg. a function call) is too long, it is split at a space and indented two times (to avoid visual conflicts with subexpressions at a lower level). Advantages: - Most parts of the expression can be inserted or removed with line operations (ie. delete whole line or insert a new line). - No need to reformat the whole expression if you inserted or deleted a lot. - Parenthese levels are easy distinguishable.