diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2023-09-16 18:06:33 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2023-09-16 18:06:33 +0000 |
commit | 390f0930aec93245c1ebe8fc63a86d68abb0cef9 (patch) | |
tree | 3b31ad1853932b1b49396cfe3b4834a17913baee /doc | |
parent | a0202ae7310caf2ec1113b2f54c4ff4bcdac1f59 (diff) | |
download | netpbm-mirror-390f0930aec93245c1ebe8fc63a86d68abb0cef9.tar.gz netpbm-mirror-390f0930aec93245c1ebe8fc63a86d68abb0cef9.tar.xz netpbm-mirror-390f0930aec93245c1ebe8fc63a86d68abb0cef9.zip |
Remove obsolete Netpbm programming rules
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@4660 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'doc')
-rw-r--r-- | doc/Netpbm.programming | 425 |
1 files changed, 0 insertions, 425 deletions
diff --git a/doc/Netpbm.programming b/doc/Netpbm.programming deleted file mode 100644 index c4d38ed4..00000000 --- a/doc/Netpbm.programming +++ /dev/null @@ -1,425 +0,0 @@ -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 -Netpbm Development release if possible. - -The preferred form for changes to existing files is a unified diff patch -- -the conventional Unix means of communicating code changes. A Subversion -'svn diff' creates that easily. - -You should update or create documentation too. But if you don't, the Netpbm -maintainer will do the update before releasing your code. The source files -for the documentation are the HTML files in the 'userguide' directory of the -Netpbm Subversion repository: https://svn.code.sf.net/p/netpbm/code/userguide. -The identical files are at http://netpbm.sourceforge.net/doc/ . - -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. - -As you code and test, do 'make dep' to create the header file dependency files -(depend.mk). Do it after 'configure' and every time you change the -dependencies. If you don't, 'make' won't know to recompile things when you -change the header files. For the convenience of users who are just building, -and not changing anything, the make files do work without a 'make dep', but do -so by automatically generating empty depend.mk files. This is not what you -want as a developer. - - -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. - -* You should try to use the "pam" library functions, which are newer than the - "pbm", "pgm", and "pnm" ones. The "pam" functions read and write all four - Netpbm formats and make code easier to write and to read. - -* A new program should generate PAM output. Note that any program that uses - the modern Netpbm library can read PAM even if it was designed for the older - formats. For other programs, one can convert PAM to the older formats - with 'pamtopnm'. - -* 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. - -* Always start the code in main() with a call to pm_proginit(). - -* 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 Pamdepth 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 runs 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. As an alternative to Standard Input, the user should be able - to name the input file as a non-option program argument. But the user - should not be able to name the output file if Standard Output is - feasible. That's just the Netpbm tradition. - -* 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. - -* Limit your C code to original ANSI C. Not C99. Not C89. Not C++. Don't - use // for comments or put declarations in the interior of a block. - Interpreted languages are OK, with Perl being preferred. - -* Make output invariant with input. The program should produce the same output - when fed the same input. This helps with testing and also reduces the chance - of bugs being encountered randomly. - - This is an issue when there are "don't care" bits in the output. You should - normally make those zero. - - The valgrind utility helps you identify instances of undefined bits and - bytes getting to output. - - A common place to find "don't care" bits is padding at the end of a raster - row. You can use pbm_cleanrowend_packed() to clear padding bits when you - use pbm_writepbmrow_packed(). - - Some programs are designed to produce random output. For those, include a - -seed option to specify the seed of the random number generator so that a - tester can make the output predictable. - - -* Never assume that input will be valid; make sure your program can - handle corrupt input gracefully. - - Watch out for arithmetic overflows caused by large numbers in the input. - - Be careful when writing decoders. Make sure that corruptions in the - input image do not cause buffer overruns. - - - - -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: - -* Use of pbm_init(), pgm_init(), ppm_init(), and pnm_init(). We use - pm_proginit() now. - -* 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 with constants. The first value a - variable has is a property of the algorithm that uses the variable, not of - the variable. A reader expects to see the whole algorithm in one place, as - opposed to having part of it hidden in the declaration of the algorithm - variables. - -* 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. - - Global constants are OK. - -* Do not use static variables, except for global constants. - -* 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 zero is a special value - that means "doesn't exist". - -* Do multiword variable names in camel case: "multiWordName". Underscores - waste valuable screen real estate. - -* If a variable's value is 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 "pointer to." E.g. if a - variable's value is a pointer to a color value, name it "colorP", not - "color". - -* A variable that represents the number of widgets should be "widgetCt", - ("widget count"), not "widgets". The latter should represent the actual - widgets instead. - -* 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). - -* Dynamically allocate buffers, for example raster buffers, rather than use - automatic variables. Accidentally overrunning an automatic variable, - typically stored on the stack, is much more dangerous than overrunning a - variable stored in the heap. - -* Use pm_asprintf() to compose strings, instead of sprintf(), strcat(), and - strcpy(). pm_asprintf() is essentially the same as GNU asprintf(), i.e. - sprintf(), except it dynamically allocates the result memory. This - effortlessly makes it impossible to overrun the result buffer. Use - pm_strfree() to free the result memory. You usually need not worry about - the pathological case that there is no memory available for the result, - because in that case, pm_asprintf() returns a constant string "OUT OF MEMORY" - and in most cases, that won't cause a disaster - just incorrect behavior that - is reasonable in the face of such a pathological situation. - -* Do not use the "register" qualifier of a variable. - - -MISCELLANEOUS -------------- - -Object code immutability across compilations --------------------------------------------- - -In a normal build process the same source code always produces the same object -code. (Of course this depends on your compiler and its settings: if your -compiler writes time stamps into the objects, they will naturally be different -with each compilation.) - -The file lib/compile.h has a time stamp which gets written into libnetpbm.a -and libnetpbm.so . This will affect individual binary executables if they are -statically linked. - -If compile.h does not exist it will be created by the program -buildtools/stamp-date. Once created, compile.h will stay unchanged until you -do "make clean". - -If you make alterations to source which are entirely cosmetic (for example, -changes to comments or amount of indentation) and need to confirm that the -actual program logic is not affected, compare the object files. If you need -to compare library files (or for some reason, statically linked binaries) make -sure that the same compile.h is used throughout. - |