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˙