]> code.delx.au - gnu-emacs/blobdiff - lib-src/update-game-score.c
*** empty log message ***
[gnu-emacs] / lib-src / update-game-score.c
index e713d732fefb63e3e975800dc1eb675339525274..188fa896bbd7e915133fc340c1d7ea67abc8efba 100644 (file)
@@ -1,11 +1,12 @@
 /* update-game-score.c --- Update a score file
 /* update-game-score.c --- Update a score file
-   Copyright (C) 2002 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+     Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
@@ -15,48 +16,79 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 /* This program is allows a game to securely and atomically update a
 
 /* This program is allows a game to securely and atomically update a
-   score file.  It should be installed setgid, owned by an appropriate
-   group like `games'.
+   score file.  It should be installed setuid, owned by an appropriate
+   user like `games'.
+
+   Alternatively, it can be compiled without HAVE_SHARED_GAME_DIR
+   defined, and in that case it will store scores in the user's home
+   directory (it should NOT be setuid).
 
    Created 2002/03/22, by Colin Walters <walters@debian.org>
 */
 
 
    Created 2002/03/22, by Colin Walters <walters@debian.org>
 */
 
-#define _GNU_SOURCE
+#include <config.h>
 
 
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #include <unistd.h>
+#endif
 #include <errno.h>
 #include <errno.h>
+#ifdef HAVE_STRING_H
 #include <string.h>
 #include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #include <stdlib.h>
+#endif
 #include <stdio.h>
 #include <time.h>
 #include <pwd.h>
 #include <ctype.h>
 #include <stdio.h>
 #include <time.h>
 #include <pwd.h>
 #include <ctype.h>
+#ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #include <fcntl.h>
+#endif
+#ifdef STDC_HEADERS
+#include <stdarg.h>
+#endif
 #include <sys/stat.h>
 #include <sys/stat.h>
-#include <config.h>
+
+/* Needed for SunOS4, for instance.  */
+extern char *optarg;
+extern int optind, opterr;
 
 #define MAX_ATTEMPTS 5
 
 #define MAX_ATTEMPTS 5
-#define SCORE_FILE_PREFIX "/var/games/emacs/"
+#define MAX_SCORES 200
+#define MAX_DATA_LEN 1024
+
+/* Declare the prototype for a general external function.  */
+#if defined (PROTOTYPES) || defined (WINDOWSNT)
+#define P_(proto) proto
+#else
+#define P_(proto) ()
+#endif
+
+#ifndef HAVE_DIFFTIME
+/* OK on POSIX (time_t is arithmetic type) modulo overflow in subtraction.  */
+#define difftime(t1, t0) (double)((t1) - (t0))
+#endif
 
 int
 
 int
-usage(int err)
+usage (err)
+     int err;
 {
 {
-  fprintf(stdout, "Usage: update-game-score [-m MAX ] [ -r ] game/scorefile SCORE DATA\n");
-  fprintf(stdout, "       update-game-score -h\n");
-  fprintf(stdout, " -h\t\tDisplay this help.\n");
-  fprintf(stdout, " -m MAX\t\tLimit the maximum number of scores to MAX.\n");
-  fprintf(stdout, " -r\t\tSort the scores in increasing order.\n");
-  exit(err);
+  fprintf (stdout, "Usage: update-game-score [-m MAX ] [ -r ] game/scorefile SCORE DATA\n");
+  fprintf (stdout, "       update-game-score -h\n");
+  fprintf (stdout, " -h\t\tDisplay this help.\n");
+  fprintf (stdout, " -m MAX\t\tLimit the maximum number of scores to MAX.\n");
+  fprintf (stdout, " -r\t\tSort the scores in increasing order.\n");
+  fprintf (stdout, " -d DIR\t\tStore scores in DIR (only if not setuid).\n");
+  exit (err);
 }
 
 }
 
-int
-lock_file(const char *filename, void **state);
-int
-unlock_file(const char *filename, void *state);
+int lock_file P_ ((const char *filename, void **state));
+int unlock_file P_ ((const char *filename, void *state));
 
 struct score_entry
 {
 
 struct score_entry
 {
@@ -65,206 +97,282 @@ struct score_entry
   char *data;
 };
 
   char *data;
 };
 
