Decoded: Rogue (1980) by Toy, Arnold, Wichman
DOS version (1983) by Mel Sibony and Jon Lane
Source file: IO.C
Beginner friendly, line-by-line code walkthrough by MaiZure

IO.C holds many miscellaneous I/O functions tied to the game. It ties
together the game with the curses library. One highlight is a custom 
sprintf implementation.

Original code:
https://britzl.github.io/roguearchive/

Original code with line numbers
http://www.maizure.org/projects/decoded-rogue/IO_linenum.txt




1     COMMENT
2     COMMENT
3     COMMENT
4     COMMENT
5     COMMENT
6     BLANK
7     Include the game header
8     Include the console management header
9     Include external variable header
10    BLANK
11    Import functions from curses.h
12    Import scr_type from curses.h
13    Import tick counter from DOS.ASM
14    BLANK
15    Define macro for armor class display
16    Import stpbrk from curses.h
17    Define macro that resolves column count based on screen size
18    COMMENT
19    COMMENT
20    COMMENT
21    COMMENT
22    Import the main message buffer from INIT.C
23    Declare a global for message position index
24    BLANK
25    COMMENT
26    BLANK

PRINT TERSE MESSAGE

27    Declare ifterse with seven arguments  
28    Arguments 1 and 2 are strings for both terse and full messages
29    Arguments 3-7 are attributes/variables within the string
30    BLOCK START - ifterse, returns strings based on game terse status
31    If the game is in terse mode...
32    Print out the formatted terse message...
33    Otherwise
34    Print out the formatted full message
35    BLOCK END - ifterse
36    BLANK

PRINT MESSAGE

37    Declare msg with six arguments
38    Argument 1 is the message string
39    Arguments 2-6 are the attributes for the string
40    BLOCK START - msg, prints a message to the screen
41    COMMENT
42    COMMENT
43    COMMENT
44    If the message is blank
45    BLOCK START - blank message
46    Move the cursor to the top left
47    Clear the line
48    Message position is zero
49    Return to caller
50    BLOCK END - blank message
51    COMMENT
52    COMMENT
53    COMMENT
54    Add input message to the current message buffer
55    Post the message to the screen
56    BLOCK END - 
57    BLANK
58    COMMENT
59    COMMENT
60    COMMENT
61    COMMENT
62    COMMENT

EXTEND MESSAGE

63    Defines addmsg with six arguments
64    Argument 1 is the message string
65    Arguments 2-6 are the message attributes
66    BLOCK START - addmsg, adds formatted message to the current message
67    Add input message to the current message buffer
68    BLOCK END - addmsg
69    BLANK
70    COMMENT
71    COMMENT
72    COMMENT
73    COMMENT
74    COMMENT

FINALIZE MESSAGE

75    Define endmsg with no arguments
76    BLOCK START - endmsg, pushes the message buffer to the screen
77    If we need to remember the previous message before clobbering now...
78    Copy the message to a backup message buffer
79    If we're positioned in the middle of the message buffer already...
80    Refresh the screen without refreshing the playing field
81    Move the cursor to the status line, but at the current position
82    Print a toggle to continue with the rest of the message
83    End of check for incomplete message
84    COMMENT
85    COMMENT
86    COMMENT
87    COMMENT
88    If the first letter of the message buffer is lower and not a label..
89    Change the letter to upper case
90    Print the message buffer
91    Cache the message buffer current position
92    Reset the buffer position to 0
93    BLOCK END - endmsg
94    BLANK
95    BLANK
96    COMMENT
97    COMMENT
98    COMMENT

PAUSE MESSAGE

