home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / xsokoban-31 / score.c < prev    next >
C/C++ Source or Header  |  1998-01-11  |  19KB  |  646 lines

  1. #include "config_local.h"
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <signal.h>
  6. #ifndef VMS
  7. #include <sys/types.h>
  8. #else
  9. #include "unix_types.h"
  10. #endif
  11. #include <assert.h>
  12. #include <string.h>
  13. #include <unistd.h>
  14.  
  15. #ifdef VMS
  16. #include "in.h"
  17. #else
  18. #include <netinet/in.h>
  19. #endif
  20.  
  21. #include <sys/stat.h>
  22. #include <errno.h>
  23. #include <time.h>
  24. #ifndef __DECC
  25. #include <sys/param.h>
  26. #endif
  27. #include <fcntl.h>
  28.  
  29. #include "externs.h"
  30. #include "globals.h"
  31.  
  32. #define SCORE_VERSION "xs01"
  33.  
  34. short scoreentries                            ;
  35. struct st_entry scoretable[MAXSCOREENTRIES]                ;
  36. Boolean merging=_false_                            ;
  37.  
  38. static FILE *scorefile                            ;
  39. static int sfdbn                            ;
  40.  
  41. #ifdef VMS
  42. extern void unlink()                            ;
  43. #endif
  44.  
  45.  
  46. /* Acquire the lock on the score file. This is done by creating a new
  47.    directory. If someone else holds the lock, the directory will exist.
  48.    Since mkdir() should be done synchronously, even over NFS,  it will
  49.    fail if someone else holds the lock.
  50.  
  51.    TIMEOUT is the number of seconds which you get to hold the lock on
  52.    the score file for. Also, the number of seconds after which we give
  53.    up on trying to acquire the lock nicely and start trying to break
  54.    the lock. In theory, there are still some very unlikely ways to get
  55.    hosed.  See comments in WriteScore about this. Portable locking over
  56.    NFS is hard.
  57.  
  58.    After TIMEOUT seconds, we break the lock, assuming that the old
  59.    lock-holder died in process. If the process that had the lock is
  60.    alive but just very slow, it could end up deleting the changes we
  61.    make, or we could delete its changes. However, the score file can't
  62.    get trashed this way, since rename() is used to update the score file.
  63.  
  64.    See comments in WriteScore about how the score file can get trashed    ,
  65.    though it's extremely unlikely.
  66. */
  67.  
  68. static time_t lock_time                                ;
  69. /* This timer is used to allow the writer to back out voluntarily if it
  70.    notices that its time has expired. This is not a guarantee that no
  71.    conflicts will occur, since the final rename() in WriteScore could
  72.    take arbitrarily long, running the clock beyond TIMEOUT seconds.
  73. */
  74.  
  75. short LockScore(void)
  76. {
  77.      int i, result                            ;
  78.  
  79. #ifdef VMS
  80.          lock_time = time(0)                        ;
  81. #else
  82.      for (i = 0; i < TIMEOUT; i++) {
  83.       result = mkdir(LOCKFILE, 0)                    ;
  84.       lock_time = time(0)                        ;
  85.       if (result < 0) {
  86.            if (errno == EEXIST) sleep(1)                ;
  87.            else return E_WRITESCORE                        ;
  88.       } else {
  89.           break                            ;
  90.       }
  91.      }
  92.  
  93.      if (result < 0) {
  94.      struct stat s                            ;
  95.      time_t t = time(0)                        ;
  96.      if (0 > stat(LOCKFILE, &s)) {
  97.          fprintf(stderr, "Warning: Can't mkdir or stat %s\n", LOCKFILE);
  98.          return E_WRITESCORE;                      /*   disappeared?              */
  99.      }
  100.      /* Check to make sure that the lock is still the same one we
  101.         saw in the first place. This code assumes loosely synchronized
  102.         clocks. To do it right, we'd have to create another file on
  103.         the server machine, and compare timestamps. Not worth it.
  104.      */
  105.      if (t - s.st_ctime <= TIMEOUT) {
  106.          fprintf(stderr                        ,
  107.      "Warning: some other process is mucking with the lock file\n") ;
  108.          return E_WRITESCORE                    ;
  109.      }
  110.      /* Assume that the last process to muck with the score file
  111.         is dead.
  112.      */
  113.      fprintf(stderr, "Warning: breaking old lock\n")        ;
  114.      if (0 > rmdir(LOCKFILE)) {
  115.          fprintf(stderr, "Warning: Couldn't remove old lock %s\n"    ,
  116.              LOCKFILE)                        ;
  117.          return E_WRITESCORE                    ;
  118.      }
  119.      result = mkdir(LOCKFILE, 0)                    ;
  120.      if (result < 0) {
  121.          fprintf(stderr, "Warning: Couldn't create %s\n", LOCKFILE) ;
  122.          return E_WRITESCORE                    ;
  123.      }
  124.      }
  125.  
  126. #endif
  127.      return 0                                ;
  128. }
  129.  
  130. void UnlockScore(void)
  131. {
  132. #ifndef VMS
  133.      if (0 > rmdir(LOCKFILE)) {
  134.      fprintf(stderr, "Warning: Couldn't remove lock %s\n", LOCKFILE);
  135.      }
  136. #endif
  137. }
  138.  
  139. /* print out the score list for level "level". If "level" == 0, show
  140.    scores for all levels. */
  141. short OutputScore(int level)
  142. {
  143.   short ret                                ;
  144.  
  145.   if ((ret = LockScore()))
  146.        return ret                            ;
  147.  
  148.   if ((ret = ReadScore(0)) == 0)
  149.     ShowScore(level)                            ;
  150.   UnlockScore()                                    ;
  151.   return ((ret == 0) ? E_ENDGAME : ret)                        ;
  152. }
  153.  
  154. /* create a new score file */
  155. short MakeNewScore(void)
  156. {
  157.   short ret = 0                                    ;
  158.  
  159.   if ((ret = LockScore()))
  160.        return ret                            ;
  161.  
  162.   scoreentries = 0                            ;
  163.  
  164.   if ((scorefile = fopen(SCOREFILE, "w")) == NULL)
  165.     ret = E_FOPENSCORE                            ;
  166.   else {
  167.     sfdbn = fileno(scorefile)                        ;
  168.     if (write(sfdbn, SCORE_VERSION, 4) != 4)
  169.       ret = E_WRITESCORE                        ;
  170.     else if (write(sfdbn, &scoreentries, 2) != 2)
  171.       ret = E_WRITESCORE                        ;
  172.     fclose(scorefile)                            ;
  173.   }
  174.   UnlockScore()                                    ;
  175.   return ((ret == 0) ? E_ENDGAME : ret)                        ;
  176. }
  177.  
  178. /* get the players current level based on the level they last scored on */
  179. short GetUserLevel(short *lv)
  180. {
  181.   short ret = 0, pos                            ;
  182.  
  183.   if ((ret = LockScore()))
  184.        return ret                            ;
  185.  
  186.   if ((scorefile = fopen(SCOREFILE, "r")) == NULL)
  187.     ret = E_FOPENSCORE                            ;
  188.   else {
  189.     if ((ret = ReadScore(0)) == 0)
  190.       *lv = ((pos = FindUser()) > -1) ? scoretable[pos].lv + 1 : 1    ;
  191.   }
  192.   UnlockScore()                                    ;
  193.   return (ret)                                ;
  194. }
  195.  
  196. /* Add a new score to the score file. Show the current scores if "show". */
  197. short Score(Boolean show)
  198. {
  199.   short ret                                ;
  200.  
  201.   if ((ret = LockScore()))
  202.        return ret                            ;
  203.   if ((ret = ReadScore(0)) == 0)
  204.     if ((ret = MakeScore()) == 0)
  205.       if ((ret = WriteScore()) == 0)
  206.     if (show) ShowScore(0)                        ;
  207.   UnlockScore()                                    ;
  208.   return ((ret == 0) ? E_ENDGAME : ret)                        ;
  209. }
  210.  
  211. void ntohs_entry(struct st_entry *entry)
  212. {
  213.     entry->lv = ntohs(entry->lv)                    ;
  214.     entry->mv = ntohs(entry->mv)                    ;
  215.     entry->ps = ntohs(entry->ps)                    ;
  216. }
  217.  
  218.  
  219.  
  220.  
  221. /* read in an existing score file.  Uses the ntoh() and hton() functions
  222.  * so that the score files transfer across systems.
  223.  */
  224. short ReadScore(char *filename)
  225. {
  226.   short ret = 0                                    ;
  227.   long tmp                                ;
  228.   char LSCOREFILE[132]                            ;
  229.  
  230.   if ( filename == 0)    strcpy(LSCOREFILE,SCOREFILE)            ;
  231.   else             strcpy(LSCOREFILE,filename)            ;
  232.  
  233.  
  234.   sfdbn = open(LSCOREFILE, O_RDONLY)                    ;
  235.   if (0 > sfdbn){
  236.     ret = E_FOPENSCORE                            ;
  237.   } else {
  238.     char magic[5]                            ;
  239.     if (read(sfdbn, &magic[0], 4) != 4) ret = E_READSCORE        ;
  240.     magic[4] = 0                            ;
  241.     if (0 == strcmp(magic, SCORE_VERSION)) {
  242.                                         /*   we have the right version          */
  243.     } else {
  244.     fprintf(stderr, "Warning: old-style score file\n")        ;
  245. #ifndef VMS
  246.     lseek(sfdbn, 0, SEEK_SET)                    ;
  247. #endif
  248.     }
  249.     if (read(sfdbn, &scoreentries, 2) != 2)
  250.       ret = E_READSCORE                                ;
  251.     else {
  252.       scoreentries = ntohs(scoreentries)                ;
  253.       tmp = scoreentries * sizeof(scoretable[0])            ;
  254.       if (read(sfdbn, &(scoretable[0]), tmp) != tmp)
  255.     ret = E_READSCORE                        ;
  256.  
  257.       /*   swap up for little-endian machines      */
  258.       for (tmp = 0; tmp < scoreentries; tmp++) ntohs_entry(&scoretable[tmp]);
  259.     }
  260.     (void)close(sfdbn)                            ;
  261.   }
  262.   return ret                                ;
  263. }
  264.  
  265. /* Return the solution rank for table index "j". The solution rank for
  266.    an entry is one greater than the number of entries that are better
  267.    than it, unless there is a better or equal solution that is by the
  268.    same person, in which case the solution rank is at least "BADSOLN".
  269.    If two solutions are equal, the one that was arrived at first, and
  270.    thus has a lower table index, is considered to be better.
  271.    One solution is at least as good as another solution if it is at
  272.    least as good in numbers of moves and pushes. Note that
  273.    non-comparable solutions may exist.
  274.  
  275.    The array "ignore" indicates that some scoretable entries should
  276.    be ignored for the purpose of computing rank.
  277. */
  278. #define BADSOLN 100
  279. int SolnRank(int j, Boolean *ignore)
  280. {
  281.     int i, rank = 1                            ;
  282.     unsigned short level = scoretable[j].lv                ;
  283.     for (i = 0; i < j; i++) {
  284.     if ((!ignore || !ignore[i]) && scoretable[i].lv == level) {
  285.         if (scoretable[i].mv <= scoretable[j].mv &&
  286.         scoretable[i].ps <= scoretable[j].ps)
  287.         {
  288.         if (0 == strcmp(scoretable[i].user            ,
  289.                 scoretable[j].user))
  290.             rank = BADSOLN                    ;
  291.         else
  292.             rank++                        ;
  293.         }
  294.     }
  295.     }
  296.     return rank                                    ;
  297. }
  298.  
  299. /* Removes all score entries for a user who has multiple entries    ,
  300.  * that are for a level below the user's top level, and that are not "best
  301.  * solutions" as defined by "SolnRank". Also removes duplicate entries
  302.  * for a level that is equal to the user's top level, but which are not
  303.  * the user's best solution as defined by table position.
  304.  *
  305.  * The current implementation is O(n^2) in the number of actual score entries.
  306.  * A hash table would fix this.
  307.  */
  308.  
  309. void CleanupScoreTable()
  310. {
  311.     int i                                ;
  312.     Boolean deletable[MAXSCOREENTRIES]                    ;
  313.     for (i = 0; i < scoreentries; i++) {
  314.     deletable[i] = _false_                        ;
  315.     if (SolnRank(i, deletable) > MAXSOLNRANK) {
  316.         char *user = scoretable[i].user                ;
  317.         int j                            ;
  318.         for (j = 0; j < i; j++) {
  319.         if (0 == strcmp(scoretable[j].user, user))
  320.           deletable[i] = _true_                        ;
  321.         }
  322.     }
  323.     }
  324.     FlushDeletedScores(deletable)                    ;
  325. }
  326.  
  327. /* Deletes entries from the score table for which the boolean array
  328.    contains true.
  329. */
  330. void FlushDeletedScores(Boolean delete[])
  331. {
  332.     int i, k = 0                            ;
  333.     for (i = 0; i < scoreentries; i++) {
  334.     if (i != k) CopyEntry(k, i)                    ;
  335.     if (!delete[i]) k++                        ;
  336.     }
  337.     scoreentries = k                            ;
  338. }
  339.  
  340. /* Adds a new user score to the score table, if appropriate. Users' top
  341.  * level scores, and the best scores for a particular level (in moves and
  342.  * pushes, separately considered), are always preserved.
  343.  */
  344. short MakeScore(void)
  345. {
  346.   short pos, i                                ;
  347.  
  348.   pos = FindPos();                             /*   find the new score position      */
  349.   if (pos > -1) {                             /*   score table not empty          */
  350.       for (i = scoreentries; i > pos; i--)
  351.     CopyEntry(i, i - 1)                        ;
  352.     } else {
  353.       pos = scoreentries                        ;
  354.     }
  355.  
  356.   strcpy(scoretable[pos].user, username)                ;
  357.   scoretable[pos].lv = scorelevel                    ;
  358.   scoretable[pos].mv = scoremoves                    ;
  359.   scoretable[pos].ps = scorepushes                    ;
  360.   scoreentries++                            ;
  361.  
  362.   CleanupScoreTable()                            ;
  363.   if (scoreentries == MAXSCOREENTRIES)
  364.     return E_TOMUCHSE                            ;
  365.   else
  366.     return 0                                ;
  367. }
  368.  
  369.  
  370. /* searches the score table to find a specific player. */
  371. short FindUser(void)
  372. {
  373.   short i                                ;
  374.   Boolean found = _false_                        ;
  375.  
  376.   for (i = 0; (i < scoreentries) && (!found); i++)
  377.     found = (strcmp(scoretable[i].user, username) == 0)                ;
  378.   return ((found) ? i - 1 : -1)                            ;
  379. }
  380.  
  381. /* finds the position for a new score in the score table */
  382. short FindPos(void)
  383. {
  384.   short i                                ;
  385.   Boolean found = _false_                        ;
  386.  
  387.   for (i = 0; (i < scoreentries) && (!found); i++)
  388.     found = ((scorelevel > scoretable[i].lv) ||
  389.          ((scorelevel == scoretable[i].lv) &&
  390.           (scoremoves < scoretable[i].mv)) ||
  391.          ((scorelevel == scoretable[i].lv) &&
  392.           (scoremoves == scoretable[i].mv) &&
  393.           (scorepushes < scoretable[i].ps)))            ;
  394.   return ((found) ? i - 1 : -1)                            ;
  395. }
  396.  
  397. /*  WriteScore() writes out the score table.  It uses ntoh() and hton()
  398.     functions to make the scorefile compatible across systems. It and
  399.     LockScore() try to avoid trashing the score file, even across NFS.
  400.     However, they are not perfect.
  401.  
  402.      The vulnerability here is that if we take more than 10 seconds to
  403.      finish Score(), AND someone else decides to break the lock, AND
  404.      they pick the same temporary name, they may write on top of the
  405.      same file. Then we could scramble the score file by moving it with
  406.      alacrity to SCOREFILE before they finish their update. This is
  407.      quite unlikely, but possible.
  408.  
  409.      We could limit the damage by writing just the one score we're
  410.      adding to a temporary file *when we can't acquire the lock*. Then    ,
  411.      the next time someone comes by and gets the lock, they integrate
  412.      all the temporary files. Since the score change would be smaller
  413.      than one block, duplicate temporary file names means only that a
  414.      score change can be lost. This approach would not require a TIMEOUT.
  415.  
  416.      The problem with that scheme is that if someone dies holding the
  417.      lock, the temporary files just pile up without getting applied.
  418.      Also, user intervention is required to blow away the lock; and
  419.      blowing away the lock can get us in the same trouble that happens
  420.      here.
  421. */
  422.  
  423. char const *tempnm = SCOREFILE "XXXXXX"                        ;
  424.  
  425. short WriteScore(void)
  426. {
  427.   short ret = 0                                    ;
  428.   int tmp                                ;
  429.  
  430.   char tempfile[MAXPATHLEN]                        ;
  431.   strcpy(tempfile, tempnm)                        ;
  432.  
  433. #ifdef DEBUG
  434.   printf("TempFile :: %s\n",tempfile)                    ;
  435. #endif
  436.  
  437.  
  438.   (void)mktemp(tempfile)                        ;
  439.  
  440. #ifdef DEBUG
  441.   printf("Mktemp() :: %s\n",tempfile)                    ;
  442. #endif
  443.  
  444.   scorefile = fopen(tempfile, "w")                    ;
  445.  
  446. #ifdef DEBUG
  447.   printf("Opening tempfile :: %s status=%d\n",tempfile,scorefile)    ;
  448. #endif
  449.  
  450.   if (!scorefile) return E_FOPENSCORE                    ;
  451.   sfdbn = fileno(scorefile)                        ;
  452.  
  453.   scoreentries = htons(scoreentries)                    ;
  454.   if (fwrite(SCORE_VERSION, 4, 1, scorefile) != 1) {
  455.       ret = E_WRITESCORE                        ;
  456.   } else if (fwrite(&scoreentries, 2, 1, scorefile) != 1) {
  457.       ret = E_WRITESCORE                        ;
  458.   } else {
  459.       scoreentries = ntohs(scoreentries)                ;
  460.  
  461.                                            /*   swap around for little-endian machines   */
  462.       for (tmp = 0; tmp < scoreentries; tmp++) {
  463.     scoretable[tmp].lv = htons(scoretable[tmp].lv)            ;
  464.     scoretable[tmp].mv = htons(scoretable[tmp].mv)            ;
  465.     scoretable[tmp].ps = htons(scoretable[tmp].ps)            ;
  466.       }
  467.       tmp = scoreentries                        ;
  468.       while (tmp > 0) {
  469.     int n = fwrite(&(scoretable[scoreentries - tmp])        ,
  470.             sizeof(struct st_entry)                    ,
  471.             tmp                        ,
  472.             scorefile)                    ;
  473.     if (n <= 0 && errno) {
  474.         perror(tempfile)                        ;
  475.         ret = E_WRITESCORE                        ;
  476.         break                            ;
  477.     }
  478.     tmp -= n                            ;
  479.       }
  480.  
  481.                                            /*   and swap back for the rest of the run ...   */
  482.       for (tmp = 0; tmp < scoreentries; tmp++) {
  483.     scoretable[tmp].lv = ntohs(scoretable[tmp].lv)            ;
  484.     scoretable[tmp].mv = ntohs(scoretable[tmp].mv)            ;
  485.     scoretable[tmp].ps = ntohs(scoretable[tmp].ps)            ;
  486.       }
  487.     }
  488.  
  489. #ifdef DEBUG
  490.   printf("Now will flush score file\n")                        ;
  491. #endif
  492.  
  493.     if (EOF == fflush(scorefile)) {
  494.     ret = E_WRITESCORE                        ;
  495.     perror(tempfile)                        ;
  496.     } else
  497.     if (0 > fsync(sfdbn)) {
  498.     ret = E_WRITESCORE                        ;
  499.     perror(tempfile)                        ;
  500.     }
  501.     if (EOF == fclose(scorefile)) {
  502.     ret = E_WRITESCORE                        ;
  503.     perror(tempfile)                        ;
  504.     }
  505.     if (ret == 0) {
  506.       time_t t = time(0)                        ;
  507.       if ((t - lock_time >= TIMEOUT) && (!merging) ) {
  508.       fprintf(stderr                        ,
  509.   "Took more than %d seconds trying to write score file; lock expired.\n",
  510.           TIMEOUT)                        ;
  511.       ret = E_WRITESCORE                        ;
  512.       } else if (0 > rename(tempfile, SCOREFILE)) {
  513.       ret = E_WRITESCORE                        ;
  514.       }
  515.     }
  516.     if (ret != 0) unlink(tempfile)                    ;
  517.     return ret                                ;
  518. }
  519.  
  520.  
  521. /* displays the score table to the user. If level == 0, show all
  522.    levels. */
  523. void ShowScore(int level)
  524. {
  525.   register i                                ;
  526.  
  527.   fprintf(stdout, "Rank      User     Level     Moves    Pushes\n")    ;
  528.   fprintf(stdout, "============================================\n")    ;
  529.   for (i = 0; i < scoreentries; i++) {
  530.     if (level == 0 || scoretable[i].lv == level) {
  531.     int rank = SolnRank(i, 0)                    ;
  532.     if (rank <= MAXSOLNRANK) fprintf(stdout, "%4.4d ", rank)    ;
  533.     else fprintf(stdout, "    ")                    ;
  534.     fprintf(stdout, "%15s  %8d  %8d  %8d\n", scoretable[i].user    ,
  535.         scoretable[i].lv, scoretable[i].mv, scoretable[i].ps)    ;
  536.     }
  537.   }
  538. }
  539.  
  540. /* duplicates a score entry */
  541. void CopyEntry(short i1, short i2)
  542. {
  543.   strcpy(scoretable[i1].user, scoretable[i2].user)            ;
  544.   scoretable[i1].lv = scoretable[i2].lv                        ;
  545.   scoretable[i1].mv = scoretable[i2].mv                        ;
  546.   scoretable[i1].ps = scoretable[i2].ps                        ;
  547. }
  548.  
  549.  
  550.  
  551.  
  552.  
  553. /* Attempt to merge score file */
  554. short MergeScore(void)
  555. {
  556. char    filename[132]                            ;
  557. char    luser[MAXUSERNAME]                        ;
  558. int    offs,j                                ;
  559. unsigned short mv,ps,lv,swap                        ;
  560. struct  st_entry lscoretable[MAXSCOREENTRIES]                ;
  561.  
  562.  
  563.  printf("Enter all file names you want to merge ; CTRL/Z to terminate\n");
  564.  offs = 0                                ;
  565.  NEXT:
  566.   strcpy(filename,"\0")                                ;
  567.   printf("File name : ")                        ;
  568.   scanf("%s",filename)                            ;
  569.   if( strcmp(filename,"\0") == 0){
  570.     if(offs != 0){
  571.        printf("Transfering scores for merging ; %d entries\n",offs)    ;
  572.        for( j = 0 ; j < offs ; j++){
  573.         strcpy(scoretable[j].user,lscoretable[j].user)        ;
  574.             scoretable[j].lv   = lscoretable[j].lv            ;
  575.             scoretable[j].mv   = lscoretable[j].mv            ;
  576.             scoretable[j].ps   = lscoretable[j].ps            ;
  577.        }
  578.  
  579.        printf("Sorting now ...\n")                    ;
  580.        do{
  581.         swap = 0                            ;
  582.         for (j = 0; j < offs-1; j++) {
  583.         if (     (scoretable[j].lv < scoretable[j+1].lv)    ||
  584.             ((scoretable[j].lv == scoretable[j+1].lv)    &&
  585.              ( scoretable[j].mv+scoretable[j].ps >     \
  586.                scoretable[j+1].mv+scoretable[j+1].ps))    ){
  587.  
  588.             /* Swap elements */
  589.             strcpy(luser,scoretable[j].user)        ;
  590.             lv = scoretable[j].lv                ;
  591.             ps = scoretable[j].ps                ;
  592.             mv = scoretable[j].mv                ;
  593.             strcpy(scoretable[j].user,scoretable[j+1].user)    ;
  594.             scoretable[j].lv = scoretable[j+1].lv        ;
  595.             scoretable[j].ps = scoretable[j+1].ps        ;
  596.             scoretable[j].mv = scoretable[j+1].mv        ;
  597.             strcpy(scoretable[j+1].user,luser)        ;
  598.             scoretable[j+1].lv = lv                ;
  599.             scoretable[j+1].ps = ps                ;
  600.             scoretable[j+1].mv = mv                ;
  601.             swap = 1                    ;
  602.         }
  603.         }
  604.        } while(swap == 1)                        ;
  605.  
  606.  
  607. /* #if DEBUG */
  608. /*            for( j = 0 ; j < offs ; j++){ */
  609. /*                 printf("%s %d %d %d\n", */
  610. /*                         scoretable[j].user      , */
  611. /*                         scoretable[j].lv        , */
  612. /*                         scoretable[j].mv        , */
  613. /*                         scoretable[j].ps        ); */
  614. /*            } */
  615. /* #endif */
  616.  
  617.        merging = _true_                        ;
  618. /*        CleanupScoreTable()                        ; */
  619.        scoreentries =    offs                    ;
  620.        WriteScore()                            ;
  621.     } else printf("Nothing to merge\n")                ;
  622.     exit(0)                                    ;
  623.   } else {
  624.     if (ReadScore(filename)    == 0){
  625.       UnlockScore()                            ;
  626.      for( j = 0 ; (j < scoreentries) && (j+offs < MAXSCOREENTRIES) ; j++){
  627.         strcpy(lscoretable[j+offs].user,scoretable[j].user)    ;
  628.                 lscoretable[j+offs].lv   = scoretable[j].lv        ;
  629.                 lscoretable[j+offs].mv   = scoretable[j].mv        ;
  630.                 lscoretable[j+offs].ps   = scoretable[j].ps        ;
  631.      }
  632.      if( j+offs >= MAXSCOREENTRIES){
  633.         printf("Cannot accept more files for merging ...\n")    ;
  634.      } else {
  635.         offs += scoreentries                    ;
  636.      }
  637.     } else    printf("Could not read %s\n",filename)            ;
  638.     goto NEXT                            ;
  639.   }
  640. /*  printf("Option not implemented\n")                    ; */
  641. /*  abort; */
  642. }
  643.  
  644.  
  645.  
  646.