diff options
Diffstat (limited to 'doc/Netpbm.programming')
-rw-r--r-- | doc/Netpbm.programming | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/doc/Netpbm.programming b/doc/Netpbm.programming new file mode 100644 index 00000000..14de8b08 --- /dev/null +++ b/doc/Netpbm.programming @@ -0,0 +1,358 @@ +This file is an attempt to give some basic guidelines for those who +wish to write new Netpbm programs. The guidelines ensure: + + A) Your program functions consistently with the rest of the package. + + B) Your program works in myriad environments on which you can't test it. + + C) You don't miss an important detail of the Netpbm formats. + + D) Your program is immune to small changes in the Netpbm formats. + +The easiest way to write your own Netpbm program is to take an +existing one, similar to the one you want to write, and to modify +it. This saves a lot of time, and ensures conformity with these rules. +But pick a recent one (check the file modification date and the +doc/HISTORY file), because many things you see in old programs are +grandfathered in. Pamtopnm is good example of a thoroughly modern +Netpbm program. Pamcut is another good one. + + +COLLECTION PARAMETERS +--------------------- + +The philosophy that guides what goes into Netpbm and what doesn't is +one of maximum distribution of function. Contributions to Netpbm are +refused very rarely, and usually because there is already some better +way to do what the contribution attempts to do, so that the +contribution would just make the package more confusing and thus +harder to use. The second most common reason for declining a +contribution is that it falls well outside Netpbm's scope. That too +would make the package more confusing and thus harder to use. + +"Nobody will use that" is not a reason for leaving something out of +Netpbm. + +"That's different from how other Netpbm programs work" is similarly +not an excuse for refusing distribution of a feature. (But it's a +good reason to accept a change to make a feature consistent!) + +The standard for adding something to Netpbm isn't that it be perfect, +or even a net improvement. The standard is that it not make Netpbm +worse. Poor quality additions are made all the time, because a +program that doesn't work well is usually no worse than no program at +all. + + +DEVELOPMENT PROCESS +------------------- + +Just email your code to the Netpbm maintainer. Base it on the most recent +Latest Netpbm release if possible. It's a good idea to ask the maintainer +before starting a big change to a file to make sure it doesn't conflict +with some other work that has been done since the last release. + +The preferred form for changes to existing files is a unified diff patch -- +the conventional Unix means of communicating code changes. + +You should update or create documentation too. The source files for +the documentation are the HTML files at +http://netpbm.sourceforge.net.doc/ . But if you don't, the Netpbm +maintainer will do the update before releasing your code. + +There are some automated tests in the package - shell scripts in files +named such as "pbmtog3.test". You can use those to verify your +changes. You should also add to these tests and create new ones. But +most developers don't. + + +CODING GUIDELINES +----------------- + +The Netpbm maintainer accepts programs that do not meet these guidelines, +so don't feel you need to hold back a contribution because you wrote it +before you read these. + +* See the Netpbm library documentation to see what library functions + you should be using and how. You can find it at: + + http://netpbm.sourceforge.net/doc/libnetpbm.html + +* See the specifications of the Netpbm formats at: + + http://netpbm.sourceforge.net/doc/pbm.html + http://netpbm.sourceforge.net/doc/pgm.html + http://netpbm.sourceforge.net/doc/ppm.html + http://netpbm.sourceforge.net/doc/pam.html + + but don't depend very much on these; use the library functions + instead to read and write the image files. + +* Your new program must belong to one of the four Netpbm classes, + which are loosely based on the Netpbm formats. They are defined as + follows: + + pbm: These programs take PBM (bitmap - pixels are black or white) + files as input or output or both. They never have PGM or PPM + primary input or output. They use the libpbm Netpbm + library. They have "pbm" in their names. + + pgm: These programs take PBM or PGM (grayscale) files as input, or + produce PGM files as output, or both. They treat PBM input + as if it were the equivalent PGM input. They never produce PBM + primary output and never have PPM primary input or output. + They use the libpbm and libpgm Netpbm libraries. They have + "pgm" in their names. + + ppm: These programs take PBM or PGM or PPM (color) files as input, + or produce PPM files as output, or both. They treat PBM and + PGM input as if it were the equivalent PPM input. They never + produce PBM or PGM primary output. They use the libpbm, + libpgm, and libppm Netpbm libraries. They have "ppm" in their + names. + + pnm: These are the most general programs. They handle all four + Netpbm formats (PBM, PGM, PPM, and PAM). They use all Netpbm + formats as input or output or both. They recognize the + difference between PBM, PGM, and PPM input, so a PBM input + might produce a different result than the equivalent PGM input. + These programs use the libpbm, libpgm, libppm, and libpnm + Netpbm libraries. They have "pnm" or "pam" in their names. + + Decide which one of these classes your program belongs to. Your choice + determines the proper naming of the program and which set of library + subroutines you should use. + +* If your program involves transparency (alpha masks), you have two + alternatives. The older method is to use a separate PGM file for the + alpha mask, in the style of Pngtopnm/Pnmtopng and Giftopnm/Pnmtogif, + and use command syntax like those programs. See the PGM format spec + for details. + + A newer method involves a PAM image with an alpha plane (tuple type + RGB_ALPHA, etc.). This is preferred because it's easier for users + to pipe stuff around. Pamtotga is an example of this. + +* Declare all your symbols except 'main' as static so that they won't + cause problems to other programs when you do a "merge build" of + Netpbm. + +* Declare main() with return type 'int', not 'void'. Some systems won't + compile void main(). + +* Always start the code in main() with a call to p?m_init(). + +* Use shhopt for option processing. i.e. call optParseOptions3(). + This is really easy if you just copy the parseCommandLine() function + and struct cmdlineInfo declaration from pamcut.c and adapt it to + your program. + + When you do this, you get a command line syntax consistent with all the + other Netpbm programs, you save coding time and debugging time, and it + is trivial to add options later on. + + Do not use shhopt's short option alternative unless you need to be + compatible with another program that has short options. Short + options are traditional one-character Unix options, which can be + stacked up like "foo -cderx myfile", and they are far too unfriendly + to be accepted by the Netpbm philosophy. Note that long options in + shhopt can always be abbreviated to the shortest unique prefix, even + one character. + + In messages and examples in documentation, always refer to an option + by its full name, not the abbreviation you usually use. E.g. if you have + a "-width" option which can be abbreviated "-w", don't use -w in + documentation. -width is far clearer. + +* Use pm_error() and pm_message() for error messages and other messages. + Note that the universal -quiet option (processed by p?m_init()) causes + messages issued via pm_message() to be suppressed. And that's what + Netpbm's architecture requires. + +* The argument to pm_error() and pm_message() is a string of text, not + a formatted message. Don't put newlines or other formatting characters + in it. These subroutines are designed to be flexible in how they issue + the messages. + +* Use MALLOCARRAY() to allocate space for an array and MALLOCVAR to allocate + space for a non-array variable. In fact, you usually want to save some + programming tedium and use the NOFAIL versions of these (they never fail + because they abort the program if memory is not available). These avoid + array bounds violations. + +* Use pm_tmpfile() to make a temporary file. This avoids races that can + be used to compromise security. + +* Properly use maxvals. As explained in the format specifications, every + sample value in an image must be interpreted relative to the image's + maxval. For example, a pixel with value 24 in an image with maxval 24 + is the same brightness as a pixel with value 255 in an image with a + maxval of 255. + + 255 is a popular maxval (use the PPM_MAXMAXVAL etc. macros) because it + makes samples fit in a single byte and at one time was the maximum + possible maxval in the format. + + Note that the Pnmdepth program converts an image from one maxval to + another. + +* Don't include extra function. If you can already do something by + piping the input or output of your program through another Netpbm + program, don't make an option on your program to do it. That's the + Netpbm philosophy -- simple building blocks. + + Similarly, if your program does two things which would be useful + separately, write two programs and advise users to pipe them + together. Or add a third program that simply calls the first two. + +* Your program should, if appropriate, allow the user to use Standard + Input and Output for images. This is because Netpbm users commonly + use pipes. + +* Don't forget to write a proper html documentation page. Get an + example to use as a template from + <http://netpbm.sourceforge.net/doc/directory.html>. + +* No Netpbm source code may contain tab characters. If you + generate such a file, the Netpbm maintainer will convert it to spaces + and possibly change all your indenting at the same time. Tabs are not + appropriate for code that multiple people must edit because they don't + necessarily look the same in every editing environment, and in most + editing environments, you can't even tell which spaces are actually in + the file and which came from the editor, expanding tabs. Spaces, on + the other hand, look the same for everyone. Modern editors let you + compose code just as easily with spaces as with tabs. + + + + +DISCONTINUED CODING GUIDELINES +------------------------------ + +Here are some things you will see in old Netpbm programs, but they are +obsolete and you shouldn't propagate them into a new program: + +* Tolerating non-standard C libraries. You may assume all users have + ANSI and POSIX compliant C libraries. E.g. use strrchr() and forget + about rindex(). + +* pm_keymatch() for option processing. Use shhopt instead, as described + above. + +* optParseOptions() and optParseOptions2(). These are obsoleted + by optParseOptions3(), which is easier to use and more powerful. + +* K&R C function declarations. Always use ANSI function declarations + exclusively (e.g. use + + void foo(int arg1){} + + instead of + + void foo(arg1) + int arg1; + {} + +* for (col=0, xP = row; col < cols; col++, xP++) + foo(*xP); + + This was done in the days before optimizing compilers because it ran faster + than the more readable: + + for (col=0; col < cols; col++) + foo(row[col]); + + Now, just use the latter. + + +CODING STYLE +------------ + +We do not generally mandate any basic coding style in these +guidelines. Where you put your braces is a matter of personal style +and other people working with your code will just have to live with +it. However, people working with your code might just recode it into +another style if it makes it easier for them to read the code and +have confidence in their changes. + +But if you have no preference, the following is what the Netpbm +maintainer currently prefers. Essentially, it is clean, elegant +computer science-type code rather than brute force engineering-type +code. Modular and structured above all. + +* No gotos. This includes all variations of goto: break, continue, leave, + mid-function return. An exception: aborting the entire program in the + middle of something is allowed. + +* No functions with side effects. This is a tough one, since + functions with side effects is highly traditional C. In fact, the + creators of C didn't even have a concept of a subroutine. However, + the average human brain, especially one trained in math, cannot + easily follow code where a function both computes a value and does + other stuff. + + For the purpose of this discussion, we say that a C function whose return + type is void is not a "function," but a "subroutine." + + So a function should never change anything in the program. All it does + is compute a value. + + Where you have to call an external function that has side effects (virtually + anything in the standard C library, for example), put it in a simple + assignment function that captures its return value and otherwise consider + it a subroutine: + + rc = fopen(...) + if (rc ...) + +* No reuse of variables. Most variables should be set at most once. + Don't say "A is 5" and then later on, "no, A is 6." A reader + should be able to take that first "A is 5" as a statement of fact + and not hunt for all the places it might be contradicted. A + variable that represents the state of execution of an algorithm is an + obvious exception. + + Use "const" everywhere you possibly can. + + Especially never modify the argument of a function. All function arguments + should be "const". + + Don't use initializers except for constants. If you're going to set + a variable twice, do it out in the open; don't hide one in the declaration + of the variable. + +* Avoid global variables. Sometimes, a value is truly global and + passing it as a parameter just muddies the code. But most of the + time, any external value to which a function refers should be one of + its arguments. + + Declare a variable in the most local scope possible. + +* Keep subroutines small. Generally under 50 lines. But if the + routine is a long sequence of simple, similar things, it's OK for it + run on ad infinitem. + +* Use the type "bool" for boolean variables. "bool" is defined in Netpbm + headers. Use TRUE and FALSE as its values. + +* Do not say "if (a)" when you mean "if (a != 0)". Use "if (a)" only if + a is a boolean variable. Or where it's defined so that a zero value + means ("doesn't exist"). + +* Do multiword variable names like this: "multiWordName". Underscores waste + valuable screen real estate. + +* If a variable's value is the address of something (a pointer to something), + it's name should reflect that, as opposed to lying and saying the value is + the thing pointed to. A "P" on the end is the conventional way to say + "address of." E.g. if a variable's value is the address of a color value, + name it "colorP", not "color". + +* Put "const" as close as possible to the thing that is constant. + "int const width", not "const int width". When pointers to + constants are involved, it makes it much easier to read. + +* Free something in the same subroutine that allocates it. Have exactly + one free per allocate (this comes naturally if you eliminate gotos). + + |