home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.wwiv.com
/
ftp.wwiv.com.zip
/
ftp.wwiv.com
/
pub
/
HATCH
/
WWIVNEWS.ZIP
/
9304_3.NWS
< prev
next >
Wrap
Text File
|
1993-04-10
|
13KB
|
262 lines
───────────────┬─────────────────────────────────────────────┬───────────────
│ "Box Mods" - Threat or Menace? │
│ By Starship Trooper (1@2750/12754) │
└─────────────────────────────────────────────┘
Many people ask for or write mods to colorize and draw boxes around
the "Last few callers" listing, sub lists, and similar items. This is
actually a very simple procedure, which can be done by anyone without
working from a published mod file. In this article, I will demonstrate
how this should be done.
As an example, consider the function to list the chains available.
Unmodified, it appears in BBSUTL1.C as:
void show_chains(int *mapp, int *map) // 1
{ // 2
int abort,i,i1; // 3
char s[81]; // 4
// 5
abort=0; // 6
nl(); // 7
for (i=0; (i<*mapp) && (!abort) && (!hangup); i++) { // 8
sprintf(s,"%d. %s",i+1, chains[map[i]].description); // 9
pla(s,&abort); // 10
} // 11
nl(); // 12
} // 13
Obviously, lines 8 through 11 do the actual printing. Line 9 writes the
number and name of the chain into string s (such as "1. Tradewars") and
line 9 sends it to the screen and modem, followed by a new line. sprintf()
format strings (like "%d. %s") work exactly like those used by printf() and
npr(). The '%' instructs the function to grab the next argument, and the 'd'
or 's' tells how to interpret it. These format specifies are very
powerful; you can specify how wide a certain item should be as well as
several other options. This is essential for arranging the data in columns.
How wide should they be? There's no simple answer. You have to take into
account the actual length of an item, how much space to leave between them,
and the width of a screen (80 columns).
Something like this is needed:
"║ xx │ description │" (xx is the number of the chain).
1234567???????????+2 Length= 7 + length(description) + 2
How long should the description be? VARDEC.H says 81 characters, but that
won't fit. Let's trim it to sixty. Sixty characters for the description
plus nine (7+2) is well under our limit of 80. These numbers are placed
within a sprintf() format specifier. First comes the '%', then the amount
of space used, then the item type. If you want the item to be against the
left side of the field, rather than the right, place a '-' before the width.
This should be done in almost all cases, except with numbers.
sprintf(s,"%2d %-60s",i+1, chains[map[i]].description); // 9
^ ^^^
In the above line, the number will be printed within a two-space field,
against the right edge, while the description will be printed within a
60-space field, against the left edge. There is an error, though: if
a description is greater than sixty characters, it will overflow the
space. This can be corrected by adding a "precision" specifier. These
tell sprintf() to only use a certain number of characters, and ignore
those that would normally cause "overflow". This is done by placing a
decimal point after the field width, and then the MAXIMUM number of
characters allowed:
sprintf(s,"%2d %-60.60s",i+1, chains[map[i]].description); // 9
^^^
Everything is now in orderly columns. We can place them within a box
by adding vertical line characters. The single bar (│) can be produced by
holding down ALT and typing 179 on the numeric keypad, and the double
bar (║) with ALT-186. Place these characters within the sprintf()
format string, between the data items:
sprintf(s,"║ %2d │ %-60.60s ║",i+1, etc.); // 9
^ ^ ^
Next, add color codes. These are produced by embedding a control-C in the
string, followed by a digit. If you're using the Borland editor or Qedit
or the DOS 5.0 editor, this is done by typing a control-P and then a
control-C. A heart symbol will appear. All the line characters
should be the same color, as they will be joined together eventually.
sprintf(s,"1║2 %2d 1│5 %-60.60s 1║",i+1, etc.); // 9
(If you read this from within WWIV you will see colors, otherwise you will
see hearts and digits.)
Your box will need a top and bottom also. Use your block-copy command to
make a copy of the sprintf line you've been working on. Cut out everything
but the string itself, and wrap a pl() (or pla()) around it:
pl("1║2 %2d 1│5 %-60.60s 1║");
Next, remove the colors:
pl("║ %2d │ %-60.60s ║");
Replace vertical bars with corner pieces or T-shaped bars:
pl("╔ %2d ╤ %-60.60s ╗");
Next replace each of the format specifies with the proper number of
horizontal lines. (in this example, two and sixty)
pl("╔ ══ ╤ ════════════════════════════════════════════════════════════ ╗");
(block-copy commands work well here for making long horizontal lines)
Finally, replace the blank spaces with horizontal lines. Then clone this
line, and make the second copy look like the bottom of a box (replace the
corners), and put a color code at the beginning:
pl("1╔════╤══════════════════════════════════════════════════════════════╗");
pl("1╚════╧══════════════════════════════════════════════════════════════╝");
Insert these lines into the function just before and after the loop. You
can, if you want, put a title on the upper bar:
pl("1╔════╤═══Online Programs Available══════════════════════════════════╗");
Your finished function might look like:
void show_chains(int *mapp, int *map) // 1
{ // 2
int abort,i,i1; // 3
char s[81]; // 4
// 5
abort=0; // 6
nl(); // 7
pl("1╔════╤═══Online Programs Available══════════════════════════════════╗");
for (i=0; (i<*mapp) && (!abort) && (!hangup); i++) { // 8
sprintf(s,"1║2 %2d 1│5 %-60.60s 1║", // 9
i+1,chains[map[i]].description); /* Continued */
pla(s,&abort); // 10
} // 11
pl("1╚════╧══════════════════════════════════════════════════════════════╝");
nl(); // 12
} // 13
Not all listing commands are easy to change. Some use multiple strcpy(),
strcat(), ansic(), outstr(), and similar non-sprintf() functions. (The function
to print file information, for example). It is necessary to convert these to
use sprintf(), which I will demonstrate here. I encourage all mod writers to
use this information... not only is it useful for "box mods", but many other
mods use the older, cumbersome methods to output several pieces of data, when
a single sprintf() is easier.
The first thing to do is locate a string variable that is not used in that part
of the code. s, s1, s2, etc., often hold things only temporarily, and can be
safely reused. If you're unsure about it, simply declare a new string of
length 81 or more.
The functions to be replaced perform these actions:
Original function sprintf code result
──────────────────────────────────────────────────────────────────────────────
strcpy(dest, src) %s or constant copy _src_ string into _dest_.
strcat(dest, src) %s or constant append _src_ string to end of _dest_
itoa(num, dest, rad) %d converts _num_ to string _dest_
ansic(num) ctrl-C digit send ANSI codes to change color
nl() \n send CR and LF
When these functions are used, there will often be several of them in
succession. The first will be a strcpy(), to begin the string to be sent out,
followed by strcat()'s and possibly itoa()'s, which add successive items to
the output string, which will eventually be sent with outstr() or pl().
Sometimes, small strings are composed and sent with outstr(), with ansic()
between them (see printinfo() in xfer.c).
The format for sprintf() is:
sprintf(buffer, "format string with %d %s etc.", var, var,...);
The first argument tells the program where to place the result. The second
tells what it should look like, with % meaning "fetch something and put it
here", and the other arguments being the items that are "fetched". Standard
WWIV source makes use of sprintf() in most instances where a box mod would be
desired. The only exception is printinfo(), which prints the information for a
file. Although it's not too difficult to convert it, the function is very long,
too long for this article. Let's try something simpler, a (hypothetical) mod
that does something fairly interesting, but in a clumsy way:
┌────────────────────────────────────────────────────┐
│ WK-001.MOD by Joe Sysop #1@xxxx. This prints a │
│ Way-K00L message to your callers: │
└────────────────────────────────────────────────────┘
<magenta>Welcome to Way-K00L BBS, <fl. red> John Smith!
<green>You are user #95, and you've called <cyan>17<green> times!
OK, put this somewhere in LILO.C:
ansic(3); /* magenta */
strcpy(s, "Welcome to ");
strcat(s, syscfg.systemname);
strcat(s, ", ");
outstr(s); /* Welcome to Way-K00L BBS, */
ansic(6); /* fl. red */
outstr(thisuser.name); /* John Smith */
outchr('!'); nl(); /* ! \n*/
ansic(5); /* green */
strcpy(s, "You are user #");
itoa(usernum, s1, 10); /* convert num to string (base 10), store in s1 */
strcat(s,s1);
strcat(s,", and you've called ");
outstr(s); /* You are user #95, and you've called */
ansic(1); /* cyan */
itoa(thisuser.timeson, s1, 10);
outstr(s1);
ansic(5); /* green */
pl("times!"); /* End WK-001.MOD */
Obviously, there has to be a better way... let's take each item to be printed,
and put it in a sprintf(). I'll reproduce the desired result here:
<magenta>Welcome to Way-K00L BBS, <fl. red> John Smith!
<green>You are user #95, and you've called <cyan>17<green> times!
The first item is a change to color #3. This is done with ^C3.
sprintf(s,"3 /* equivalent to ansic(3) */
Next comes a constant string, "Welcome to ":
sprintf(s,"3Welcome to /* we used strcpy in the badly-done example */
Then the system name, which is a string. Use "%s" to tell sprintf() to
fetch it, and pass the variable as a parameter:
sprintf(s,"3Welcome to %s", syscfg.systemname); /* instead of strcat() */
Comma, space, then color #6:
sprintf(s,"3Welcome to %s, 6", syscfg.systemname);
And the user's name, then a '!', and print it:
sprintf(s,"3Welcome to %s, 6%s!", syscfg.systemname, thisuser.name);
pl(s); /* print the above, then a linefeed */
The first line has been sent, so we'll do the next line now:
sprintf(s,"5You are user #
A number can be printed by placing "%d" in the string and the variable in
the argument list. sprintf() will do an itoa() conversion:
sprintf(s,"5You are user #%d, ",usernum);
Another string constant follows, a color change, a number, a color change,
and finally the last word:
sprintf(s,"5You are user #%d, and you've called 1%d5 times!",usernum,
thisuser.timeson); /* ^C1%d^C5 ^^^ */
pl(s);
As you can see, the 19 lines of code in the original have been replaced by two
sprintf() and two pl() calls. You could even use npr() to integrate the
sprintf() and pl() sets into one line each (if you choose to do this, remember
to add a \r\n to the end). sprintf() produces more compact code and allows easy
"Box Mods" using the procedure I detailed previously.
──────────────────────────────────────────────────────────────────────────────
Now you know how to make box mods - and so does almost everyone else. So please
do not flood the mod subs with mods created using the guidelines here! If
someone asks how to box a listing, send them this file.