Network Working Group M. Wildgrube Internet Draft Document: draft-wildgrube-preprfc-01.txt July 2000 Category: Experimental Expiration Date: December 2000 Preparation of text in RFC style STATUS OF THIS MEMO: This document is an Internet-Draft and is in full conformance with all provisions of Section 10 of RFC2026 [1]. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet- Drafts. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt The list of Internet-Draft Shadow Directories can be accessed at http://www.ietf.org/shadow.html. ABSTRACT This document contains a C program to convert a raw text (containing format commands similar to nroff) into a text which is in accordance with the demands of the RFC editors. DIFFERENCE TO THE PREVIOUS DRAFT: # Correction: If there was a sequence of blank - nonblank at the end of line, the non-blank character was stripped. # Introducing ordered and unordered lists. Wildgrube Experimental [Page 1] Preparation of text in RFC style July 2000 Table of Contents 1. Introduction 2. Description of the program 3. The rules for formatting a page 4. A Template for the raw text 5. Program Source 6. Security Considerations 7. Author's Address Wildgrube Experimental [Page 2] Preparation of text in RFC style July 2000 1. Introduction This document contains a C program (prep.c) to convert a raw text (containing format commands similar to nroff) into a text which is in accordance with the demands of the RFC editors (I hope). To format someone's draft in a correct manner is not an easy task. Especially Windows / Word user has some problems to use the way which is described in RFC 1543 by J. Postel. In the document http://www.ietf.org/ID-nits.html there is a link to a MS Word Template: A nice idea, but on my environment it does not work (even I used Word 97): The running footers disappeared. May be that the problem is my text-only printer driver. There is also a proposal in draft-shirey-ugli-01.txt (35 pages). I think the procedere described here is too complex (changing between UNIX with nroff and vi and Windows with Word). The proposed 'prep' program creates a file in RFC style from a raw text - ready! The program is tested under Windows 98 and Linux. It is to decide if the two alternatives, presented by the macros EXTRALINE (to put 3 blank lines instead of 2 after a new page) and LASTPAGE (for suppressing the form feed after the last page) has to be removed. Of course this document was created with prep.c. Wildgrube Experimental [Page 3] Preparation of text in RFC style July 2000 2. Description of the program Usage of program: prep infile outfile The infile contains text merged with some line commands (similar as for nroff): Commands starts with a dot (.) on column 1. These are the commands: . text comment .ce=text centered text .pw=number page width (default: 72) .ph=number page hight (default: 58) .in=number indentation (default: 0) .ti=text title line (no indentation) .ff form feed .ff=number conditional form feed: If at least 'number' textlines remains on the actual page, only an empty line will be produced. .hd=left/center/right running header .ft=left/center/right running footer .it=filename inserts a file .ww=on | off switching word wrapping on or off. .ol start of ordered list .ul=listchar start of unordered list (default listchar is '-') .el end of actual list. Lists can be nested up to 4 levels. .li list entry. Text of this entry starts at next line. .li=text list entry with text, text may be continued on following lines. There are also some additional rules for the text processing: At the begin of the input file text will be put out as is. It is assumed that this text is preformatted (document header block). The first .in command switches to "word floating mode". ".in=3" is usual. In this mode following rules applies: o Lines in succession (not separated by blank lines) defines a paragraph; words in a paragraph are shifted to match the page width optimally. o Besides blank lines a paragraph is terminated by the following line if this line begins with a space. The first word of this line starts always on the beginning of a new line (This first leading space will be removed). Wildgrube Experimental [Page 4] Preparation of text in RFC style July 2000 o With a series of lines prefixed with a space you can realize a chunk of formatted text. A bigger chunk of preformatted text can be enclosed in a pair of '.ww=on' / '.ww=off' commands. 3. The rules for formatting a page Following rules are realized by the program (together with the settings as in the template): o A page consists of exactly 58 lines. o Lines 56 and 57 are always empty o Line 58 contains the running footer, with: - authors last name on the left, - the status in the middle (centered) and - the page number in the form '[Page #]' on the right. o With the exception of the first page: Line 1 contains the running header, with: - 'RFC ####' on the left (not for drafts) - the title in the middle (centered) - the day of publishing on the right. o Line 2 and 3 of all pages are blank. o Line 1 of the first page is blank. o The length of all lines are limited to 72 printable characters, followed by a carriage return (x'0D') and a line feet (x'0A') character. o The footer line (including the trailing x'0D0A') is followed by the form feed character (x'0C') 4. A Template for the raw text With following template the correct layout for a draft will be initiated. The template contains some elements enclosed in angle brackets <...> which has to be substituted by user specifications, in detail: : abbreviation of user's first name : user's last name : of the document, like draft-username-project-vv.txt : of publishing : expiration date : of document : user's text
: user's post address. : user's email address. Wildgrube Experimental [Page 5] Preparation of text in RFC style July 2000 . begin of text . --+----1----+----2----+-- (helper for right margin) +----6----+----7-- Network Working Group . Internet Draft Document: Category: Informational Expiration Date: .ce= .in=3 .hd=// .ft=/Informational/[Page %d] .ti=STATUS OF THIS MEMO: This document is an Internet-Draft and is in full conformance with all provisions of Section 10 of RFC2026 [1]. . or: . This document is an Internet-Draft and is in full conformance . with all provisions of Section 10 of RFC2026 except that the . right to produce derivative works is not granted. . or: . This document is an Internet-Draft and is NOT offered in . accordance with Section 10 of RFC2026, and the author does not . provide the IETF with any rights other than to publish as an . Internet-Draft Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet-Drafts. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt The list of Internet-Draft Shadow Directories can be accessed at http://www.ietf.org/shadow.html. .ti=Abstract Wildgrube Experimental [Page 6] Preparation of text in RFC style July 2000 .ti=Table of Contents .ol .li=Introduction .li=Security Considerations .li=Author's Address .el .ff .ti=1. Introduction .ti=x. Security Considerations .ti=x. Author's Address email: 5. Program Source This source is to be compiled with 'cc prep.c -o prep' under unix. With the GNU compiler (LINUX) warning messages about the 2-character constants in function handle_command are issued. Let me know if this caused any problems in some environments. /*--- start of source -------------------------------------------*/ /* program: prep.c title: preparation of text to RFC style author: max wildgrube copyright: free for use and further development, no warranties. */ #define VERSION "prep 02 by max" /* alternatives: */ #define EXTRALINE 0 /* or 1 if extra line after new page */ #define LASTPAGE 0 /* or 1 if no FF after last page */ /* defaults: */ #define DEF_PAGEWIDTH 72 #define DEF_PAGEHIGHT 58 #define DEF_INDENT 0 Wildgrube Experimental [Page 7] Preparation of text in RFC style July 2000 #define LISTIND 3 #define DEF_ULCHAR '-' /* constants: */ #define LINELEN 1200 /* max. linesize */ #define HFLENGTH 100 /* max. length of any of the 3 parts */ /* of header and footer */ #define PBUFFLENGTH 1200 /* max. no. of chars in a paragraph (17 lines) */ #define MAXLEVEL 4 /* max. levels for lists */ #define OL_FMT "%2d.%d.%d.%d." #define SEPARATORS " .,-:;!?" #define BORDERDEL "/" /* delimiter for header and footer parts */ #define VALASS '=' #define FF 12 /* form feed */ #define CR 13 /* carriage return */ #define LF 10 /* line feed */ #define EL "" /* empty line */ #define SP ' ' /* space */ #include #include #include static struct GLOBALS { FILE *infile, *outfile; unsigned int pw, ph, in; /* page width, page height, indent */ char ce [HFLENGTH]; /* center line */ char hd [HFLENGTH]; /* header */ char ft [HFLENGTH]; /* footer */ int header_set, footer_set, format, listflag; /* boolean */ int in_line_counter, pg_line_counter, page_counter; int ul_level, ol_level; char parabuff [PBUFFLENGTH]; char listtype; /* == 'u' or 'o' */ char ul_char [MAXLEVEL+1]; /* actual char for unordered list */ int ol_number [MAXLEVEL+1]; /* ol_number [0] will be ignored */ } g; /* prototypes */ void handle_command (char *); void flush_para_buffer (void); void fill_para_buffer (char *); void putout (char *); /* put output string to file */ void do_form_feed (int); char *make_border (char *); void strip (char *); /* strips blanks and CR at end of line */ /*---------------------------------------------------------------*/ int main (int argc, char **argv) Wildgrube Experimental [Page 8] Preparation of text in RFC style July 2000 { char inpline [LINELEN]; if (argc != 3) { printf ("%s at %s\nusage: %s infile outfile\n", VERSION, __DATE__, argv [0]); return 1; } if (!(g.infile = fopen (argv [1], "rt"))) { printf ("could not open input file %s\n", argv [1]); return 1; } if (!(g.outfile = fopen (argv [2], "wb"))) { printf ("could not open output file %s\n", argv [2]); return 1; } /* defaults: */ g.pw = DEF_PAGEWIDTH; g.ph = DEF_PAGEHIGHT; g.in = DEF_INDENT; g.page_counter = 1; g.ul_level = 0; #if EXTRALINE putout (EL); #endif /* loop over input lines: */ while (fgets (inpline, LINELEN-2, g.infile)) { ++g.in_line_counter; strip (inpline); switch (inpline [0]) { case '.': handle_command (inpline); break; case SP: flush_para_buffer (); fill_para_buffer (inpline + 1); break; case 0 : flush_para_buffer (); putout (EL); Wildgrube Experimental [Page 9] Preparation of text in RFC style July 2000 break; default: fill_para_buffer (inpline); } /*end-switch*/ } /*end-while*/ flush_para_buffer (); do_form_feed (LASTPAGE); /* 1 = end of file (no FF) */ fclose (g.infile); fclose (g.outfile); } /*end- main */ /*---------------------------------------------------------------*/ void handle_command (char *p_inline) { short cmd; int datalen, oldind; unsigned int wnum; char wrkline [LINELEN], wrkline1 [LINELEN]; FILE *ifile; if (p_inline [1] <= SP) return; /* comment */ /* indifference to big or little endian: */ cmd = (((short)*(p_inline+1) & 0xFF) << 8) | ((short)*(p_inline+2) & 0xFF); switch (cmd) { case 'pw': /* page width **********************************/ sscanf (p_inline+4, "%d", &g.pw); break; case 'ph': /* page height ********************************/ sscanf (p_inline+4, "%d", &g.ph); break; case 'in': /* indentation ********************************/ sscanf (p_inline+4, "%d", &g.in); flush_para_buffer (); g.format = 1; break; case 'ti': /* title line *********************************/ if (g.ul_level > 0) { printf ("list structure not closed inside a chapter! (line %d)\n", g.in_line_counter); exit (1); } Wildgrube Experimental [Page 10] Preparation of text in RFC style July 2000 flush_para_buffer (); oldind = g.in; g.in = 0; putout (p_inline + 4); g.in = oldind; break; case 'ce': /* center line ********************************/ flush_para_buffer (); memset (wrkline, SP, g.pw); datalen = strlen (p_inline) - 4; if (datalen <= 0) return; strcpy (wrkline + (g.pw - datalen)/2, p_inline + 4); putout (wrkline); break; case 'ff': /* form feed (new page) *************************/ wnum = 999; sscanf (p_inline+4, "%d", &wnum); flush_para_buffer (); if (g.pg_line_counter + wnum > g.ph - 3*g.footer_set) do_form_feed (0); /* 0 = not the end of file */ else putout (EL); break; case 'hd': /* header **************************************/ strcpy (g.hd, p_inline+4); g.header_set = 1; break; case 'ft': /* footer **************************************/ strcpy (g.ft, p_inline+4); g.footer_set = 1; break; case 'it': /* insert file *********************************/ if (!(ifile = fopen (p_inline+4, "rt"))) { printf ("could not open insert file '%s' at line %d\n", p_inline+4, g.in_line_counter); exit (1); } while (fgets (wrkline, LINELEN-2, ifile)) { strip (wrkline); putout (wrkline); } fclose (ifile); break; Wildgrube Experimental [Page 11] Preparation of text in RFC style July 2000 case 'ww': /* word-wrap modus *****************************/ if (0 == strcmp (p_inline+4, "on")) { g.format = 1; break; } if (0 == strcmp (p_inline+4, "off")) { g.format = 0; break; } printf ("parameter error for cmd '.ww=on|off' at line %d: %s\n", g.in_line_counter, p_inline); break; case 'ul': /* unordered list *******************************/ g.listtype = 'u'; flush_para_buffer (); g.in += LISTIND; g.ul_level += 1; g.ul_char [g.ul_level] = p_inline [3] == VALASS ? p_inline [4] : DEF_ULCHAR; break; case 'ol': /* ordered list *********************************/ g.listtype = 'o'; flush_para_buffer (); g.in += LISTIND; g.ol_level += 1; break; case 'el': /* end of list **********************************/ flush_para_buffer (); g.in -= LISTIND; if (g.listtype == 'o') g.ol_level -= 1; else g.ul_level -= 1; break; case 'li': /* list item ************************************/ flush_para_buffer (); g.listflag = 1; g.in -= LISTIND; if (g.listtype == 'o') { g.ol_number [g.ol_level] += 1; memset (wrkline1, 0, LINELEN); sprintf (wrkline, strncpy (wrkline1, OL_FMT, 3 * g.ol_level + 1) , g.ol_number [1] Wildgrube Experimental [Page 12] Preparation of text in RFC style July 2000 , g.ol_number [2] , g.ol_number [3] , g.ol_number [4] ); fill_para_buffer (wrkline); } else { wrkline [0] = g.ul_char [g.ul_level]; memset (wrkline+1, SP, LISTIND-1); wrkline [LISTIND] = 0; fill_para_buffer (wrkline); } if (p_inline [3] == VALASS) fill_para_buffer (p_inline+4); break; default: printf ("unkonwn command at line %d: %s\n", g.in_line_counter, p_inline); }/*end-switch cmd */ }/*end- handle_command */ /*---------------------------------------------------------------*/ void fill_para_buffer (char *p_inline) { static char *cursor; static int actual_size; int datalen; if (p_inline == NULL) /* reset buffer after flush */ { g.parabuff [0] = 0; cursor = NULL; actual_size = 0; return; } if (! g.format) { putout (p_inline); return; } datalen = strlen (p_inline); if (actual_size + datalen + 1 > PBUFFLENGTH) { printf ("Paragraph buffer overflow!\n" Wildgrube Experimental [Page 13] Preparation of text in RFC style July 2000 "Please insert some breaks.\n"); exit (1); } if (cursor == NULL) cursor = g.parabuff; else if (*(cursor-1) != SP) *(cursor++) = SP; strcpy (cursor, p_inline); cursor += datalen; actual_size += datalen; }/*end- fill_para_buffer */ /*---------------------------------------------------------------*/ void flush_para_buffer (void) { char outline [LINELEN] = EL; unsigned int wordlen; char *cursor = g.parabuff; if (*cursor == 0) return; if (g.in > 0) memset (outline, SP, g.in); outline [g.in] = 0; while (*cursor) { wordlen = strcspn (cursor, SEPARATORS); if (*(cursor + wordlen)) wordlen += 1; if (strlen (outline) + wordlen > g.pw) { putout (outline); if (g.in > 0) memset (outline, SP, g.in); outline [g.in] = 0; if (*cursor == SP) cursor++; } strncat (outline, cursor, wordlen); cursor += wordlen; } putout (outline); fill_para_buffer (NULL); /* reset buffer */ }/*end- flush_para_buffer */ /*---------------------------------------------------------------*/ void do_form_feed (int p_last) Wildgrube Experimental [Page 14] Preparation of text in RFC style July 2000 { while (g.pg_line_counter++ < (int)g.ph - 1) { fputc (CR, g.outfile); fputc (LF, g.outfile); } if (g.footer_set) { fputs (make_border (g.ft), g.outfile); fputc (CR, g.outfile); fputc (LF, g.outfile); } if (! p_last) { fputc (FF, g.outfile); #if EXTRALINE fputc (CR, g.outfile); fputc (LF, g.outfile); #endif g.pg_line_counter = EXTRALINE; } g.page_counter += 1; }/*end- do_form_feed */ /*---------------------------------------------------------------*/ void putout (char *p_str) /* put output string to file */ { if (g.pg_line_counter == (int)g.ph - 3 * g.footer_set) { if (p_str [0] == 0) return; /* no empty lines at end of page */ do_form_feed (0); } if (g.pg_line_counter == EXTRALINE && g.header_set) { fputs (make_border (g.hd), g.outfile); fputc (CR, g.outfile); fputc (LF, g.outfile); fputc (CR, g.outfile); fputc (LF, g.outfile); fputc (CR, g.outfile); fputc (LF, g.outfile); g.pg_line_counter += 3; } fputs (p_str, g.outfile); fputc (CR, g.outfile); fputc (LF, g.outfile); if (g.listflag) { g.listflag = 0; g.in += LISTIND; } Wildgrube Experimental [Page 15] Preparation of text in RFC style July 2000 g.pg_line_counter += 1; } /*end- putout */ /*---------------------------------------------------------------*/ char *make_border (char *p_str) { static char line [LINELEN]; char *token, work [LINELEN]; unsigned int len; if (strlen (p_str) > g.pw) { printf ("line: %d: header / footer too long\n", g.in_line_counter); exit (1); } memset (line, SP, g.pw); sprintf (work, p_str, g.page_counter); token = strtok (work, BORDERDEL); if (work [0] == BORDERDEL [0]) goto center; if (token == NULL) goto error; len =strlen (token); memcpy (line, token, len); token = strtok (NULL, BORDERDEL); center: if (token == NULL) goto error; len =strlen (token); memcpy (line + (g.pw - len)/2, token, len); token = strtok (NULL, BORDERDEL); if (token == NULL) goto error; len =strlen (token); memcpy (line + g.pw - len, token, len); line [g.pw] = 0; return line; error: printf ("line %d: '%s'\nwrong param for header/footer: " "format 'left/center/right'\n", g.in_line_counter, p_str); exit (1); }/*end- make_border */ /*---------------------------------------------------------------*/ Wildgrube Experimental [Page 16] Preparation of text in RFC style July 2000 void strip (char *p_str) { int ixLastChar; if (p_str == NULL) return; ixLastChar = strlen (p_str) - 1; if (p_str [ixLastChar] == '\n') p_str [ixLastChar--] = 0; if (ixLastChar >= 0 && p_str [ixLastChar] == CR) p_str [ixLastChar--] = 0; while (ixLastChar >= 0 && p_str [ixLastChar] == SP) p_str [ixLastChar--] = 0; }/*end- strip */ /*--- end of source ---------------------------------------------*/ 6. Security Considerations There are no Security Considerations. 7. Author's Address Max Wildgrube Schlossstr. 120 60486 Frankfurt Germany email: max@wildgrube.com You can download the newest program source, template and docu from this web site: www.pinpi.com/prep Wildgrube Experimental [Page 17]