-int
-read_scores(const char *filename, struct score_entry **scores,
-           int *count);
-int
-push_score(struct score_entry **scores, int *count,
-          int newscore, char *username, char *newdata);
+int read_scores P_ ((const char *filename, struct score_entry **scores,
+                    int *count));
+int push_score P_ ((struct score_entry **scores, int *count,
+                   int newscore, char *username, char *newdata));
+void sort_scores P_ ((struct score_entry *scores, int count, int reverse));
+int write_scores P_ ((const char *filename, const struct score_entry *scores,
+                     int count));
+
+void lose P_ ((const char *msg)) NO_RETURN;
+
 void
 void
-sort_scores(struct score_entry *scores, int count, int reverse);
-int
-write_scores(const char *filename, const struct score_entry *scores,
-            int count);
+lose (msg)
+     const char *msg;
+{
+  fprintf (stderr, "%s\n", msg);
+  exit (EXIT_FAILURE);
+}
+
+void lose_syserr P_ ((const char *msg)) NO_RETURN;
+
+/* Taken from sysdep.c.  */
+#ifndef HAVE_STRERROR
+#ifndef WINDOWSNT
+char *
+strerror (errnum)
+     int errnum;
+{
+  extern char *sys_errlist[];
+  extern int sys_nerr;
+
+  if (errnum >= 0 && errnum < sys_nerr)
+    return sys_errlist[errnum];
+  return (char *) "Unknown error";
+}
+#endif /* not WINDOWSNT */
+#endif /* ! HAVE_STRERROR */
+
+void
+lose_syserr (msg)
+     const char *msg;
+{
+  fprintf (stderr, "%s: %s\n", msg, strerror (errno));
+  exit (EXIT_FAILURE);
+}
 
 char *
 
 char *
-get_user_id()
+get_user_id P_ ((void))
 {
   char *name;
 {
   char *name;
-  struct passwd *buf = getpwuid(getuid());
+  struct passwd *buf = getpwuid (getuid ());
   if (!buf)
     {
       int count = 1;
   if (!buf)
     {
       int count = 1;
-      int uid = (int) getuid();
-      while (uid /= 10)
+      int uid = (int) getuid ();
+      int tuid = uid;
+      while (tuid /= 10)
        count++;
        count++;
-      name = malloc(count+1);
-      sprintf(name, "%d", uid);
+      name = malloc (count+1);
+      if (!name)
+       return NULL;
+      sprintf (name, "%d", uid);
       return name;
     }
   return buf->pw_name;
 }
 
       return name;
     }
   return buf->pw_name;
 }
 
+char *
+get_prefix (running_suid, user_prefix)
+     int running_suid;
+     char *user_prefix;
+{
+  if (!running_suid && user_prefix == NULL)
+    lose ("Not using a shared game directory, and no prefix given.");
+  if (running_suid)
+    {
+#ifdef HAVE_SHARED_GAME_DIR
+      return HAVE_SHARED_GAME_DIR;
+#else
+      lose ("This program was compiled without HAVE_SHARED_GAME_DIR,\n and should not be suid.");
+#endif
+    }
+  return user_prefix;
+}
+
 int
 int
