1 /*
2  *  File:       chardump.cc
3  *  Summary:    Dumps character info out to the morgue file.
4  *  Written by: Linley Henzell
5  *
6  *  Change History (most recent first):
7  *
8  *
9  * <4> 19 June 2000    GDL Changed handles to FILE *
10  * <3> 6/13/99         BWR Improved spell listing
11  * <2> 5/30/99         JDJ dump_spells dumps failure rates (from Brent).
12  * <1> 4/20/99         JDJ Reformatted, uses string objects, split out 7
13  *                         functions from dump_char, dumps artifact info.
14  */
15 
16 #include "AppHdr.h"
17 #include "chardump.h"
18 
19 #include <string>
20 #include <stdio.h>
21 #include <string.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #if !(defined(__IBMCPP__) || defined(__BCPLUSPLUS__))
25 #include <unistd.h>
26 #endif
27 #include <ctype.h>
28 
29 #ifdef USE_EMX
30 #include <sys/types.h>
31 #endif
32 
33 #ifdef OS9
34 #include <stat.h>
35 #else
36 #include <sys/stat.h>
37 #endif
38 
39 #ifdef DOS
40 #include <conio.h>
41 #endif
42 
43 #include "externs.h"
44 
45 #include "debug.h"
46 #include "describe.h"
47 #include "itemname.h"
48 #include "items.h"
49 #include "macro.h"
50 #include "mutation.h"
51 #include "player.h"
52 #include "religion.h"
53 #include "shopping.h"
54 #include "skills2.h"
55 #include "spl-book.h"
56 #include "spl-cast.h"
57 #include "spl-util.h"
58 #include "stuff.h"
59 #include "version.h"
60 
61 
62  // ========================================================================
63  //      Internal Functions
64  // ========================================================================
65 
66  // fillstring() is a hack to get around a missing constructor in
67  // Borland C++ implementation of the STD basic_string.   Argh!!!
fillstring(size_t strlen,char filler)68 static std::string fillstring(size_t strlen, char filler)
69 {
70     std::string s;
71 
72     for (size_t i=0; i<strlen; i++)
73         s += filler;
74 
75     return s;
76 }
77 
78  //---------------------------------------------------------------
79  //
80  // munge_description
81  //
82  // Convert dollar signs to EOL and word wrap to 80 characters.
83  // (for some obscure reason get_item_description uses dollar
84  // signs instead of EOL).
85  //  - It uses $ signs because they're easier to manipulate than the EOL
86  //  macro, which is of uncertain length (well, that and I didn't know how
87  //  to do it any better at the time) (LH)
88  //---------------------------------------------------------------
munge_description(const std::string & inStr)89 static std::string munge_description(const std::string & inStr)
90 {
91     std::string outStr;
92 
93     outStr.reserve(inStr.length() + 32);
94 
95     const long kIndent = 3;
96     long lineLen = kIndent;
97 
98     long i = 0;
99 
100     outStr += fillstring(kIndent, ' ');
101 
102     while (i < (long) inStr.length())
103     {
104         char ch = inStr[i];
105 
106         if (ch == '$')
107         {
108             outStr += EOL;
109 
110             outStr += fillstring(kIndent, ' ');
111             lineLen = kIndent;
112 
113             while (inStr[++i] == '$')
114                 ;
115         }
116         else if (isspace(ch))
117         {
118             if (lineLen >= 79)
119             {
120                 outStr += EOL;
121                 outStr += fillstring(kIndent, ' ');
122                 lineLen = kIndent;
123 
124             }
125             else if (lineLen > 0)
126             {
127                 outStr += ch;
128                 ++lineLen;
129             }
130             ++i;
131         }
132         else
133         {
134             std::string word;
135 
136             while (i < (long) inStr.length()
137                    && lineLen + (long) word.length() < 79
138                    && !isspace(inStr[i]) && inStr[i] != '$')
139             {
140                 word += inStr[i++];
141             }
142 
143             if (lineLen + word.length() >= 79)
144             {
145                 outStr += EOL;
146                 outStr += fillstring(kIndent, ' ');
147                 lineLen = kIndent;
148             }
149 
150             outStr += word;
151             lineLen += word.length();
152         }
153     }
154 
155     outStr += EOL;
156 
157     return (outStr);
158 }                               // end munge_description()
159 
160  //---------------------------------------------------------------
161  //
162  // dump_stats
163  //
164  //---------------------------------------------------------------
dump_stats(std::string & text)165 static void dump_stats( std::string & text )
166 {
167     char st_prn[20];
168 
169     text += you.your_name;
170     text += " the ";
171 
172     text += player_title();
173     text += " (";
174     text += species_name(you.species, you.experience_level);
175     text += ")";
176     text += EOL;
177 
178     text += "(Level ";
179     itoa(you.experience_level, st_prn, 10);
180     text += st_prn;
181     text += " ";
182     text += you.class_name;
183     text += ")";
184     text += EOL EOL;
185 
186     if (you.real_time != -1)
187     {
188         const time_t curr = you.real_time + (time(NULL) - you.start_time);
189         char buff[200];
190 
191         make_time_string( curr, buff, sizeof(buff) );
192 
193         text += "Play time: ";
194         text += buff;
195 
196         text += "       Number of turns: ";
197         itoa( you.num_turns, st_prn, 10 );
198         text += st_prn;
199         text += EOL EOL;
200     }
201 
202     text += "Experience : ";
203     itoa(you.experience_level, st_prn, 10);
204     text += st_prn;
205     text += "/";
206     itoa(you.experience, st_prn, 10);
207     text += st_prn;
208     text += EOL;
209 
210     text += "Strength ";
211     itoa(you.strength, st_prn, 10);
212     text += st_prn;
213     if (you.strength < you.max_strength)
214     {
215         text += "/";
216         itoa(you.max_strength, st_prn, 10);
217         text += st_prn;
218     }
219 
220     text += "     Dexterity ";
221     itoa(you.dex, st_prn, 10);
222     text += st_prn;
223     if (you.dex < you.max_dex)
224     {
225         text += "/";
226         itoa(you.max_dex, st_prn, 10);
227         text += st_prn;
228     }
229 
230     text += "     Intelligence ";
231     itoa(you.intel, st_prn, 10);
232     text += st_prn;
233     if (you.intel < you.max_intel)
234     {
235         text += "/";
236         itoa(you.max_intel, st_prn, 10);
237         text += st_prn;
238     }
239     text += EOL;
240 
241     text += "Hit Points : ";
242     itoa(you.hp, st_prn, 10);
243     text += st_prn;
244 
245     int max_max_hp = you.hp_max + player_rotted();
246 
247     if (you.hp < you.hp_max || max_max_hp != you.hp_max)
248     {
249         text += "/";
250         itoa(you.hp_max, st_prn, 10);
251         text += st_prn;
252 
253         if (max_max_hp != you.hp_max)
254         {
255             text += " (";
256             itoa(max_max_hp, st_prn, 10);
257             text += st_prn;
258             text += ")";
259         }
260 
261         if (you.hp < 1)
262         {
263             text += " ";
264             text += ((!you.deaths_door) ? "(dead)" : "(almost dead)");
265         }
266     }
267 
268     text += "          Magic Points : ";
269     itoa(you.magic_points, st_prn, 10);
270     text += st_prn;
271     if (you.magic_points < you.max_magic_points)
272     {
273         text += "/";
274         itoa(you.max_magic_points, st_prn, 10);
275         text += st_prn;
276     }
277     text += EOL;
278 
279     text += "AC : ";
280     itoa(player_AC(), st_prn, 10);
281     text += st_prn;
282 
283     text += "          Evasion : ";
284     itoa(player_evasion(), st_prn, 10);
285     text += st_prn;
286 
287     text += "          Shield : ";
288     itoa(player_shield_class(), st_prn, 10);
289     text += st_prn;
290     text += EOL;
291 
292     text += "GP : ";
293     itoa( you.gold, st_prn, 10 );
294     text += st_prn;
295     text += EOL;
296     text += EOL;
297 }                               // end dump_stats()
298 
299  //---------------------------------------------------------------
300  //
301  // dump_location
302  //
303  //---------------------------------------------------------------
dump_location(std::string & text)304 static void dump_location( std::string & text )
305 {
306     if (you.level_type != LEVEL_DUNGEON || you.your_level != -1)
307         text += "You are ";
308 
309     if (you.level_type == LEVEL_PANDEMONIUM)
310         text += "in Pandemonium";
311     else if (you.level_type == LEVEL_ABYSS)
312         text += "in the Abyss";
313     else if (you.level_type == LEVEL_LABYRINTH)
314         text += "in a labyrinth";
315     else if (you.where_are_you == BRANCH_DIS)
316         text += "in Dis";
317     else if (you.where_are_you == BRANCH_GEHENNA)
318         text += "in Gehenna";
319     else if (you.where_are_you == BRANCH_VESTIBULE_OF_HELL)
320         text += "in the Vestibule of Hell";
321     else if (you.where_are_you == BRANCH_COCYTUS)
322         text += "in Cocytus";
323     else if (you.where_are_you == BRANCH_TARTARUS)
324         text += "in Tartarus";
325     else if (you.where_are_you == BRANCH_INFERNO)
326         text += "in the Inferno";
327     else if (you.where_are_you == BRANCH_THE_PIT)
328         text += "in the Pit";
329     else if (you.where_are_you == BRANCH_ORCISH_MINES)
330         text += "in the Mines";
331     else if (you.where_are_you == BRANCH_HIVE)
332         text += "in the Hive";
333     else if (you.where_are_you == BRANCH_LAIR)
334         text += "in the Lair";
335     else if (you.where_are_you == BRANCH_SLIME_PITS)
336         text += "in the Slime Pits";
337     else if (you.where_are_you == BRANCH_VAULTS)
338         text += "in the Vaults";
339     else if (you.where_are_you == BRANCH_CRYPT)
340         text += "in the Crypt";
341     else if (you.where_are_you == BRANCH_HALL_OF_BLADES)
342         text += "in the Hall of Blades";
343     else if (you.where_are_you == BRANCH_HALL_OF_ZOT)
344         text += "in the Hall of Zot";
345     else if (you.where_are_you == BRANCH_ECUMENICAL_TEMPLE)
346         text += "in the Ecumenical Temple";
347     else if (you.where_are_you == BRANCH_SNAKE_PIT)
348         text += "in the Snake Pit";
349     else if (you.where_are_you == BRANCH_ELVEN_HALLS)
350         text += "in the Elven Halls";
351     else if (you.where_are_you == BRANCH_TOMB)
352         text += "in the Tomb";
353     else if (you.where_are_you == BRANCH_SWAMP)
354         text += "in the Swamp";
355     else
356     {
357         if (you.your_level == -1)
358             text += "You escaped";
359         else
360         {
361             text += "on level ";
362 
363             char st_prn[20];
364             itoa(you.your_level + 1, st_prn, 10);
365             text += st_prn;
366         }
367     }
368 
369     text += ".";
370     text += EOL;
371 }                               // end dump_location()
372 
373  //---------------------------------------------------------------
374  //
375  // dump_religion
376  //
377  //---------------------------------------------------------------
dump_religion(std::string & text)378 static void dump_religion( std::string & text )
379 {
380     if (you.religion != GOD_NO_GOD)
381     {
382         text += "You worship ";
383         text += god_name(you.religion);
384         text += ".";
385         text += EOL;
386 
387         if (!player_under_penance())
388         {
389             if (you.religion != GOD_XOM)
390             {                   // Xom doesn't care
391                 text += god_name(you.religion);
392                 text += " is ";
393                 text += ((you.piety <= 5) ? "displeased" :
394                          (you.piety <= 20) ? "noncommittal" :
395                          (you.piety <= 40) ? "pleased with you" :
396                          (you.piety <= 70) ? "most pleased with you" :
397                          (you.piety <= 100) ? "greatly pleased with you" :
398                          (you.piety <= 130) ? "extremely pleased with you"
399                          : "exalted by your worship");
400                 text += ".";
401                 text += EOL;
402             }
403         }
404         else
405         {
406             text += god_name(you.religion);
407             text += " is demanding penance.";
408             text += EOL;
409         }
410     }
411 }                               // end dump_religion()
412 
413  //---------------------------------------------------------------
414  //
415  // dump_inventory
416  //
417  //---------------------------------------------------------------
dump_inventory(std::string & text,bool show_prices)418 static void dump_inventory( std::string & text, bool show_prices )
419 {
420     int i, j;
421     char temp_id[4][50];
422 
423     std::string text2;
424 
425     for (i = 0; i < 4; i++)
426     {
427         for (j = 0; j < 50; j++)
428         {
429             temp_id[i][j] = 1;
430         }
431     }
432 
433     char st_pass[ ITEMNAME_SIZE ] = "";
434     int inv_class2[OBJ_GOLD];
435     int inv_count = 0;
436     char tmp_quant[20];
437 
438     for (i = 0; i < OBJ_GOLD; i++)
439     {
440         inv_class2[i] = 0;
441     }
442 
443     for (i = 0; i < ENDOFPACK; i++)
444     {
445         if (is_valid_item( you.inv[i] ))
446         {
447             // adds up number of each class in invent.
448             inv_class2[you.inv[i].base_type]++;
449             inv_count++;
450         }
451     }
452 
453     if (!inv_count)
454     {
455         text += "You aren't carrying anything.";
456         text += EOL;
457     }
458     else
459     {
460         text += "  Inventory:";
461         text += EOL;
462 
463         for (i = 0; i < OBJ_GOLD; i++)
464         {
465             if (inv_class2[i] != 0)
466             {
467                 switch (i)
468                 {
469                 case OBJ_WEAPONS:    text += "Hand weapons";    break;
470                 case OBJ_MISSILES:   text += "Missiles";        break;
471                 case OBJ_ARMOUR:     text += "Armour";          break;
472                 case OBJ_WANDS:      text += "Magical devices"; break;
473                 case OBJ_FOOD:       text += "Comestibles";     break;
474                 case OBJ_SCROLLS:    text += "Scrolls";         break;
475                 case OBJ_JEWELLERY:  text += "Jewellery";       break;
476                 case OBJ_POTIONS:    text += "Potions";         break;
477                 case OBJ_BOOKS:      text += "Books";           break;
478                 case OBJ_STAVES:     text += "Magical staves";  break;
479                 case OBJ_ORBS:       text += "Orbs of Power";   break;
480                 case OBJ_MISCELLANY: text += "Miscellaneous";   break;
481                 case OBJ_CORPSES:    text += "Carrion";         break;
482 
483                 default:
484                     DEBUGSTR("Bad item class");
485                 }
486                 text += EOL;
487 
488                 for (j = 0; j < ENDOFPACK; j++)
489                 {
490                     if (is_valid_item(you.inv[j]) && you.inv[j].base_type == i)
491                     {
492                         text += " ";
493 
494                         in_name( j, DESC_INVENTORY_EQUIP, st_pass );
495                         text += st_pass;
496 
497                         inv_count--;
498 
499                         if (show_prices)
500                         {
501                             text += " (";
502 
503                             itoa( item_value( you.inv[j], temp_id, true ),
504                                   tmp_quant, 10 );
505 
506                             text += tmp_quant;
507                             text += " gold)";
508                         }
509 
510                         if (is_dumpable_artifact( you.inv[j],
511                                                   Options.verbose_dump ))
512                         {
513                             text2 = get_item_description( you.inv[j],
514                                                           Options.verbose_dump,
515                                                           true );
516 
517                             text += munge_description(text2);
518                         }
519                         else
520                         {
521                             text += EOL;
522                         }
523                     }
524                 }
525             }
526         }
527     }
528 }                               // end dump_inventory()
529 
530 //---------------------------------------------------------------
531 //
532 // dump_skills
533 //
534 //---------------------------------------------------------------
dump_skills(std::string & text)535 static void dump_skills( std::string & text )
536 {
537     char tmp_quant[20];
538 
539     text += EOL;
540     text += EOL;
541     text += "   Skills:";
542     text += EOL;
543 
544     for (unsigned char i = 0; i < 50; i++)
545     {
546         if (you.skills[i] > 0)
547         {
548             text += ( (you.skills[i] == 27)   ? " * " :
549                       (you.practise_skill[i]) ? " + "
550                                               : " - " );
551 
552             text += "Level ";
553             itoa( you.skills[i], tmp_quant, 10 );
554             text += tmp_quant;
555             text += " ";
556             text += skill_name(i);
557             text += EOL;
558         }
559     }
560 
561     text += EOL;
562     text += EOL;
563 }                               // end dump_skills()
564 
565 //---------------------------------------------------------------
566 //
567 // Return string of the i-th spell type, with slash if required
568 //
569 //---------------------------------------------------------------
spell_type_name(int spell_class,bool slash)570 static std::string spell_type_name(int spell_class, bool slash)
571 {
572     std::string ret;
573 
574     if (slash)
575         ret = "/";
576 
577     ret += spelltype_name(spell_class);
578 
579     return (ret);
580 }                               // end spell_type_name()
581 
582 //---------------------------------------------------------------
583 //
584 // dump_spells
585 //
586 //---------------------------------------------------------------
dump_spells(std::string & text)587 static void dump_spells( std::string & text )
588 {
589     char tmp_quant[20];
590 
591 // This array helps output the spell types in the traditional order.
592 // this can be tossed as soon as I reorder the enum to the traditional order {dlb}
593     const int spell_type_index[] = {
594         SPTYP_HOLY,
595         SPTYP_POISON,
596         SPTYP_FIRE,
597         SPTYP_ICE,
598         SPTYP_EARTH,
599         SPTYP_AIR,
600         SPTYP_CONJURATION,
601         SPTYP_ENCHANTMENT,
602         SPTYP_DIVINATION,
603         SPTYP_TRANSLOCATION,
604         SPTYP_SUMMONING,
605         SPTYP_TRANSMIGRATION,
606         SPTYP_NECROMANCY,
607         0
608     };
609 
610     int spell_levels = player_spell_levels();
611 
612     if (spell_levels == 1)
613         text += "You have one spell level left.";
614     else if (spell_levels == 0)
615         text += "You cannot memorise any spells.";
616     else
617     {
618         text += "You have ";
619         itoa( spell_levels, tmp_quant, 10 );
620         text += tmp_quant;
621         text += " spell levels left.";
622     }
623 
624     text += EOL;
625 
626     if (!you.spell_no)
627     {
628         text += "You don't know any spells.";
629         text += EOL;
630 
631     }
632     else
633     {
634         text += "You know the following spells:" EOL;
635         text += EOL;
636 
637         text += "  Your Spells                       Type                  Success   Level" EOL;
638 
639         for (int j = 0; j < 52; j++)
640         {
641             const char letter = index_to_letter( j );
642             const int  spell  = get_spell_by_letter( letter );
643 
644             if (spell != SPELL_NO_SPELL)
645             {
646                 std::string spell_line = " ";
647 
648                 char strng[2];
649                 strng[0] = letter;
650                 strng[1] = '\0';
651 
652                 spell_line += strng;
653                 spell_line += " - ";
654                 spell_line += spell_title( spell );
655 
656                 for (int i = spell_line.length(); i < 34; i++)
657                 {
658                     spell_line += ' ';
659                 }
660 
661                 bool already = false;
662 
663                 for (int i = 0; spell_type_index[i] != 0; i++)
664                 {
665                     if (spell_typematch( spell, spell_type_index[i] ))
666                     {
667                         spell_line +=
668                                 spell_type_name(spell_type_index[i], already);
669                         already = true;
670                     }
671                 }
672 
673                 for (int i = spell_line.length(); i < 58; i++)
674                 {
675                     spell_line += ' ';
676                 }
677 
678                 int fail_rate = spell_fail( spell );
679 
680                 spell_line += (fail_rate == 100) ? "Useless"   :
681                               (fail_rate >   90) ? "Terrible"  :
682                               (fail_rate >   80) ? "Cruddy"    :
683                               (fail_rate >   70) ? "Bad"       :
684                               (fail_rate >   60) ? "Very Poor" :
685                               (fail_rate >   50) ? "Poor"      :
686                               (fail_rate >   40) ? "Fair"      :
687                               (fail_rate >   30) ? "Good"      :
688                               (fail_rate >   20) ? "Very Good" :
689                               (fail_rate >   10) ? "Great"     :
690                               (fail_rate >   0)  ? "Excellent"
691                                                  : "Perfect";
692 
693                 for (int i = spell_line.length(); i < 70; i++)
694                     spell_line += ' ';
695 
696                 itoa((int) spell_difficulty( spell ), tmp_quant, 10 );
697                 spell_line += tmp_quant;
698                 spell_line += EOL;
699 
700                 text += spell_line;
701             }
702         }
703     }
704 }                               // end dump_spells()
705 
706 //---------------------------------------------------------------
707 //
708 // dump_mutations
709 //
710 //---------------------------------------------------------------
dump_mutations(std::string & text)711 static void dump_mutations( std::string & text )
712 {
713     // Can't use how_mutated() here, as it doesn't count demonic powers
714     int xz = 0;
715 
716     for (int xy = 0; xy < 100; xy++)
717     {
718         if (you.mutation[xy] > 0)
719             xz++;
720     }
721 
722     if (xz > 0)
723     {
724         text += "";
725         text += EOL;
726         text += "           Mutations & Other Weirdness";
727         text += EOL;
728 
729         for (int j = 0; j < 100; j++)
730         {
731             if (you.mutation[j])
732             {
733                 if (you.demon_pow[j] > 0)
734                     text += "* ";
735 
736                 text += mutation_name(j);
737                 text += EOL;
738             }
739         }
740     }
741 }                               // end dump_mutations()
742 
743 #if MAC
744 #pragma mark -
745 #endif
746 
747 // ========================================================================
748 //      Public Functions
749 // ========================================================================
750 
751 //---------------------------------------------------------------
752 //
753 // dump_char
754 //
755 // Creates a disk record of a character. Returns true if the
756 // character was successfully saved.
757 //
758 //---------------------------------------------------------------
dump_char(const char fname[30],bool show_prices)759 bool dump_char( const char fname[30], bool show_prices )  // $$$ a try block?
760 {
761     bool succeeded = false;
762 
763     std::string text;
764 
765     // start with enough room for 100 80 character lines
766     text.reserve(100 * 80);
767 
768     text += " Dungeon Crawl version " VERSION " character file.";
769     text += EOL;
770     text += EOL;
771 
772     dump_stats(text);
773     dump_location(text);
774     dump_religion(text);
775 
776     switch (you.burden_state)
777     {
778     case BS_OVERLOADED:
779         text += "You are overloaded with stuff.";
780         text += EOL;
781         break;
782     case BS_ENCUMBERED:
783         text += "You are encumbered.";
784         text += EOL;
785         break;
786     }
787 
788     text += "You are ";
789 
790     text += ((you.hunger <= 1000) ? "starving" :
791              (you.hunger <= 2600) ? "hungry" :
792              (you.hunger < 7000) ? "not hungry" :
793              (you.hunger < 11000) ? "full" : "completely stuffed");
794 
795     text += ".";
796     text += EOL;
797     text += EOL;
798 
799     if (you.attribute[ATTR_TRANSFORMATION])
800     {
801         switch (you.attribute[ATTR_TRANSFORMATION])
802         {
803         case TRAN_SPIDER:
804             text += "You are in spider-form.";
805             break;
806         case TRAN_BLADE_HANDS:
807             text += "Your hands are blades.";
808             break;
809         case TRAN_STATUE:
810             text += "You are a stone statue.";
811             break;
812         case TRAN_ICE_BEAST:
813             text += "You are a creature of crystalline ice.";
814             break;
815         case TRAN_DRAGON:
816             text += "You are a fearsome dragon!";
817             break;
818         case TRAN_LICH:
819             text += "You are in lich-form.";
820             break;
821         case TRAN_SERPENT_OF_HELL:
822             text += "You are a huge, demonic serpent!";
823             break;
824         case TRAN_AIR:
825             text += "You are a cloud of diffuse gas.";
826             break;
827         }
828 
829         text += EOL;
830         text += EOL;
831     }
832 
833     dump_inventory(text, show_prices);
834 
835     char tmp_quant[20];
836 
837     text += EOL;
838     text += EOL;
839     text += " You have ";
840     itoa( you.exp_available, tmp_quant, 10 );
841     text += tmp_quant;
842     text += " experience left.";
843 
844     dump_skills(text);
845     dump_spells(text);
846     dump_mutations(text);
847 
848     char file_name[kPathLen] = "\0";
849 
850     if (SysEnv.crawl_dir)
851         strncpy(file_name, SysEnv.crawl_dir, kPathLen);
852 
853     strncat(file_name, fname, kPathLen);
854 
855     if (strcmp(fname, "morgue.txt") != 0)
856         strncat(file_name, ".txt", kPathLen);
857 
858     FILE *handle = fopen(file_name, "wb");
859 
860 #if DEBUG_DIAGNOSTICS
861     strcpy( info, "File name: " );
862     strcat( info, file_name );
863     mpr( info, MSGCH_DIAGNOSTICS );
864 #endif
865 
866     if (handle != NULL)
867     {
868         size_t begin = 0;
869         size_t end = text.find(EOL);
870 
871         while (end != std::string::npos)
872         {
873             end += strlen(EOL);
874 
875             size_t len = end - begin;
876 
877             if (len > 80)
878                 len = 80;
879 
880             fwrite(text.c_str() + begin, len, 1, handle);
881 
882             begin = end;
883             end = text.find(EOL, end);
884         }
885 
886         fclose(handle);
887         succeeded = true;
888     }
889     else
890         mpr("Error opening file.");
891 
892     return (succeeded);
893 }                               // end dump_char()
894