99    Define more with one argument
100   Argument 1 is the message buffer
101   BLOCK START - more, pauses messages to wait for user input
102   Declare x and y screen position integers
103   Declare an iterator and a size integer
104   Declare another message buffer
105   Declare flag if there is more to the current message
106   Declare a flag is the current message is covering a previous message
107   BLANK
108   Get the size of the message
109   Get the current position of the cursor (note that x is row #)
110   COMMENT
111   COMMENT
112   COMMENT
113   COMMENT
114   If we're not on row 1, then there was a wrap so...
115   Set x to 0
116   Set y to the last column
117   End check for wrap
118   If the message won't fit on the remainder of the line...
119   Move the cursor to a position that just fits
120   Set flag that the message covers another message. (Triggers more check)
121   End check for long messages
122   BLANK
123   Loop through the message buffer
124   Read the the console at cursor position in to the message buffer
125   If the cursor still has space on the line..
126   Move the cursor right by one space
127   Otherwise we're at the end of the line so null-term the message buffer
128   Repeat above for the line
129   BLANK
130   Move the cursor back to the saved x and y positions
131   Bold the message
132   Format and print the message to the status line
133   Remove the bold format
134   BLANK
135   Loop until user presses space
136   If the last message covered an older message and there's more to print
137   Move the cursor to the stored x,y position
138   Add the message to the muffer
139   There is no more to the message...
140   End check for covered messages with more to print
141   Otherwise, there isn't more, but something is covered
142   BLOCK START - Printing excess messages
143   Move the cursor to x/y
144   Bold the output
145   Add the new message
146   Clear the bold
147   There is still more there
148   BLOCK END - printing excess mesages
149   End of looping while player isn't pressing space
150   Move the cursor to x,y position
151   Print the message
152   BLOCK END - more
153   BLANK
154   BLANK
155   COMMENT
156   COMMENT
157   COMMENT
158   COMMENT

CONCATENATE MESSAGE

159   Declare doadd with six arguments
160   Argument 1 is the string to print
161   Arguments 2-6 are the attributes for the string
162   BLOCK START - doadd, adds a string to the end of the message buffer
163   Format print the string to the current end of the message buffer
164   Update position in the message buffer
165   BLOCK END - doadd
166   BLANK
167   COMMENT
168   COMMENT
169   COMMENT
170   COMMENT
171   COMMENT

STEP THROUGH MESSAGE

172   Declare putmsg with two arguments
173   Argument 1 is the line number
174   Argument 2 is the message string 
175   BLOCK START - putmsg, adds a multiline message to the top line as needed
176   Declare a pointer to the current, last, and in progress message
177   Declare a counter for the message length
178   BLANK
179   The current message is the input message
180   Loop while there is still space on the line
181   Output the message to the line
182   Update the line position
183   If we've gone past the edge of the screen...
184   Create a 'more' delay
185   Cache the last message state
186   Loop until current message is parsed
187   Temporary message is the current message without blanks
188   COMMENT
189   COMMENT
190   COMMENT
191   If there are no blanks or the whole message is printed...
192   Point the current message to the end of the column
193   Break out of pointer updates
194   End check for blanks
195   Otherwise, check if the final part of the message fits
196   Nothing else to do
197   If none of the above fits, move current message in to tmp and retry
198   BLOCK END - update message to the end
199   End check for the end of the line
200   Repeat loop while there's space on the line
201   BLOCK END - putmsg
202   BLANK
203   COMMENT
204   COMMENT
205   COMMENT

SCROLL MESSAGE

206   Define scrl with three arguments
207   Argument 1 is the message line number
208   Argument 2 and 3 are the previous and next message
209   BLOCK START - scrl, push a message across a line
210   Declare a pointer to a formatted string
211   Declare unused integers
212   BLANK
213   If the number of columns is over 40...
214   Set the stirng format to 80
215   Otherwise...
216   Set the string format to 40
217   BLANK
218   If there is no previous string...
219   Move the cursor to the left side of the target line
220   If the new string fits across one screen...
221   Clear the line
222   Print the formatted string
223   Otherwise there is a previous string so...
224   While the previous string hasn't caught up to the current
225   Move to the start of the target line
226   Print the formatted string and increment to the next byte
227   If the remaining length of the last string fits on the screen
228   Clear the line
229   End check for previous string
230   BLOCK END - scrl
231   BLANK
232   COMMENT
233   COMMENT
234   COMMENT
235   COMMENT

CONVERT CHARACTER TO PRINTABLE

236   unctrl returns a pointer to character
237   Define unctrl with one argument  
238   Argument 1 is the character to escape
239   BLOCK START - unctrl, converts input character to printable
240   Defines a character array of length 9
241   BLANK
242   If the input is a space...
243   Copy the space to the string
244   Otherwise, if it's not a printable character (value > 128)..
245   Then if it's below printable...
246   Add '@' to the input character (@ is the base for 'A'. this almost 
      guarantees an alpha-numeric result)
247   Otherwise the value is high...
248   So print the hex value
249   Otherwise, the character is printable so we handle the base case...
250   The string is simply the character
251   Null-term'd
252   End check for printable characters
253   BLANK
254   Return the string
255   BLOCK END - 
256   BLANK
257   COMMENT
258   COMMENT
259   COMMENT
260   COMMENT

DISPLAY THE STATUS BAR

261   Define status with no arguments
262   BLOCK START - status, displays the player status line
263   Declare cursor position x and y
264   Declare a flag for hungry state
265   Declare integers for game lvl, purse, hp, and ac
266   Declare a strength type variable (int) for strength
267   Declare a variable for player level
268   Declare a character array for names
269   BLOCK START - player state names
270   The player state strings
271   BLOCK END - player state names
272   BLANK
273   Read and update keyboard state
274   BLANK
275   Get the cursor coordinates in to ox and oy, stdscr is ignored
276   If we are in color mode..
277   Set the current color to yellow
278   BLANK
279   COMMENT
280   COMMENT
281   COMMENT
282   If the game level has changed recently...
283   BLOCK START - show level
284   Update status level to actual level
285   Move to the left side of the bottom row  
286   Print the game level information
287   BLOCK END - show level
288   BLANK
289   COMMENT
290   COMMENT
291   COMMENT
292   If the player's health has changed recently...
293   BLOCK START - show player health
294   Update the status health to actual health
295   Move the cursor to 12 spaces in to the bottom row
296   Print the new health info
297   COMMENT
298   If the player has less than 100 health...
299   Add a space to the end to overwrite any artifacts
300   BLOCK END - show player health
301   BLANK
302   COMMENT
303   COMMENT
304   COMMENT
305   If the player's strength has changed since last status update...
306   BLOCK START - show player strength
307   Update the status strength to actual strength
308   Move the cursor to space 26 on the bottom row
309   Print new strength status
310   BLOCK END - show player strength
311   BLANK
312   COMMENT
313   COMMENT
314   COMMENT
315   If the player's gold has changed recently...
316   BLOCK START - show player's gold
317   Update the status gold to the actual gold
318   Move the cursor to the bottom row and left or right depending on size..
319   Print the gold amount
320   BLOCK END - show player's gold
321   BLANK
322   COMMENT
323   COMMENT
324   COMMENT
325   If the player's armor class has changed...
326   BLOCK START - update armor status
327   Update armor class to the current amount
328   If the player's left ring modifies ac...
329   Update status ac
330   If the player's right ring modifies ac...
331   Update status ac
332   Move the cursor to the bottom line with space depending on screen size
333   Print the new armor status...
334   using the newly calcualted armor attribute
335   BLOCK END - update armor status
336   BLANK
337   COMMENT
338   COMMENT
339   COMMENT
340   If the player's experience level has changed
341   BLOCK START - show experience level
342   Update the experience level
343   Move the cursor to the last line. Space depends on screen size..22 or 62
344   Print the new experience level
345   BLOCK END - show experience level
346   BLANK
347   COMMENT
348   COMMENT
349   COMMENT
350   If the hunger state has changed...
351   BLOCK START - update hunger state
352   Update hunger state to actual
353   Move cursor to the bottom line 3/4th of the way across
354   Print the hunger state
355   Move the cursor back to the start of the state
356   If the player was hungry...
357   BLOCK START - hungry text attributes
358   Set the text to strong
359   Print the name again
360   End the bold text
361   BLOCK END - hungry text attributes
362   BLOCK END - update hunger state
363   BLANK
364   If we're in color mode
365   End the bold color
366   BLANK
367   Move the cursor back to the original position
368   BLANK
369   BLOCK END - status
370   BLANK
371   COMMENT
372   COMMENT
373   COMMENT
374   COMMENT

PAUSE FOR KEY

375   Declare wait_for with one argument
376   Argument 1 is an input character
377   BLOCK START - wait_for, stops game until player hits the input character
378   Declare a character
379   BLANK
380   If we're only waiting for a new line...
381   Loop until it's either carriage return or newline
382   Continue the loop while there's no match
383   Otherwise..
384   Only loop if the character's don't match
385   Continue the loop on input of other characters
386   BLOCK END - wait_for
387   BLANK
388   COMMENT
389   COMMENT
390   COMMENT
391   COMMENT

SHOW WINDOW

392   Declare show_win with two arguments
393   Argument 1 is the pointer to the window (unused)
394   Argument 2 is the message to display
395   BLOCK START - show_Win
396   Print the message to the top left corner
397   Move the cursor to the player
398   Wait for a space input from the user
399   BLOCK END - 
400   BLANK
401   BLANK
402   COMMENT
403   COMMENT
404   COMMENT
405   COMMENT
406   COMMENT

READ INPUT FROM KEYBOARD
Big wrapper function for as many cases as possible

407   Declare getinfo with two arguments
408   Argument 1 is the buffer to read in to
409   Argument 2 is the max size of the expected input
410   BLOCK START - getinfo, it is the general purpose keyboard reader/parser
411   Declare a pointer to the return string, and a character
412   Initialize a read counter to zero
413   Declare a flag for previous cursor state and a return value
414   Declare a general character buffer
415   BLANK
416   Read in 80 characters from the screen data segment in to the buffer
417   Set the return string to the input string
418   Nullterm the input string
419   Turn on the cursor blinking
420   Loop while there's still work to do parsing input...
421   BLOCK START - Read and switch on the input character
422   CASE escape key
423   While the current string ptr and return don't match (work was done...)
424   Backspace along the line
425   Decrement the read counter
426   Back the string pointer up
427   End check for work
428   Set the return value and string to escape
429   Reset cursor to it's original state
430   Break from escape key
431   CASE backspace key
432   If the current string ptr and return don't match (work was done...)
433   Backspace along the line
434   Decerement the read counter
435   Back up the string pointer
436   End check for work
437   Break from the backspace key
438   CASE all other normal keyboard input
439   If the input size has exceeded the buffer..
440   Send a beep
441   Break from processing input
442   End check for input size
443   Increment the read counter
444   Add the character to the screen
445   Add the new character to stored string
446   If the input was a readable character (bit 7 is 0)...
447   End processing input
448   CASE newline (fallthrough)
449   CASE carriage return
450   Nullterm the input string
451   Reset cursor to it's original state
452   Return value is either \n or \r
453   Break from the end of the chararcters
454   BLOCK END - Read and switch on input character
455   Output the buffer to the screen
456   Return the last input
457   BLOCK END - getinfo
458   BLANK

HANDLE BACKSPACE

459   Declare backspace with no arguments
460   BLOCK START - backspace, executes a backspace on the screen cursor
461   Declares position integers for x and y (y is horizontal this time)
462   Get the current cursor position in to x and y
463   Decrement the horizontal position and if it's less than 0...
464   Floor it at zero
465   Move the cursor to the new position (should be left one space)
466   Clear the character that we just backspaced
467   BLOCK END - backspace
468   BLANK
469   COMMENT
470   COMMENT
471   COMMENT
472   COMMENT
473   COMMENT
474   COMMENT
475   COMMENT
476   COMMENT
477   COMMENT
478   COMMENT
479   COMMENT
480   COMMENT
481   COMMENT
482   COMMENT
483   COMMENT
484   COMMENT
485   COMMENT
486   COMMENT
487   COMMENT

FORMATS A STRING

488   Define str_attr with one argument
489   Argument 1 is the input string
490   BLOCK START - str_attr, prints a formatted string
491   Check if the LUXURY flag is set (It's not, so following is dead code)
492   Declare flags for attribute state and changed
493   BLANK
494   Loop while there is still a valid string value...  
495   BLOCK START - string loop
496   If any changes were made to a single character...
497   BLOCK START - single character changes
498   End printing attributes
499   Deassert flag for attribute changes
500   Deassert flag for changes
501   BLOCK END - single character changes
502   Check if this is an attribute
503   BLOCK START - attribute processing
504   Skip the '%'
505   Switch on the type of attribute
506   BLOCK START - attribute type
507   CASE u (underline character)
508   Set flag that single change has been made
509   CASE U (underlined string)
510   Underline character
511   Set flag for character stream attribute enabled
512   Advance string position
513   End check for attribute
514   CASE i (invert character)
515   Set flag that single change has been made
516   CASE I (invert line)
517   Accentuate the character (bold/invert)
518   The character steam is now enabled for an attribute
519   Increment through the string
520   End check for invert attribute
521   CASE $ (end of attribute formatting)
522   If attribute is being processed...
523   Set flag that something changes...
524   Advance the string position
525   Continue to next attribute
526   BLOCK START - attribute type
527   BLOCK START - attribute processing
528   If the current charcater is a newline or return...
529   BLOCK START - newline handling
530   Move across the string
531   Print a newline
532   BLOCK END - newline handling
533   Otherwise if the character isn't the end of the string
534   Print the current character and advance to the next one
535   BLOCK END - string loop
536   If this is an attribute...
537   End the current formatting state
538   Otherwise, LUXURY isn't enabled (normal)
539   While there is a valid character...
540   BLOCK START - process actual characters
541   If the character is an attribute marker...
542   Advance along the string
543   Accentuate the character
544   End check for attribute marker
545   Just a normal character...print it and advnace the pointer
546   End accentuated characters
547   BLOCK END - process actual characters
548   BLANK
549   End check for LUXURY
550   BLOCK END - str_attr
551   BLANK
552   COMMENT
553   COMMENT
554   COMMENT

LOW-LEVEL KEYBOARD INTERFACE

555   Define SIG2 with no arguments
556   BLOCK START - SIG2, keyboard hardware handler
557   Declares a counter (unused) and a tick counter
558   Declares flag if this is a first time initializationm
559   Declares flags for numlock and capslock
560   Declares location variables for numlock, capslock, and the time
561   Declares state variables for num/caps and fast mode
562   Declares variables for the hours and minutes
563   Initialize time display to false. Declare variable for time remainder
564   Declare position integers
565   Declare an unused buffer
566   If demo mode is on (it's not)
567   Declare a variable for game time
568   End check for demo mode
569   BLANK
570   If enough time hasn't passed...
571   Nothing to do
572   Otherwise...kick the next trigger time to 6 ticks ahead
573   If there the screen has been saved or dumped (fake dos)...
574   Return - nothing to do
575   Set AH to 0x02 (prep for keyboard misc key check..shift/num/caps)
576   Invoke BIOS interrupt for keyboard keys
577   Save result in to numlock result (not correct for numlock yet)
578   Mask for the capslock bit (bit 6)
579   Mask for the fast mode bit (scroll lock, bit 4)
580   Mask for the numlock bit (bit 5)
581   COMMENT
582   COMMENT
583   COMMENT
584   If this is the first check...set up clock
585   Set AH to 0x2c 
586   Invoke DOS interrupt 0x21 to get time in to CX and DX (CH/CL = hour/min)
587   Save the hour
588   Save the minute  
589   Increment the showtime counter
590   End initial set up pass
591   If many ticks have passed...
592   COMMENT
593   COMMENT
594   COMMENT
595   COMMENT
596   Wrap the minute hand
597   If the mine hand is 0...
598   Wrap the hour hand...
599   Reset the tick counter
600   Increment the next tick trigger
601   Update the show time counter
602   End check for large tick count
603   BLANK
604   COMMENT
605   COMMENT
606   COMMENT
607   COMMENT
608   If this is a first run or a re-run...
609   BLOCK START - keyboard init
610   This is initialization, so remove the (re)init flags now
611   If the screen is small...
612   BLOCK START - small screen status init
613   Numlock status is at 10 spaces
614   Capslock status is at 19 spaces
615   Time is at 35 spaces
616   BLOCK END - small screen status init
617   Otherwise this is a big bigger screen...
618   BLOCK START - big screen init
619   Numlock is at 20 spaces
620   Capslock is at 39 spaces
621   Time is at 75 spaces
622   BLOCK END - big screen init
623   COMMENT
624   COMMENT
625   COMMENT
626   Toggle numlock
627   Toggle capslock
628   Increment clock set timer
629   Toggle faststate
630   BLOCK END - keyboard init
631   BLANK
632   Get current cursor coordinates
633   BLANK
634   If fast state doesn't match mode (then trigger fast mode)...
635   BLOCK START - Init fast mode
636   BLANK
637   Match fast mode with flag
638   Clear counter
639   Show the counter
640   Cancel run mode
641   Move the cursor to the bottom left
642   If fast mode is now on...
643   BLOCK START - Fast mode on status
644   Accent the display colors (invert/bold)
645   Print fast mode notification
646   End bold display
647   BLOCK END - Fast mode on status
648   Otherwise, fast mode is now off
649   BLOCK START - Fast mode off status
650   Print all blank to cover up previous status
651   BLOCK END - Fast mode off status
652   BLOCK END - Init fast mode
653   BLANK
654   If numlock status no longer matches previous status...
655   BLOCK START - Init numlock
656   Match numlock states
657   Reset counter
658   Display the counter
659   Stop running mode
660   Move the cursor to the last line to the proper numlock position
661   If numlock is active...
662   BLOCK START - Numlock on status
663   Sent text to strong style
664   Print numlock indicator
665   End strong text
666   BLOCK END - Numlock on status
667   Otherwise numlock is off...
668   Print spaces over the previous numlock text
669   BLOCK END - Init numlock
670   If capslock status no longer matches previous status...
671   BLOCK START - Init capslock
672   Match capslock status
673   Move to the last line and the proper capslock position
674   If capslock is now on..
675   BLOCK START - Capslock status change on
676   Highlight/invert the text color
677   Add the caps lock message
678   End the highlight/invert of text color
679   BLOCK END - Capslock status change on
680   Otherwise capslock is off
681   Print blank spaces over previous status
682   BLOCK END - Init capslock
683   If time should be updated...
684   BLOCK START - update time
685   Reset update flag
686   Check for DEMO mode (not demo mode)
687   COMMENT
688   COMMENT
689   COMMENT
690   COMMENT
691   Check if the demo time has expired
692   End game due to time expiring
693   End check for demo mode
694   COMMENT
695   Get the minute remainder
696   Move to the timer position on the right side of the last line
697   Invert the color
698   Print the current time
699   End color change
700   BLOCK END - update time
701   Move cursor to original position
702   BLOCK END - SIG2
703   BLANK
704   COMMENT
705   COMMENT
706   COMMENT
707   COMMENT
708   BLANK
709   Declare individual print formatting functions
710   Declare format string pointers and a flag
711   Declare dimension limits
712   BLANK
713   Declare a function pointer array to include the formatting helpers
714   There are 5 helpers defined later
715   End of print function helpers
716   BLANK

STRING COPY WRAPPER

717   Declare my_stccpy with three arguments (strings)
718   BLOCK START - my_stccpy, alternative to stccpy. copies strings
719   Call the basic function to put b in to a with a size limit of c
720   Returns a pointer to the end of string a (useful for concatenation)
721   BLOCK END - my_stccpy
722   BLANK

SPRINTF AS IT WAS IN 1984

723   sprintf returns a pointer to a character string
724   Define sprintf with three arguments
725   Argument 1 is the target buffer, argument two is the formatted string
726   Argument 3+ are the variables to associate with formats
727   BLOCK START - sprintf, resolves a formatted string in a target buffer
728   Declare two pointers, one for current character and one to the buffer
729   Declare a pointer for the argument location, and a pad
730   Declare a temporary buffer
731   BLANK
732   Set init buffer pointer to the input target buffer (init is the return)
733   START BLOCK - While the formatted string has data...
734   If the next character isn't an attribute token...  
735   Simply copy the character from input to output buffer and increment both
736   Otherwise, we need to handle the token...
737   Reset the output format size calculations
738   If the 2nd character is a dash, then we left pad the output....
739   Increment both the left pad amount and the pointer to the next character
740   Count the digits in the remaining fomat and thats the minimum width
741   Bp is the end of the nondigit token, check if that end is a width op
742   If so....check the length of the number after that operator
743   We have the width data, move the scanning pointer to the end
744   Move bp to the unused temp buffer to work on output format
745   If the format type is valid (has a real index to format function array)
746   Then process the argument string and increment the pointer to next arg
747   Nullterm the output buffer just in case this is the last operation
748   If we've set a max format width, but the buffer exceeded that width...
749   Nullterm the buffer at the max width to truncate
750   Calculate necessary pad value...positive if min width > actual width
751   Repoint the buffer pointer to the base of the output buffer
752   If we don't have to left-justify...
753   Then add pad to the front
754   Move the buffer pointer to the other end of the buffer
755   If we do have to left justify...
756   Add the pad at the end
757   Match pointers
758   We're done with a single argument but if there is more work to do...
759   Move the input string's pointer forward
760   End of formatted argument handling...repeats as needed
761   BLOCK END - While the formatted string has data...
762   Nullterm the current buffer just in case
763   Return the newly formatted string
764   BLOCK END - sprintf
765   BLANK

SCAN NUMBER STRING

766   Define scan_num with one argument
767   Argument 1 is the pointer to the base scanning position
768   BLOCK START - scan_num, returns value of the current number in string
769   Set the initial number to zero
770   BLANK
771   Match the pointers for now
772   While there are digits to process
773   Multiply the current value by decimal base...offset by next char from 0
774   Return the final value
775   BLOCK END - scan_num, returns value of the current number in string
776   BLANK

FORMATS STRINGS

777   Declare pf_str with one argument
778   Argument 1 is a pointer to an array of characters
779   BLOCK START - pf_str, converts all input to string
780   Point to the newly formatted string
781   Return 1 (parent increments the argument pointer by this value)
782   BLOCK END - pf_str
783   BLANK

FORMATS BLANKS

784   Defines blanks with one argument, the number of blanks to add
785   BLOCK START - blanks, pads buffer with blanks
786   While there are blanks to process...
787   Add spaces and increment buffer pointer
788   Nullterm the end of the pad
789   BLOCK END - blanks
790   BLANK

FORMATS CHARACTERS

791   Defines pf_chr with one argument
792   Argument one is the desired argument to format
793   BLOCK START - pf_chr, format the input as a character
794   Simply copy the argument to the output...char is easy
795   Return 1 to increment argument pointer
796   BLOCK END - pf_chr
797   BLANK
798   Declare a small buffer for unsigned int processing
799   BLANK

FORMATS INT

800   Declares pf_int with one argument
801   Argument 1 points to the argument to format
802   BLOCK START - pf_int, format the input as a signed integer
803   If the input argument is negative...
804   Put a negative sign at the beginning of the output
805   Invert input argument (make it positive)
806   End check for negative input
807   Pass the modified argument to the unsigned int handler and return result
808   BLOCK END - pf_int
809   BLANK

FORMATS UNSIGNED INT
Loops from largest to smallest digit base, printing quotent values

810   Defines pf_uint with one argument
811   Argument 1 is a pointer to the argument to format
812   BLOCK START - pf_uint, format the input as an unsigned integer
813   Point to the small buffer and declare a flag to end loop
814   Set return base value to the pointer, set divisor, delcare result
815   BLANK
816   If there is no input value...
817   Set the output buffer position to character 0
818   Nullterm the next buffer position
819   Otherwise there is a number to process...
820   Reset the process flag
821   While there is a divisor....
822   Divide the number by the current divisor size (floor of position value)
823   Set the current position to the result offset by '0' to represet a char
824   Flag that we've done at least one loop
825   Remove the calcualted value from the base value (leaving smaller places)
826   End of check one process loop
827   Shave one zero off the divisor
828   Repeat loop to the end
829   Nullterm the final result
830   End check for number to process
831   Copy the final result as a string to the buffer
832   Return 1 to increment the argument pointer
833   BLOCK END - pf_uint
834   BLANK

SYMBOL CHECK

835   Define pf_per with one argument
836   BLOCK START - pf_per, formats the input as an attribute token
837   Outputs an argument token to the input string
838   Don't advance the buffer on return
839   BLOCK END - pf_per
840   BLANK

TERSE FILTER

841   Defines noterse with one argument
842   Argument 1 is an input string
843   BLOCK START - noterse, returns the appropriate (non)terse string
844   Returns the string or null depending on current terse mode
845   BLOCK END - noterse
846   BLANK
847   EOF