-main(int argc, char **argv)
+main (argc, argv)
+     int argc;
+     char **argv;
 {
 {
-  int c;
+  int c, running_suid;
   void *lockstate;
   void *lockstate;
-  char *scorefile;
+  char *user_id, *scorefile, *prefix, *user_prefix = NULL;
   struct stat buf;
   struct score_entry *scores;
   struct stat buf;
   struct score_entry *scores;
-  int newscore, scorecount, reverse = 0, max = -1;
+  int newscore, scorecount, reverse = 0, max = MAX_SCORES;
   char *newdata;
 
   char *newdata;
 
-  srand(time(0));
+  srand (time (0));
 
 
-  while ((c = getopt(argc, argv, "hrm:")) != -1)
+  while ((c = getopt (argc, argv, "hrm:d:")) != -1)
     switch (c)
       {
       case 'h':
     switch (c)
       {
       case 'h':
-       usage(0);
+       usage (EXIT_SUCCESS);
+       break;
+      case 'd':
+       user_prefix = optarg;
        break;
       case 'r':
        reverse = 1;
        break;
       case 'm':
        break;
       case 'r':
        reverse = 1;
        break;
       case 'm':
-       max = atoi(optarg);
+       max = atoi (optarg);
+       if (max > MAX_SCORES)
+         max = MAX_SCORES;
        break;
       default:
        break;
       default:
-       usage(1);
+       usage (EXIT_FAILURE);
       }
 
   if (optind+3 != argc)
       }
 
   if (optind+3 != argc)
-    usage(1);
-  scorefile = malloc(strlen(SCORE_FILE_PREFIX) + strlen(argv[optind]) + 1);
+    usage (EXIT_FAILURE);
+
+  running_suid = (getuid () != geteuid ());
+
+  prefix = get_prefix (running_suid, user_prefix);
+
+  scorefile = malloc (strlen (prefix) + strlen (argv[optind]) + 2);
   if (!scorefile)
   if (!scorefile)
-    {
-      fprintf(stderr, "Couldn't create score file name: %s\n",
-             strerror(errno));
-      goto fail;
-    }
-  strcpy(scorefile, SCORE_FILE_PREFIX);
-  strcat(scorefile, argv[optind]);
-  newscore = atoi(argv[optind+1]);
+    lose_syserr ("Couldn't allocate score file");
+
+  strcpy (scorefile, prefix);
+  strcat (scorefile, "/");
+  strcat (scorefile, argv[optind]);
+  newscore = atoi (argv[optind+1]);
   newdata = argv[optind+2];
   newdata = argv[optind+2];
-  
-  if (stat(scorefile, &buf) < 0)
-    {
-      fprintf(stderr, "Failed to access scores file \"%s\": %s\n",
-             scorefile, strerror(errno));
-      goto fail;
-    }
-  if (lock_file(scorefile, &lockstate) < 0)
-    {
-      fprintf(stderr, "Failed to lock scores file \"%s\": %s\n",
-             scorefile, strerror(errno));
-      goto fail;
-    }
-  if (read_scores(scorefile, &scores, &scorecount) < 0)
+  if (strlen (newdata) > MAX_DATA_LEN)
+    newdata[MAX_DATA_LEN] = '\0';
+
+  user_id = get_user_id ();
+  if (user_id == NULL)
+    lose_syserr ("Couldn't determine user id");
+
+  if (stat (scorefile, &buf) < 0)
+    lose_syserr ("Failed to access scores file");
+
+  if (lock_file (scorefile, &lockstate) < 0)
+    lose_syserr ("Failed to lock scores file");
+
+  if (read_scores (scorefile, &scores, &scorecount) < 0)
     {
     {
-      fprintf(stderr, "Failed to read scores file \"%s\": %s\n",
-             scorefile, strerror(errno));
-      goto fail_unlock;
+      unlock_file (scorefile, lockstate);
+      lose_syserr ("Failed to read scores file");
     }
     }
-  push_score(&scores, &scorecount, newscore, get_user_id(), newdata);
-  sort_scores(scores, scorecount, reverse);
-  if (write_scores(scorefile, scores, scorecount) < 0)
+  push_score (&scores, &scorecount, newscore, user_id, newdata);
+  /* Limit the number of scores.  If we're using reverse sorting, then
+     we should increment the beginning of the array, to skip over the
+     *smallest* scores.  Otherwise, we just decrement the number of
+     scores, since the smallest will be at the end. */
+  if (scorecount > MAX_SCORES)
+    scorecount -= (scorecount - MAX_SCORES);
+    if (reverse)
+      scores += (scorecount - MAX_SCORES);
+  sort_scores (scores, scorecount, reverse);
+  if (write_scores (scorefile, scores, scorecount) < 0)
     {
     {
-      fprintf(stderr, "Failed to write scores file \"%s\": %s\n",
-             scorefile, strerror(errno));
-      goto fail_unlock;
+      unlock_file (scorefile, lockstate);
+      lose_syserr ("Failed to write scores file");
     }
     }
-  unlock_file(scorefile, lockstate);
-  exit(0);
- fail_unlock:
-  unlock_file(scorefile, lockstate);
- fail:
-  exit(1);
+  unlock_file (scorefile, lockstate);
+  exit (EXIT_SUCCESS);
 }
 
 int
 }
 
 int
