[GNU Manual] [POSIX requirement] [Linux man] [FreeBSD man]
Summary
nl - number lines and write files
Lines of code: 601
Principal syscall: write()
Support syscalls: open(), close(), fadvise()
Options: 24 (11 short, 13 long)
Descended from nl introduced in System III (1982)
Added to Textutils in October 1992 [First version]
Number of revisions: 137
build_type_arg()
- Sets the section type and builds the regex patterncheck_section()
- Determines which section we are processing (header, body, footer)nl_file()
- The top-level nl procedureprint_lineno()
- Prints the line number as currently formattedproc_body()
- The body procedure for nlproc_footer()
- The footer procedure for nlproc_header()
- The header procedure for nlproc_text()
- Processes non-sectioned textprocess_file()
- Processes a single input file
die()
- Exit with mandatory non-zero error and message to stderrerror()
- Outputs error message to standard error with possible process termination
Setup
At global scope, nl declares a long list of globals that describe and control execution:
*body_del
is the body delimiter stringbody_del_len
is the length of the body delimiterbody_fastmap
is the body's fastmap supporting regex searchbody_regex
is the body's regular expression pattern bufferbody_type
is the style of the body section (-b [atnp])current_type
holds the type in use now -- should match the current sectionhave_read_stdin
is set if STDIN is ever read from*header_del
is the header delimiter stringheader_del_len
is the length of the header delimiterheader_fastmap
is the header's fastmap supporting regex searchheader_regex
is the header's regular expression pattern bufferheader_type
is the style of the header section (-h [atnp])*footer_del
is the footer delimiter stringfooter_del_len
is the length of the footer delimiterfooter_fastmap
is the footer's fastmap supporting regex searchfooter_regex
is the footer's regular expression pattern bufferfooter_type
is the style of the footer section (-f [atnp])line_buf
is the buffer to hold the next lineline_no
is the current line number to readpage_incr
is the amount to increment the line counter after each linereset_numbers
is a flag to force reset of line numbers after each pagestarting_line_number
is the initial line number
main() initializes the following:
c
- Holds the next option character for parsinglen
- The base length of the section delimiter (footer).ok
- Flag for execution success
Parsing kicks off with the short options passed as a string literal:
"h:b:f:v:i:pl:s:w:n:d:"
Parsing
Parsing collects the options that answer the questions we need to know from the user:
- What line number formatting applies to each section?
- What are the section delimiters?
- What line number do we start with and do we increment by 1?
- Do we reset line number after each page?
- Should we force the width of the line number field?
A subtle parsing task is that any user provided styles are compiled in to a pattern buffer as they are read in from options. See the the gnulib manual for more details
Parsing failures
These failure cases are explicitly checked:
- Unknown numbering style
- Unusual line starting numbers
- Non-sensical line increments or number widths (such as zero or negatives)
- Unknown formats
- Unknown options used
Failures result in a short error message followed by the usage instructions.
Execution
The nl execution path is predictable with few gotchas. For each file, perform the following procedure:
- For each input file:
- Open the file streams
- Read a line
- Check the line type to set the formats and regex pattern buffer
- Write the current line number and increment statistics
- Write the actual text line
- Close the file streams
- Close standard input if used
Failure cases:
- Unable to open or close input file
- Too many lines (overflow)
- A regular expression search failed
- Failure to write to STDOUT
- Failure to close input standard input (if used)
All failures at this stage output an error message to STDERR and return without displaying usage help