Atrinik Server 2.5
server/recipe.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00042 #include <global.h>
00043 
00045 static recipelist *formulalist;
00046 
00047 static void check_formulae();
00048 static archetype *find_treasure_by_name(treasure *t, char *name, int depth);
00049 static long find_ingred_cost(const char *name);
00050 static const char *ingred_name(const char *name);
00051 static int numb_ingred(const char *buf);
00052 static recipelist *get_random_recipelist();
00053 
00057 static recipelist *init_recipelist()
00058 {
00059     recipelist *tl = (recipelist *) malloc(sizeof(recipelist));
00060 
00061     if (tl == NULL)
00062     {
00063         LOG(llevError, "init_recipelist(): Out of memory.\n");
00064     }
00065 
00066     tl->total_chance = 0;
00067     tl->number = 0;
00068     tl->items = NULL;
00069     tl->next = NULL;
00070     return tl;
00071 }
00072 
00076 static recipe *get_empty_formula()
00077 {
00078     recipe *t = (recipe *) malloc(sizeof(recipe));
00079 
00080     if (t == NULL)
00081     {
00082         LOG(llevError, "get_empty_formula(): Out of memory.\n");
00083     }
00084 
00085     t->chance = 0;
00086     t->index = 0;
00087     t->transmute = 0;
00088     t->yield = 0;
00089     t->keycode = 0;
00090     t->title = NULL;
00091     t->arch_name = NULL;
00092     t->ingred = NULL;
00093     t->next = NULL;
00094     return t;
00095 }
00096 
00101 recipelist *get_formulalist(int i)
00102 {
00103     recipelist *fl = formulalist;
00104     int number = i;
00105 
00106     while (fl && number > 1)
00107     {
00108         if (!(fl = fl->next))
00109         {
00110             break;
00111         }
00112 
00113         number--;
00114     }
00115 
00116     return fl;
00117 }
00118 
00123 static int check_recipe(recipe *rp)
00124 {
00125     if (find_archetype(rp->arch_name) != NULL)
00126     {
00127         artifact *art = locate_recipe_artifact(rp);
00128 
00129         if (!art && rp->title != shstr_cons.NONE)
00130         {
00131             LOG(llevBug, "Formula %s of %s has no artifact.\n", rp->arch_name, rp->title);
00132             return 0;
00133         }
00134     }
00135     else
00136     {
00137         LOG(llevBug, "Can't find archetype:%s for formula:%s\n", rp->arch_name, rp->title);
00138         return 0;
00139     }
00140 
00141     return 1;
00142 }
00143 
00146 void init_formulae()
00147 {
00148     static int has_been_done = 0;
00149     FILE *fp;
00150     char filename[MAX_BUF], buf[MAX_BUF], *cp, *next;
00151     recipe *formula = NULL;
00152     recipelist *fl = init_recipelist();
00153     linked_char *tmp;
00154     int value, comp;
00155 
00156     if (!formulalist)
00157     {
00158         formulalist = fl;
00159     }
00160 
00161     if (has_been_done)
00162     {
00163         return;
00164     }
00165     else
00166     {
00167         has_been_done = 1;
00168     }
00169 
00170     snprintf(filename, sizeof(filename), "%s/formulae", settings.datadir);
00171     LOG(llevDebug, "Reading alchemical formulae from %s...",filename);
00172 
00173     if ((fp = open_and_uncompress(filename, 0, &comp)) == NULL)
00174     {
00175         LOG(llevBug, "Can't open %s.\n", filename);
00176         return;
00177     }
00178 
00179     while (fgets(buf, MAX_BUF, fp) != NULL)
00180     {
00181         if (*buf == '#')
00182         {
00183             continue;
00184         }
00185 
00186         if ((cp = strchr(buf, '\n')) != NULL)
00187         {
00188             *cp = '\0';
00189         }
00190 
00191         cp = buf;
00192 
00193         /* Skip blanks */
00194         while (*cp == ' ')
00195         {
00196             cp++;
00197         }
00198 
00199         if (!strncmp(cp, "Object", 6))
00200         {
00201             formula = get_empty_formula();
00202             FREE_AND_COPY_HASH(formula->title, strchr(cp, ' ') + 1);
00203         }
00204         else if (!strncmp(cp, "keycode", 7))
00205         {
00206             FREE_AND_COPY_HASH(formula->keycode, strchr(cp, ' ') + 1);
00207         }
00208         else if (sscanf(cp, "trans %d", &value))
00209         {
00210             formula->transmute = (uint16) value;
00211         }
00212         else if (sscanf(cp, "yield %d", &value))
00213         {
00214             formula->yield = (uint16) value;
00215         }
00216         else if (sscanf(cp, "chance %d", &value))
00217         {
00218             formula->chance = (uint16) value;
00219         }
00220         else if (!strncmp(cp, "ingred", 6))
00221         {
00222             int num_ingred = 1;
00223             cp = strchr(cp, ' ') + 1;
00224 
00225             do
00226             {
00227                 if ((next = strchr(cp, ',')) != NULL)
00228                 {
00229                     *(next++) = '\0';
00230                     num_ingred++;
00231                 }
00232 
00233                 tmp = (linked_char *) malloc(sizeof(linked_char));
00234                 tmp->name = NULL;
00235                 FREE_AND_COPY_HASH(tmp->name, cp);
00236                 tmp->next = formula->ingred;
00237                 formula->ingred = tmp;
00238 
00239                 /* each ingredient's ASCII value is coadded. Later on this
00240                  * value will be used allow us to search the formula lists
00241                  * quickly for the right recipe. */
00242                 formula->index += strtoint(cp);
00243             }
00244             while ((cp = next) != NULL);
00245 
00246             /* now find the correct (# of ingred ordered) formulalist */
00247             fl = formulalist;
00248 
00249             while (num_ingred != 1)
00250             {
00251                 if (!fl->next)
00252                 {
00253                     fl->next = init_recipelist();
00254                 }
00255 
00256                 fl = fl->next;
00257                 num_ingred--;
00258             }
00259 
00260             fl->total_chance += formula->chance;
00261             fl->number++;
00262             formula->next = fl->items;
00263             fl->items = formula;
00264         }
00265         else if (!strncmp(cp, "arch", 4))
00266         {
00267             FREE_AND_COPY_HASH(formula->arch_name, strchr(cp, ' ') + 1);
00268             (void) check_recipe(formula);
00269         }
00270         else
00271         {
00272             LOG(llevBug, "Unknown input in file %s: %s\n", filename, buf);
00273         }
00274     }
00275 
00276     LOG(llevDebug, "done.\n");
00277     close_and_delete(fp, comp);
00278     /* Lastly, lets check for problems in formula we got */
00279     check_formulae();
00280 }
00281 
00290 static void check_formulae()
00291 {
00292     recipelist *fl;
00293     recipe *check, *formula;
00294     int numb = 1;
00295 
00296     LOG(llevDebug,"Checking formulae lists...");
00297 
00298     for (fl = formulalist; fl != NULL; fl = fl->next)
00299     {
00300         for (formula = fl->items; formula != NULL; formula = formula->next)
00301         {
00302             for (check = formula->next; check != NULL; check = check->next)
00303             {
00304                 if (check->index == formula->index)
00305                 {
00306                     LOG(llevBug, "On %d ingred list: ", numb);
00307                     LOG(llevBug, "Formulae [%s] of %s and [%s] of %s have matching index id (%d)\n", formula->arch_name, formula->title, check->arch_name, check->title, formula->index);
00308                 }
00309             }
00310         }
00311 
00312         numb++;
00313     }
00314 
00315     LOG(llevDebug, "done.\n");
00316 }
00317 
00320 void dump_alchemy()
00321 {
00322     recipelist *fl = formulalist;
00323     recipe *formula = NULL;
00324     linked_char *next;
00325     int num_ingred = 1;
00326 
00327     LOG(llevInfo, "\n");
00328 
00329     while (fl)
00330     {
00331         LOG(llevInfo, "\n Formulae with %d ingredient%s  %d Formulae with total_chance=%d\n", num_ingred, num_ingred > 1 ? "s." : ".", fl->number, fl->total_chance);
00332 
00333         for (formula = fl->items; formula != NULL; formula = formula->next)
00334         {
00335             artifact *art = NULL;
00336             char buf[MAX_BUF], tmpbuf[MAX_BUF], *string;
00337 
00338             strncpy(tmpbuf, formula->arch_name, MAX_BUF - 1);
00339             tmpbuf[MAX_BUF - 1] = 0;
00340             string = strtok(tmpbuf, ",");
00341 
00342             while (string)
00343             {
00344                 if (find_archetype(string) != NULL)
00345                 {
00346                     art = locate_recipe_artifact(formula);
00347 
00348                     if (!art && formula->title != shstr_cons.NONE)
00349                     {
00350                         LOG(llevBug, "Formula %s has no artifact\n", formula->title);
00351                     }
00352                     else
00353                     {
00354                         if (formula->title != shstr_cons.NONE)
00355                         {
00356                             snprintf(buf, sizeof(buf), "%s of %s", string, formula->title);
00357                         }
00358                         else
00359                         {
00360                             snprintf(buf, sizeof(buf), "%s", string);
00361                         }
00362 
00363                         LOG(llevInfo, "%-30s(%d) bookchance %3d  ", buf, formula->index, formula->chance);
00364                         LOG(llevInfo, "\n");
00365 
00366                         if (formula->ingred != NULL)
00367                         {
00368                             int nval = 0, tval = 0;
00369                             LOG(llevInfo, "\tIngred: ");
00370 
00371                             for (next = formula->ingred; next != NULL; next = next->next)
00372                             {
00373                                 if (nval != 0)
00374                                 {
00375                                     LOG(llevInfo, ",");
00376                                 }
00377 
00378                                 LOG(llevInfo, "%s(%d)", next->name, (nval = strtoint(next->name)));
00379                                 tval += nval;
00380                             }
00381 
00382                             LOG(llevInfo, "\n");
00383 
00384                             if (tval != formula->index)
00385                             {
00386                                 LOG(llevInfo, "ingredient list and formula values not equal.\n");
00387                             }
00388                         }
00389                     }
00390                 }
00391                 else
00392                 {
00393                     LOG(llevBug, "Can't find archetype:%s for formula %s\n", string, formula->title);
00394                 }
00395 
00396                 string = strtok(NULL, ",");
00397             }
00398         }
00399 
00400         LOG(llevInfo, "\n");
00401         fl = fl->next;
00402         num_ingred++;
00403     }
00404 }
00405 
00414 static archetype *find_treasure_by_name(treasure *t, char *name, int depth)
00415 {
00416     treasurelist *tl;
00417     archetype *at;
00418 
00419     if (depth > 10)
00420     {
00421         return NULL;
00422     }
00423 
00424     while (t != NULL)
00425     {
00426         if (t->name != NULL)
00427         {
00428             tl = find_treasurelist(t->name);
00429             at = find_treasure_by_name(tl->items, name, depth + 1);
00430 
00431             if (at != NULL)
00432             {
00433                 return at;
00434             }
00435         }
00436         else
00437         {
00438             if (!strcasecmp(t->item->clone.name, name))
00439             {
00440                 return t->item;
00441             }
00442         }
00443 
00444         if (t->next_yes != NULL)
00445         {
00446             at = find_treasure_by_name(t->next_yes, name, depth);
00447 
00448             if (at != NULL)
00449             {
00450                 return at;
00451             }
00452         }
00453 
00454         if (t->next_no != NULL)
00455         {
00456             at = find_treasure_by_name(t->next_no, name, depth);
00457 
00458             if (at != NULL)
00459             {
00460                 return at;
00461             }
00462         }
00463 
00464         t = t->next;
00465     }
00466 
00467     return NULL;
00468 }
00469 
00484 static long find_ingred_cost(const char *name)
00485 {
00486     archetype *at, *at2;
00487     artifactlist *al;
00488     artifact *art;
00489     long mult;
00490     char *cp;
00491     char part1[100], part2[100];
00492 
00493     /* Same as atoi(), but skip number */
00494     mult = 0;
00495 
00496     while (isdigit(*name))
00497     {
00498         mult = 10 * mult + (*name - '0');
00499         name++;
00500     }
00501 
00502     if (mult > 0)
00503     {
00504         name++;
00505     }
00506     else
00507     {
00508         mult = 1;
00509     }
00510 
00511     /* First, try to match the name of an archetype */
00512     for (at = first_archetype; at != NULL; at = at->next)
00513     {
00514         if (at->clone.title != NULL)
00515         {
00516             /* Inefficient, but who cares? */
00517             snprintf(part1, sizeof(part1), "%s %s", at->clone.name, at->clone.title);
00518 
00519             if (!strcasecmp(part1, name))
00520             {
00521                 return mult * at->clone.value;
00522             }
00523         }
00524 
00525         if (!strcasecmp(at->clone.name, name))
00526         {
00527             return mult * at->clone.value;
00528         }
00529     }
00530 
00531     /* Second, try to match an artifact ("arch of something") */
00532     cp = strstr(name, " of ");
00533 
00534     if (cp != NULL)
00535     {
00536         strcpy(part1, name);
00537         part1[cp - name] = '\0';
00538         strcpy(part2, cp + 4);
00539 
00540         /* Find the first archetype matching the first part of the name */
00541         for (at = first_archetype; at != NULL; at = at->next)
00542         {
00543             if (!strcasecmp(at->clone.name, part1) && at->clone.title == NULL)
00544             {
00545                 break;
00546             }
00547         }
00548 
00549         if (at != NULL)
00550         {
00551             /* Find the first artifact derived from that archetype (same type) */
00552             for (al = first_artifactlist; al != NULL; al = al->next)
00553             {
00554                 if (al->type == at->clone.type)
00555                 {
00556                     for (art = al->items; art != NULL; art = art->next)
00557                     {
00558                         if (!strcasecmp(art->def_at.clone.name, part2))
00559                         {
00560                             return mult * at->clone.value * art->def_at.clone.value;
00561                         }
00562                     }
00563                 }
00564             }
00565         }
00566     }
00567 
00568     /* Third, try to match a body part ("arch's something") */
00569     cp = strstr(name, "'s ");
00570 
00571     if (cp != NULL)
00572     {
00573         strcpy(part1, name);
00574         part1[cp - name] = '\0';
00575         strcpy(part2, cp + 3);
00576 
00577         /* Examine all archetypes matching the first part of the name */
00578         for (at = first_archetype; at != NULL; at = at->next)
00579         {
00580             if (!strcasecmp (at->clone.name, part1) && at->clone.title == NULL)
00581             {
00582                 if (at->clone.randomitems != NULL)
00583                 {
00584                     at2 = find_treasure_by_name(at->clone.randomitems->items, part2, 0);
00585 
00586                     if (at2 != NULL)
00587                     {
00588                         return mult * at2->clone.value * isqrt(at->clone.level * 2);
00589                     }
00590                 }
00591             }
00592         }
00593     }
00594 
00595     /* Failed to find any matching items -- formula should be checked */
00596     return -1;
00597 }
00598 
00601 void dump_alchemy_costs()
00602 {
00603     recipelist *fl = formulalist;
00604     recipe *formula = NULL;
00605     linked_char *next;
00606     int num_ingred = 1, num_errors = 0;
00607     long cost, tcost;
00608 
00609     LOG(llevInfo, "\n");
00610 
00611     while (fl)
00612     {
00613         LOG(llevInfo, "\n Formulae with %d ingredient%s  %d Formulae with total_chance=%d\n", num_ingred, num_ingred > 1 ? "s." : ".", fl->number, fl->total_chance);
00614 
00615         for (formula = fl->items; formula != NULL; formula = formula->next)
00616         {
00617             artifact *art = NULL;
00618             archetype *at = NULL;
00619             char buf[MAX_BUF], tmpbuf[MAX_BUF], *string;
00620 
00621             strncpy(tmpbuf, formula->arch_name, MAX_BUF - 1);
00622             tmpbuf[MAX_BUF - 1] = '\0';
00623             string = strtok(tmpbuf, ",");
00624 
00625             while (string)
00626             {
00627                 if ((at = find_archetype(string)) != NULL)
00628                 {
00629                     art = locate_recipe_artifact(formula);
00630 
00631                     if (!art && formula->title != shstr_cons.NONE)
00632                     {
00633                         LOG(llevBug, "Formula %s has no artifact\n", formula->title);
00634                     }
00635                     else
00636                     {
00637                         if (formula->title == shstr_cons.NONE)
00638                         {
00639                             snprintf(buf, sizeof(buf), "%s", string);
00640                         }
00641                         else
00642                         {
00643                             snprintf(buf, sizeof(buf), "%s of %s", string, formula->title);
00644                         }
00645 
00646                         LOG(llevInfo, "\n%-40s bookchance %3d\n", buf, formula->chance);
00647 
00648                         if (formula->ingred != NULL)
00649                         {
00650                             tcost = 0;
00651 
00652                             for (next = formula->ingred; next != NULL; next = next->next)
00653                             {
00654                                 cost = find_ingred_cost(next->name);
00655 
00656                                 if (cost < 0)
00657                                 {
00658                                     num_errors++;
00659                                 }
00660 
00661                                 LOG(llevInfo, "\t%-33s%5ld\n", next->name, cost);
00662 
00663                                 if (cost < 0 || tcost < 0)
00664                                 {
00665                                     tcost = -1;
00666                                 }
00667                                 else
00668                                 {
00669                                     tcost += cost;
00670                                 }
00671                             }
00672 
00673                             if (art != NULL && &art->def_at.clone != NULL)
00674                             {
00675                                 cost = at->clone.value * art->def_at.clone.value;
00676                             }
00677                             else
00678                             {
00679                                 cost = at->clone.value;
00680                             }
00681 
00682                             LOG(llevInfo, "\t\tBuying result costs: %5ld", cost);
00683 
00684                             if (formula->yield > 1)
00685                             {
00686                                 LOG(llevInfo, " to %ld (max %d items)\n", cost * formula->yield, formula->yield);
00687                                 cost = cost * (formula->yield + 1L) / 2L;
00688                             }
00689                             else
00690                             {
00691                                 LOG(llevInfo, "\n");
00692                             }
00693 
00694                             LOG(llevInfo, "\t\tIngredients cost:    %5ld\n\t\tComment: ", tcost);
00695 
00696                             if (tcost < 0)
00697                             {
00698                                 LOG(llevInfo, "Could not find some ingredients. Check the formula!\n");
00699                             }
00700                             else if (tcost > cost)
00701                             {
00702                                 LOG(llevInfo, "Ingredients are much expensive. Useless formula.\n");
00703                             }
00704                             else if (tcost * 2L > cost)
00705                             {
00706                                 LOG(llevInfo, "Ingredients are too expensive.\n");
00707                             }
00708                             else if (tcost * 10L < cost)
00709                             {
00710                                 LOG(llevInfo, "Ingredients are too cheap.\n");
00711                             }
00712                             else
00713                             {
00714                                 LOG(llevInfo, "OK.\n");
00715                             }
00716                         }
00717                     }
00718                 }
00719                 else
00720                 {
00721                     LOG(llevBug, "Can't find archetype:%s for formula %s\n", string, formula->title);
00722                 }
00723 
00724                 string = strtok(NULL, ",");
00725             }
00726         }
00727 
00728         LOG(llevInfo, "\n");
00729         fl = fl->next;
00730         num_ingred++;
00731     }
00732 
00733     if (num_errors > 0)
00734     {
00735         LOG(llevInfo, "%d objects required by the formulae do not exist in the game.\n", num_errors);
00736     }
00737 }
00738 
00744 static const char *ingred_name(const char *name)
00745 {
00746     const char *cp = name;
00747 
00748     if (atoi(cp))
00749     {
00750         cp = strchr(cp, ' ') + 1;
00751     }
00752 
00753     return cp;
00754 }
00755 
00763 int strtoint(const char *buf)
00764 {
00765     const char *cp = ingred_name(buf);
00766     int val = 0, mult = numb_ingred(buf);
00767     size_t len = strlen(cp);
00768 
00769     while (len)
00770     {
00771         val += tolower(*cp);
00772         cp++;
00773         len--;
00774     }
00775 
00776     return val * mult;
00777 }
00778 
00783 artifact *locate_recipe_artifact(recipe *rp)
00784 {
00785     object *item = get_archetype(rp->arch_name);
00786     artifactlist *at = NULL;
00787     artifact *art = NULL;
00788 
00789     if (!item)
00790     {
00791         return NULL;
00792     }
00793 
00794     if ((at = find_artifactlist(item->type)))
00795     {
00796         for (art = at->items; art; art = art->next)
00797         {
00798             if (!strcmp(art->def_at.clone.name, rp->title))
00799             {
00800                 break;
00801             }
00802         }
00803     }
00804 
00805     return art;
00806 }
00807 
00812 static int numb_ingred(const char *buf)
00813 {
00814     int numb;
00815 
00816     if ((numb = atoi(buf)))
00817     {
00818         return numb;
00819     }
00820 
00821     return 1;
00822 }
00823 
00827 static recipelist *get_random_recipelist()
00828 {
00829     recipelist *fl = NULL;
00830     int number = 0, roll = 0;
00831 
00832     /* First, determine # of recipelist we have */
00833     for (fl = get_formulalist(1); fl; fl = fl->next)
00834     {
00835         number++;
00836     }
00837 
00838     /* Now, randomly choose one */
00839     if (number > 0)
00840     {
00841         roll = RANDOM() % number;
00842     }
00843 
00844     fl = get_formulalist(1);
00845 
00846     while (roll && fl)
00847     {
00848         if (fl->next)
00849         {
00850             fl = fl->next;
00851         }
00852         else
00853         {
00854             break;
00855         }
00856 
00857         roll--;
00858     }
00859 
00860     /* Failed! */
00861     if (!fl)
00862     {
00863         LOG(llevBug, "get_random_recipelist(): no recipelists found!\n");
00864     }
00865     else if (fl->total_chance == 0)
00866     {
00867         fl = get_random_recipelist();
00868     }
00869 
00870     return fl;
00871 }
00872 
00879 recipe *get_random_recipe(recipelist *rpl)
00880 {
00881     recipelist *fl = rpl;
00882     recipe *rp = NULL;
00883     int r = 0;
00884 
00885     /* Looks like we have to choose a random one */
00886     if (fl == NULL)
00887     {
00888         if ((fl = get_random_recipelist()) == NULL)
00889         {
00890             return rp;
00891         }
00892     }
00893 
00894     if (fl->total_chance > 0)
00895     {
00896         r = RANDOM() % fl->total_chance;
00897 
00898         for (rp = fl->items; rp; rp = rp->next)
00899         {
00900             r -= rp->chance;
00901 
00902             if (r < 0)
00903             {
00904                 break;
00905             }
00906         }
00907     }
00908 
00909     return rp;
00910 }
00911 
00914 void free_all_recipes()
00915 {
00916     recipelist *fl = formulalist, *flnext;
00917     recipe *formula = NULL, *next;
00918     linked_char *lchar, *charnext;
00919 
00920     LOG(llevDebug, "Freeing all the recipes\n");
00921 
00922     for (fl = formulalist; fl != NULL; fl = flnext)
00923     {
00924         flnext = fl->next;
00925 
00926         for (formula = fl->items; formula != NULL; formula = next)
00927         {
00928             next = formula->next;
00929 
00930             FREE_AND_CLEAR_HASH2(formula->arch_name);
00931             FREE_AND_CLEAR_HASH2(formula->title);
00932 
00933             for (lchar = formula->ingred; lchar; lchar = charnext)
00934             {
00935                 charnext = lchar->next;
00936                 FREE_AND_CLEAR_HASH2(lchar->name);
00937                 free(lchar);
00938             }
00939 
00940             free(formula);
00941         }
00942 
00943         free(fl);
00944     }
00945 }