-read_score(FILE *f, struct score_entry *score)
+read_score (f, score)
+     FILE *f;
+     struct score_entry *score;
 {
   int c;
 {
   int c;
-  if (feof(f))
+  if (feof (f))
     return 1;
     return 1;
-  while ((c = getc(f)) != EOF
-        && isdigit(c))
+  while ((c = getc (f)) != EOF
+        && isdigit (c))
     {
       score->score *= 10;
       score->score += (c-48);
     }
     {
       score->score *= 10;
       score->score += (c-48);
     }
-  while ((c = getc(f)) != EOF
-        && isspace(c))
+  while ((c = getc (f)) != EOF
+        && isspace (c))
     ;
   if (c == EOF)
     return -1;
     ;
   if (c == EOF)
     return -1;
+  ungetc (c, f);
 #ifdef HAVE_GETDELIM
   {
 #ifdef HAVE_GETDELIM
   {
-    int count = 0;
-    if (getdelim(&score->username, &count, ' ', f) < 1
+    size_t count = 0;
+    if (getdelim (&score->username, &count, ' ', f) < 1
        || score->username == NULL)
       return -1;
        || score->username == NULL)
       return -1;
+    /* Trim the space */
+    score->username[strlen (score->username)-1] = '\0';
   }
 #else
   {
     int unameread = 0;
     int unamelen = 30;
   }
 #else
   {
     int unameread = 0;
     int unamelen = 30;
-    char *username;
-    
-    while ((c = getc(f)) != EOF
-          && !isspace(c))
+    char *username = malloc (unamelen);
+    if (!username)
+      return -1;
+
+    while ((c = getc (f)) != EOF
+          && !isspace (c))
       {
       {
-       if (unameread == unamelen)
-         {
-           if (!(username = realloc(username, unamelen *= 2)))
-             return -1;
-         }
+       if (unameread >= unamelen-1)
+         if (!(username = realloc (username, unamelen *= 2)))
+           return -1;
        username[unameread] = c;
        unameread++;
       }
        username[unameread] = c;
        unameread++;
       }
+    if (c == EOF)
+      return -1;
+    username[unameread] = '\0';
     score->username = username;
   }
 #endif
 #ifdef HAVE_GETLINE
   score->data = NULL;
     score->username = username;
   }
 #endif
 #ifdef HAVE_GETLINE
   score->data = NULL;
-  errno = ESUCCES;
+  errno = 0;
   {
   {
-    int len;
-    if (getline(&score->data, &len, f) < 0)
+    size_t len;
+    if (getline (&score->data, &len, f) < 0)
       return -1;
       return -1;
+    score->data[strlen (score->data)-1] = '\0';
   }
 #else
   {
     int cur = 0;
     int len = 16;
   }
 #else
   {
     int cur = 0;
     int len = 16;
-    char *buf = malloc(len);
+    char *buf = malloc (len);
     if (!buf)
       return -1;
     if (!buf)
       return -1;
-    while ((c = getc(f)) != EOF)
+    while ((c = getc (f)) != EOF
+          && c != '\n')
       {
        if (cur >= len-1)
          {
       {
        if (cur >= len-1)
          {
-           if (!(buf = realloc(buf, len *= 2)))
+           if (!(buf = realloc (buf, len *= 2)))
              return -1;
          }
        buf[cur] = c;
        cur++;
       }
     score->data = buf;
              return -1;
          }
        buf[cur] = c;
        cur++;
       }
     score->data = buf;
-    score->data[cur+1] = '\0';
+    score->data[cur] = '\0';
   }
 #endif
   }
 #endif
-  /* Trim the newline */
-  score->data[strlen(score->data)-1] = '\0';
   return 0;
 }
 
 int
   return 0;
 }
 
 int
