
How I use GNU Indent
Compilers don’t care if you use spaces or tabs and they don’t care if you put instructions on new lines; as long as program statements end with a semicolon (in C-like languages) and is otherwise syntactically correct, your program should compile fine.
But these are long-standing and strongly-help beliefs among many developers. In fact, you may find a patch is rejected by a program maintainer because your coding style does not match theirs.
That’s where it helps to have a tool to do all of the hard work for you, to reformat your source code so statements are nested and arranged correctly.
Coding styles
Throughout the history of programming, there have been several common styles of writing C source code. The original “Kernighan and Ritchie” style, used throughout their original book that described the C programming language, was one common style. The UCB developers adopted a slightly different coding style. The GNU Project uses its own style for coding, as do the Linux kernel developers.
Here’s a sample program to show you what I mean. The program prints the numbers from 1 to 10. For any number less than 5, the program prints foo; for any numbers greater than 5, it prints bar. The program might be written in original “K&R” style like this:
#include <stdio.h>
int main()
{
int i;
for (i = 1; i <= 10; i++) {
printf("%d", i);
if (i < 5) {
puts("foo");
} else if (i > 5) {
puts("bar");
}
putchar('\n');
}
return 0;
}
Note that lines are indented by four spaces at each level. Also, brackets are inline with the if
and else
statements, and the else
statement is “cuddled” next to the ending bracket for the if
statement. This style was efficient to write in a book, requiring fewer lines (and thus fewer pages to print) at a time when printed books were more expensive to produce.
The GNU coding style favors vertical space instead, so the program source code is easier to read on a high resolution display. The brackets for the if
and else
appear on the next line, indented by a few extra spaces. Also, the return type for the main
function is on a different line:
#include <stdio.h>
int
main ()
{
int i;
for (i = 1; i <= 10; i++)
{
printf ("%d", i);
if (i < 5)
{
puts ("foo");
}
else if (i > 5)
{
puts ("bar");
}
putchar ('\n');
}
return 0;
}
The Linux kernel developers adopt a different style, preferring tabs at each indent level. Also, the else
is “cuddled” after the closing bracket from the if
block. The result is code that is more “dense” vertically and easier to adjust to each programmer’s preferences simply by changing the tab stops in the text editor:
#include <stdio.h>
int main()
{
int i;
for (i = 1; i <= 10; i++) {
printf("%d", i);
if (i < 5) {
puts("foo");
} else if (i > 5) {
puts("bar");
}
putchar('\n');
}
return 0;
}
Formatting code with Indent
When a Unix variant was under development at the University of California at Berkeley, the UCB developers added new programs that were useful to them. One such program was Indent, which reformats C program source code according to a set of rules. This is such a useful program that the GNU Project created their own implementation, which you should find on every Linux system.
I’ll often create a sample program so I can write an article about it. When I do, I want to be sure that the code is easy to read, and doesn’t take up too much room on the screen or in print. I’ve been writing C code since the 1990s, not long after the ANSI organization adopted the C programming language standard (“ANSI C”) so perhaps it’s not too surprising that my preferred coding style is not too different from the “K&R C” style. However, I do have a few differences due to personal preference: I don’t use tabs, I like to align comments at column 40 (half-way across an 80-column terminal display) and I don’t “cuddle” the else
and the if
blocks.
When I need to reformat my code, I use Indent to do it for me. Because I don’t want to type all of the command line options all the time, I wrote a short script to run Indent for me:
#!/bin/bash
indent -kr --no-tabs -c40 --dont-cuddle-else "$@"
This starts Indent with the defaults for “K&R C” style, then modifies it to never use tabs (--no-tabs
) and align same-line comments at column 40 (-c40)
. Finally, the command line tells Indent not to “cuddle” the else
and if
blocks together, so it starts else
on a new line. The "$@"
at the end is a Bash expansion to include any files that I might specify on the command line, so Indent can process each of them. I called this script reindent because it’s something I can easily remember, and it’s not exactly the same as the indent command.
Reformatting the foo-bar program
Let’s look at the sample “foo-bar” program. To reformat the source code to my preferred style, I type:
$ reindent foo.c
This saves a copy of the old foo.c
file as foo.c~
which is a nice backup in case I need to revert to it. The original foo.c
gets reformatted to use my preferred style:
#include <stdio.h>
int main()
{
int i;
for (i = 1; i <= 10; i++) {
printf("%d", i);
if (i < 5) {
puts("foo");
}
else if (i > 5) {
puts("bar");
}
putchar('\n');
}
return 0;
}
Reformatting made easy
Indent is also really useful for times when I might create a program very quickly, without paying too much attention to my coding style. With Indent, I know that I can always reformat my code to use my preferred style.
Here’s one example. I wrote a program to generate the printable ASCII characters. I wrote this using ed, the classic line editor. I just tapped out a quick program without any special formatting, because it was easier to write a program that way in a line editor:
#include <stdio.h>
int main()
{
int r, c;
/* body */
for (r=' ';r<=127;r+=16) {
for (c=0;c<16;c++) {
putchar(r+c); putchar(' ');
}
putchar('\n'); /* end of line */
}
return 0;
}
That program generates a simple ASCII table, but without any notation to tell me the value for each character:
! " # $ % & ' ( ) * + , - . /
0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ A B C D E F G H I J K L M N O
P Q R S T U V W X Y Z [ \ ] ^ _
` a b c d e f g h i j k l m n o
p q r s t u v w x y z { | } ~
I made a quick edit to add a table header and labels on each row.
-- 0 1 2 3 4 5 6 7 8 9 a b c d e f
20 ! " # $ % & ' ( ) * + , - . /
30 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
40 @ A B C D E F G H I J K L M N O
50 P Q R S T U V W X Y Z [ \ ] ^ _
60 ` a b c d e f g h i j k l m n o
70 p q r s t u v w x y z { | } ~
For example, this shows that the space character is 0x20, capital A is 0x41, and the underscore character is 0x5f.
The easiest way to update the program was to copy the inner for
loop to generate a header, then add a new line to write a label, and another line to make a placeholder on the header row. The updated program doesn’t look very pretty, but is syntactically correct, and compiles without warnings:
#include <stdio.h>
int main()
{
int r, c;
/* header */
fputs("-- ", stdout);
for (c=0;c<16;c++) {
printf("%x ", c);
}
putchar('\n'); /* end of line */
/* body */
for (r=' ';r<=127;r+=16) {
printf("%x ", r);
for (c=0;c<16;c++) {
putchar(r+c); putchar(' ');
}
putchar('\n'); /* end of line */
}
return 0;
}
To reformat the program to use my preferred coding style, I ran my reindent script and let Indent do the work for me:
$ reindent ascii.c
This reformats the program source code in a way that makes it more readable and consistent:
#include <stdio.h>
int main()
{
int r, c;
/* header */
fputs("-- ", stdout);
for (c = 0; c < 16; c++) {
printf("%x ", c);
}
putchar('\n'); /* end of line */
/* body */
for (r = ' '; r <= 127; r += 16) {
printf("%x ", r);
for (c = 0; c < 16; c++) {
putchar(r + c);
putchar(' ');
}
putchar('\n'); /* end of line */
}
return 0;
}
Try GNU Indent
GNU Indent comes with a bazillion command line options to support all kinds of code formatting configurations. Read the man indent
documentation to read more about what Indent can do for you. You can install Indent on your system using your package manager, such as dnf
on Fedora Linux:
$ sudo dnf install indent