-read_scores(const char *filename, struct score_entry **scores,
-           int *count)
+read_scores (filename, scores, count)
+     const char *filename;
+     struct score_entry **scores;
+     int *count;
 {
   int readval, scorecount, cursize;
   struct score_entry *ret;
 {
   int readval, scorecount, cursize;
   struct score_entry *ret;
-  FILE *f = fopen(filename, "r");
-  if (!f) 
+  FILE *f = fopen (filename, "r");
+  if (!f)
     return -1;
   scorecount = 0;
   cursize = 16;
     return -1;
   scorecount = 0;
   cursize = 16;
-  ret = malloc(sizeof(struct score_entry) * cursize);
-  if (!ret) 
+  ret = (struct score_entry *) malloc (sizeof (struct score_entry) * cursize);
+  if (!ret)
     return -1;
     return -1;
-  while ((readval = read_score(f, &ret[scorecount])) == 0)
+  while ((readval = read_score (f, &ret[scorecount])) == 0)
     {
       /* We encoutered an error */
       if (readval < 0)
     {
       /* We encoutered an error */
       if (readval < 0)
@@ -272,7 +380,9 @@ read_scores(const char *filename, struct score_entry **scores,
       scorecount++;
       if (scorecount >= cursize)
        {
       scorecount++;
       if (scorecount >= cursize)
        {
-         ret = realloc(ret, cursize *= 2);
+         cursize *= 2;
+         ret = (struct score_entry *)
+           realloc (ret, (sizeof (struct score_entry) * cursize));
          if (!ret)
            return -1;
        }
          if (!ret)
            return -1;
        }
@@ -283,7 +393,9 @@ read_scores(const char *filename, struct score_entry **scores,
 }
 
 int
 }
 
 int
-score_compare(const void *a, const void *b)
+score_compare (a, b)
+     const void *a;
+     const void *b;
 {
   const struct score_entry *sa = (const struct score_entry *) a;
   const struct score_entry *sb = (const struct score_entry *) b;
 {
   const struct score_entry *sa = (const struct score_entry *) a;
   const struct score_entry *sb = (const struct score_entry *) b;
@@ -291,7 +403,9 @@ score_compare(const void *a, const void *b)
 }
 
 int
 }
 
 int
-score_compare_reverse(const void *a, const void *b)
+score_compare_reverse (a, b)
+     const void *a;
+     const void *b;
 {
   const struct score_entry *sa = (const struct score_entry *) a;
   const struct score_entry *sb = (const struct score_entry *) b;
 {
   const struct score_entry *sa = (const struct score_entry *) a;
   const struct score_entry *sb = (const struct score_entry *) b;
@@ -299,11 +413,15 @@ score_compare_reverse(const void *a, const void *b)
 }
 
 int
 }
 
 int
-push_score(struct score_entry **scores, int *count,
-          int newscore, char *username, char *newdata) 
+push_score (scores, count, newscore, username, newdata)
+     struct score_entry **scores;
+     int *count; int newscore;
+     char *username;
+     char *newdata;
 {
 {
- struct score_entry *newscores = realloc(*scores,
-                                        sizeof(struct score_entry) * ((*count) + 1));
+ struct score_entry *newscores
+   = (struct score_entry *) realloc (*scores,
+                                    sizeof (struct score_entry) * ((*count) + 1));
   if (!newscores)
     return -1;
   newscores[*count].score = newscore;
   if (!newscores)
     return -1;
   newscores[*count].score = newscore;
@@ -313,129 +431,106 @@ push_score(struct score_entry **scores, int *count,
   *scores = newscores;
   return 0;
 }
   *scores = newscores;
   return 0;
 }
-  
+
 void
 void
-sort_scores(struct score_entry *scores, int count, int reverse)
+sort_scores (scores, count, reverse)
+     struct score_entry *scores;
+     int count;
+     int reverse;
 {
 {
-  qsort(scores, count, sizeof(struct score_entry),
+  qsort (scores, count, sizeof (struct score_entry),
        reverse ? score_compare_reverse : score_compare);
 }
 
 int
        reverse ? score_compare_reverse : score_compare);
 }
 
 int
-write_scores(const char *filename, const struct score_entry *scores,
-            int count)
+write_scores (filename, scores, count)
+     const char *filename;
+     const struct score_entry * scores;
+     int count;
 {
 {
-  FILE *f;  
+  FILE *f;
   int i;
   int i;
-  char *tempfile = malloc(strlen(filename) + strlen(".tempXXXXXX") + 1);
+  char *tempfile = malloc (strlen (filename) + strlen (".tempXXXXXX") + 1);
   if (!tempfile)
     return -1;
   if (!tempfile)
     return -1;
-  strcpy(tempfile, filename);
-  strcat(tempfile, ".tempXXXXXX");
-  if (mkstemp(tempfile) < 0
-      || !(f = fopen(tempfile, "w")))
+  strcpy (tempfile, filename);
+  strcat (tempfile, ".tempXXXXXX");
+#ifdef HAVE_MKSTEMP
+  if (mkstemp (tempfile) < 0
+#else
+  if (mktemp (tempfile) != tempfile
+#endif
+      || !(f = fopen (tempfile, "w")))
     return -1;
   for (i = 0; i < count; i++)
     return -1;
   for (i = 0; i < count; i++)
-    if (fprintf(f, "%ld %s %s\n", scores[i].score, scores[i].username,
+    if (fprintf (f, "%ld %s %s\n", scores[i].score, scores[i].username,
                scores[i].data) < 0)
       return -1;
                scores[i].data) < 0)
       return -1;
-  fclose(f);
-  rename(tempfile, filename);
-  return 0;
-}
-
-#if 0
-int
-lock_file(const char *filename, void **state)
-{
-  struct flock lock;
-  int *istate;
-  int fd, attempts = 0, ret = 0;
-  lock.l_type = F_WRLCK;
-  lock.l_whence = SEEK_SET;
-  lock.l_start = 0;
-  lock.l_len = 0;
-  istate = malloc(sizeof(int));
-  if (!istate)
+  fclose (f);
+  if (rename (tempfile, filename) < 0)
     return -1;
     return -1;
-  if ((fd = open(filename, O_RDWR, 0600)) < 0)
+  if (chmod (filename, 0644) < 0)
     return -1;
     return -1;
-  *istate = fd;
- trylock:
-  attempts++;
-  if ((ret = fcntl(fd, F_GETLK, &lock)) == -1)
-    {
-      if (ret == EACCES || ret == EAGAIN)
-       if (attempts > MAX_ATTEMPTS)
-         exit(1);
-       else
-         {
-           sleep((rand() % 3)+1);
-           goto trylock;
-         }
-      else
-       ret = 0 ;
-    }
-  else
-    ret = 0;
-  *state = istate;
-  return ret;
+  return 0;
 }
 
 int
 }
 
 int
-unlock_file(const char *filename, void *state)
-{
-  int fd, ret;
-  fd = *((int *) state);
-  free(state);
-  ret = close(fd);
-  return ret;
-}
-
-#else
-
-int
-lock_file(const char *filename, void **state)
+lock_file (filename, state)
+  const char *filename;
+  void **state;
 {
   int fd;
 {
   int fd;
+  struct stat buf;
   int attempts = 0;
   char *lockext = ".lockfile";
   int attempts = 0;
   char *lockext = ".lockfile";
-  char *lockpath = malloc(strlen(filename) + strlen(lockext) + 60);
+  char *lockpath = malloc (strlen (filename) + strlen (lockext) + 60);
   if (!lockpath)
     return -1;
   if (!lockpath)
     return -1;
-  strcpy(lockpath, filename);
-  strcat(lockpath, lockext);
+  strcpy (lockpath, filename);
+  strcat (lockpath, lockext);
   *state = lockpath;
  trylock:
   attempts++;
   *state = lockpath;
  trylock:
   attempts++;
-  if ((fd = open(lockpath, O_CREAT | O_EXCL, 0600)) < 0)
+  /* If the lock is over an hour old, delete it. */
+  if (stat (lockpath, &buf) == 0
+      && (difftime (buf.st_ctime, time (NULL) > 60*60)))
+    unlink (lockpath);
+  fd = open (lockpath, O_CREAT | O_EXCL, 0600);
+  if (fd < 0)
     {
       if (errno == EEXIST)
        {
          /* Break the lock; we won't corrupt the file, but we might
             lose some scores. */
          if (attempts > MAX_ATTEMPTS)
     {
       if (errno == EEXIST)
        {
          /* Break the lock; we won't corrupt the file, but we might
             lose some scores. */
          if (attempts > MAX_ATTEMPTS)
-           unlink(lockpath);
-         else
-           sleep((rand() % 2)+1);
+           {
+             unlink (lockpath);
+             attempts = 0;
+           }
+         sleep ((rand () % 2)+1);
          goto trylock;
        }
       else
        return -1;
     }
          goto trylock;
        }
       else
        return -1;
     }
-  close(fd);
+  close (fd);
   return 0;
 }
 
 int
   return 0;
 }
 
 int
-unlock_file(const char *filename, void *state)
+unlock_file (filename, state)
+  const char *filename;
+ void *state;
 {
   char *lockpath = (char *) state;
 {
   char *lockpath = (char *) state;
-  int ret = unlink(lockpath);
+  int ret = unlink (lockpath);
   int saved_errno = errno;
   int saved_errno = errno;
-  free(lockpath);
+  free (lockpath);
   errno = saved_errno;
   return ret;
 }
 
   errno = saved_errno;
   return ret;
 }
 
-#endif
+/* arch-tag: 2bf5c52e-4beb-463a-954e-c58b9c64736b
+   (do not change this comment) */
+
+/* update-game-score.c ends here */