diff options
| author | Paul Eggert | 2014-01-19 00:50:53 -0800 |
|---|---|---|
| committer | Paul Eggert | 2014-01-19 00:50:53 -0800 |
| commit | d70efef4a8e5c12db9fe3c305aa9590a91be8467 (patch) | |
| tree | a1fc8f230c757b69fe96f8b1973cf4da7ea0376d /lib-src | |
| parent | 9a685cf6bdb1636008acaf84ec8c1b3d3e21f419 (diff) | |
| download | emacs-d70efef4a8e5c12db9fe3c305aa9590a91be8467.tar.gz emacs-d70efef4a8e5c12db9fe3c305aa9590a91be8467.zip | |
update-game-score fixes for -m and integer overflow
* update-game-score.c: Include inttypes.h, stdbool.h.
(min): New macro, if not already defined.
(MAX_SCORES, main): Limit the maximum number of scores only from
limits imposed by the underyling platform, instead of the
arbitrary value 200.
(struct score_entry, main, read_score, write_score):
Scores are now intmax_t, not long.
(get_user_id): Reject user names containing spaces or newlines,
as they would mess up the score file.
Allow uids that don't fit in 'long'.
Increase the size of the buffer, to avoid overrun in weird cases.
(get_prefix, main): Use bool for boolean.
(main): Rewrite expr to avoid possibility of signed integer
overflow. Don't allow newlines in data, as this would mess up
the score file. Check for memory allocation failure when adding
the new score, or when unlockint the file. Implement -m.
(read_score): Check for integer overflow when reading a score.
(read_score) [!HAVE_GETDELIM]: Check for integer overflow when
data gets very long. Check only for space to delimit names,
since that's what's done in the HAVE_GETDELIM case.
(read_scores): New parameter ALLOC. Change counts to ptrdiff_t.
All uses changed. Use push_score to add individual scores;
that's simpler than repeating its contents.
(score_compare_reverse): Simplify.
(push_score): New parameter SIZE. Change counts to ptrdiff_t.
All uses changed. Check for integer overflow of size calculation.
(sort_scores, write_scores): Change counts to ptrdiff_t.
(unlock_file): Preserve errno on success, so that storage
exhaustion is diagnosed correctly.
Fixes: debbugs:16428
Diffstat (limited to 'lib-src')
| -rw-r--r-- | lib-src/ChangeLog | 33 | ||||
| -rw-r--r-- | lib-src/update-game-score.c | 221 |
2 files changed, 169 insertions, 85 deletions
diff --git a/lib-src/ChangeLog b/lib-src/ChangeLog index dc4ec91c512..50273e2a60a 100644 --- a/lib-src/ChangeLog +++ b/lib-src/ChangeLog | |||
| @@ -1,3 +1,36 @@ | |||
| 1 | 2014-01-19 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | update-game-score fixes for -m and integer overflow (Bug#16428) | ||
| 4 | * update-game-score.c: Include inttypes.h, stdbool.h. | ||
| 5 | (min): New macro, if not already defined. | ||
| 6 | (MAX_SCORES, main): Limit the maximum number of scores only from | ||
| 7 | limits imposed by the underyling platform, instead of the | ||
| 8 | arbitrary value 200. | ||
| 9 | (struct score_entry, main, read_score, write_score): | ||
| 10 | Scores are now intmax_t, not long. | ||
| 11 | (get_user_id): Reject user names containing spaces or newlines, | ||
| 12 | as they would mess up the score file. | ||
| 13 | Allow uids that don't fit in 'long'. | ||
| 14 | Increase the size of the buffer, to avoid overrun in weird cases. | ||
| 15 | (get_prefix, main): Use bool for boolean. | ||
| 16 | (main): Rewrite expr to avoid possibility of signed integer | ||
| 17 | overflow. Don't allow newlines in data, as this would mess up | ||
| 18 | the score file. Check for memory allocation failure when adding | ||
| 19 | the new score, or when unlockint the file. Implement -m. | ||
| 20 | (read_score): Check for integer overflow when reading a score. | ||
| 21 | (read_score) [!HAVE_GETDELIM]: Check for integer overflow when | ||
| 22 | data gets very long. Check only for space to delimit names, | ||
| 23 | since that's what's done in the HAVE_GETDELIM case. | ||
| 24 | (read_scores): New parameter ALLOC. Change counts to ptrdiff_t. | ||
| 25 | All uses changed. Use push_score to add individual scores; | ||
| 26 | that's simpler than repeating its contents. | ||
| 27 | (score_compare_reverse): Simplify. | ||
| 28 | (push_score): New parameter SIZE. Change counts to ptrdiff_t. | ||
| 29 | All uses changed. Check for integer overflow of size calculation. | ||
| 30 | (sort_scores, write_scores): Change counts to ptrdiff_t. | ||
| 31 | (unlock_file): Preserve errno on success, so that storage | ||
| 32 | exhaustion is diagnosed correctly. | ||
| 33 | |||
| 1 | 2014-01-05 Paul Eggert <eggert@cs.ucla.edu> | 34 | 2014-01-05 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 35 | ||
| 3 | Spelling fixes. | 36 | Spelling fixes. |
diff --git a/lib-src/update-game-score.c b/lib-src/update-game-score.c index e2bf93bb1eb..916a351432d 100644 --- a/lib-src/update-game-score.c +++ b/lib-src/update-game-score.c | |||
| @@ -35,7 +35,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 35 | 35 | ||
| 36 | #include <unistd.h> | 36 | #include <unistd.h> |
| 37 | #include <errno.h> | 37 | #include <errno.h> |
| 38 | #include <inttypes.h> | ||
| 38 | #include <limits.h> | 39 | #include <limits.h> |
| 40 | #include <stdbool.h> | ||
| 39 | #include <string.h> | 41 | #include <string.h> |
| 40 | #include <stdlib.h> | 42 | #include <stdlib.h> |
| 41 | #include <stdio.h> | 43 | #include <stdio.h> |
| @@ -50,8 +52,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 50 | #include "ntlib.h" | 52 | #include "ntlib.h" |
| 51 | #endif | 53 | #endif |
| 52 | 54 | ||
| 55 | #ifndef min | ||
| 56 | # define min(a,b) ((a) < (b) ? (a) : (b)) | ||
| 57 | #endif | ||
| 58 | |||
| 53 | #define MAX_ATTEMPTS 5 | 59 | #define MAX_ATTEMPTS 5 |
| 54 | #define MAX_SCORES 200 | ||
| 55 | #define MAX_DATA_LEN 1024 | 60 | #define MAX_DATA_LEN 1024 |
| 56 | 61 | ||
| 57 | #ifndef HAVE_DIFFTIME | 62 | #ifndef HAVE_DIFFTIME |
| @@ -76,18 +81,21 @@ static int unlock_file (const char *filename, void *state); | |||
| 76 | 81 | ||
| 77 | struct score_entry | 82 | struct score_entry |
| 78 | { | 83 | { |
| 79 | long score; | 84 | intmax_t score; |
| 80 | char *username; | 85 | char *username; |
| 81 | char *data; | 86 | char *data; |
| 82 | }; | 87 | }; |
| 83 | 88 | ||
| 89 | #define MAX_SCORES min (PTRDIFF_MAX, SIZE_MAX / sizeof (struct score_entry)) | ||
| 90 | |||
| 84 | static int read_scores (const char *filename, struct score_entry **scores, | 91 | static int read_scores (const char *filename, struct score_entry **scores, |
| 85 | int *count); | 92 | ptrdiff_t *count, ptrdiff_t *alloc); |
| 86 | static int push_score (struct score_entry **scores, int *count, | 93 | static int push_score (struct score_entry **scores, ptrdiff_t *count, |
| 87 | int newscore, char *username, char *newdata); | 94 | ptrdiff_t *size, struct score_entry const *newscore); |
| 88 | static void sort_scores (struct score_entry *scores, int count, int reverse); | 95 | static void sort_scores (struct score_entry *scores, ptrdiff_t count, |
| 96 | bool reverse); | ||
| 89 | static int write_scores (const char *filename, | 97 | static int write_scores (const char *filename, |
| 90 | const struct score_entry *scores, int count); | 98 | const struct score_entry *scores, ptrdiff_t count); |
| 91 | 99 | ||
| 92 | static _Noreturn void | 100 | static _Noreturn void |
| 93 | lose (const char *msg) | 101 | lose (const char *msg) |
| @@ -107,19 +115,19 @@ static char * | |||
| 107 | get_user_id (void) | 115 | get_user_id (void) |
| 108 | { | 116 | { |
| 109 | struct passwd *buf = getpwuid (getuid ()); | 117 | struct passwd *buf = getpwuid (getuid ()); |
| 110 | if (!buf) | 118 | if (!buf || strchr (buf->pw_name, ' ') || strchr (buf->pw_name, '\n')) |
| 111 | { | 119 | { |
| 112 | long uid = getuid (); | 120 | intmax_t uid = getuid (); |
| 113 | char *name = malloc (sizeof uid * CHAR_BIT / 3 + 1); | 121 | char *name = malloc (sizeof uid * CHAR_BIT / 3 + 4); |
| 114 | if (name) | 122 | if (name) |
| 115 | sprintf (name, "%ld", uid); | 123 | sprintf (name, "%"PRIdMAX, uid); |
| 116 | return name; | 124 | return name; |
| 117 | } | 125 | } |
| 118 | return buf->pw_name; | 126 | return buf->pw_name; |
| 119 | } | 127 | } |
| 120 | 128 | ||
| 121 | static const char * | 129 | static const char * |
| 122 | get_prefix (int running_suid, const char *user_prefix) | 130 | get_prefix (bool running_suid, const char *user_prefix) |
| 123 | { | 131 | { |
| 124 | if (!running_suid && user_prefix == NULL) | 132 | if (!running_suid && user_prefix == NULL) |
| 125 | lose ("Not using a shared game directory, and no prefix given."); | 133 | lose ("Not using a shared game directory, and no prefix given."); |
| @@ -137,14 +145,18 @@ get_prefix (int running_suid, const char *user_prefix) | |||
| 137 | int | 145 | int |
| 138 | main (int argc, char **argv) | 146 | main (int argc, char **argv) |
| 139 | { | 147 | { |
| 140 | int c, running_suid; | 148 | int c; |
| 149 | bool running_suid; | ||
| 141 | void *lockstate; | 150 | void *lockstate; |
| 142 | char *user_id, *scorefile; | 151 | char *scorefile; |
| 152 | char *nl; | ||
| 143 | const char *prefix, *user_prefix = NULL; | 153 | const char *prefix, *user_prefix = NULL; |
| 144 | struct stat buf; | 154 | struct stat buf; |
| 145 | struct score_entry *scores; | 155 | struct score_entry *scores; |
| 146 | int newscore, scorecount, reverse = 0, max = MAX_SCORES; | 156 | struct score_entry newscore; |
| 147 | char *newdata; | 157 | bool reverse = false; |
| 158 | ptrdiff_t scorecount, scorealloc; | ||
| 159 | ptrdiff_t max_scores = MAX_SCORES; | ||
| 148 | 160 | ||
| 149 | srand (time (0)); | 161 | srand (time (0)); |
| 150 | 162 | ||
| @@ -161,15 +173,18 @@ main (int argc, char **argv) | |||
| 161 | reverse = 1; | 173 | reverse = 1; |
| 162 | break; | 174 | break; |
| 163 | case 'm': | 175 | case 'm': |
| 164 | max = atoi (optarg); | 176 | { |
| 165 | if (max > MAX_SCORES) | 177 | intmax_t m = strtoimax (optarg, 0, 10); |
| 166 | max = MAX_SCORES; | 178 | if (m < 0) |
| 179 | usage (EXIT_FAILURE); | ||
| 180 | max_scores = min (m, MAX_SCORES); | ||
| 181 | } | ||
| 167 | break; | 182 | break; |
| 168 | default: | 183 | default: |
| 169 | usage (EXIT_FAILURE); | 184 | usage (EXIT_FAILURE); |
| 170 | } | 185 | } |
| 171 | 186 | ||
| 172 | if (optind+3 != argc) | 187 | if (argc - optind != 3) |
| 173 | usage (EXIT_FAILURE); | 188 | usage (EXIT_FAILURE); |
| 174 | 189 | ||
| 175 | running_suid = (getuid () != geteuid ()); | 190 | running_suid = (getuid () != geteuid ()); |
| @@ -183,13 +198,18 @@ main (int argc, char **argv) | |||
| 183 | strcpy (scorefile, prefix); | 198 | strcpy (scorefile, prefix); |
| 184 | strcat (scorefile, "/"); | 199 | strcat (scorefile, "/"); |
| 185 | strcat (scorefile, argv[optind]); | 200 | strcat (scorefile, argv[optind]); |
| 186 | newscore = atoi (argv[optind+1]); | ||
| 187 | newdata = argv[optind+2]; | ||
| 188 | if (strlen (newdata) > MAX_DATA_LEN) | ||
| 189 | newdata[MAX_DATA_LEN] = '\0'; | ||
| 190 | 201 | ||
| 191 | user_id = get_user_id (); | 202 | newscore.score = strtoimax (argv[optind + 1], 0, 10); |
| 192 | if (user_id == NULL) | 203 | |
| 204 | newscore.data = argv[optind + 2]; | ||
| 205 | if (strlen (newscore.data) > MAX_DATA_LEN) | ||
| 206 | newscore.data[MAX_DATA_LEN] = '\0'; | ||
| 207 | nl = strchr (newscore.data, '\n'); | ||
| 208 | if (nl) | ||
| 209 | *nl = '\0'; | ||
| 210 | |||
| 211 | newscore.username = get_user_id (); | ||
| 212 | if (! newscore.username) | ||
| 193 | lose_syserr ("Couldn't determine user id"); | 213 | lose_syserr ("Couldn't determine user id"); |
| 194 | 214 | ||
| 195 | if (stat (scorefile, &buf) < 0) | 215 | if (stat (scorefile, &buf) < 0) |
| @@ -198,29 +218,34 @@ main (int argc, char **argv) | |||
| 198 | if (lock_file (scorefile, &lockstate) < 0) | 218 | if (lock_file (scorefile, &lockstate) < 0) |
| 199 | lose_syserr ("Failed to lock scores file"); | 219 | lose_syserr ("Failed to lock scores file"); |
| 200 | 220 | ||
| 201 | if (read_scores (scorefile, &scores, &scorecount) < 0) | 221 | if (read_scores (scorefile, &scores, &scorecount, &scorealloc) < 0) |
| 202 | { | 222 | { |
| 203 | unlock_file (scorefile, lockstate); | 223 | unlock_file (scorefile, lockstate); |
| 204 | lose_syserr ("Failed to read scores file"); | 224 | lose_syserr ("Failed to read scores file"); |
| 205 | } | 225 | } |
| 206 | push_score (&scores, &scorecount, newscore, user_id, newdata); | 226 | if (push_score (&scores, &scorecount, &scorealloc, &newscore) < 0) |
| 227 | { | ||
| 228 | unlock_file (scorefile, lockstate); | ||
| 229 | lose_syserr ("Failed to add score"); | ||
| 230 | } | ||
| 207 | sort_scores (scores, scorecount, reverse); | 231 | sort_scores (scores, scorecount, reverse); |
| 208 | /* Limit the number of scores. If we're using reverse sorting, then | 232 | /* Limit the number of scores. If we're using reverse sorting, then |
| 209 | also increment the beginning of the array, to skip over the | 233 | also increment the beginning of the array, to skip over the |
| 210 | *smallest* scores. Otherwise, just decrementing the number of | 234 | *smallest* scores. Otherwise, just decrementing the number of |
| 211 | scores suffices, since the smallest is at the end. */ | 235 | scores suffices, since the smallest is at the end. */ |
| 212 | if (scorecount > MAX_SCORES) | 236 | if (scorecount > max_scores) |
| 213 | { | 237 | { |
| 214 | if (reverse) | 238 | if (reverse) |
| 215 | scores += (scorecount - MAX_SCORES); | 239 | scores += scorecount - max_scores; |
| 216 | scorecount = MAX_SCORES; | 240 | scorecount = max_scores; |
| 217 | } | 241 | } |
| 218 | if (write_scores (scorefile, scores, scorecount) < 0) | 242 | if (write_scores (scorefile, scores, scorecount) < 0) |
| 219 | { | 243 | { |
| 220 | unlock_file (scorefile, lockstate); | 244 | unlock_file (scorefile, lockstate); |
| 221 | lose_syserr ("Failed to write scores file"); | 245 | lose_syserr ("Failed to write scores file"); |
| 222 | } | 246 | } |
| 223 | unlock_file (scorefile, lockstate); | 247 | if (unlock_file (scorefile, lockstate) < 0) |
| 248 | lose_syserr ("Failed to unlock scores file"); | ||
| 224 | exit (EXIT_SUCCESS); | 249 | exit (EXIT_SUCCESS); |
| 225 | } | 250 | } |
| 226 | 251 | ||
| @@ -234,8 +259,12 @@ read_score (FILE *f, struct score_entry *score) | |||
| 234 | return 1; | 259 | return 1; |
| 235 | for (score->score = 0; (c = getc (f)) != EOF && isdigit (c); ) | 260 | for (score->score = 0; (c = getc (f)) != EOF && isdigit (c); ) |
| 236 | { | 261 | { |
| 262 | if (INTMAX_MAX / 10 < score->score) | ||
| 263 | return -1; | ||
| 237 | score->score *= 10; | 264 | score->score *= 10; |
| 238 | score->score += (c-48); | 265 | if (INTMAX_MAX - (c - '0') < score->score) |
| 266 | return -1; | ||
| 267 | score->score += c - '0'; | ||
| 239 | } | 268 | } |
| 240 | while ((c = getc (f)) != EOF | 269 | while ((c = getc (f)) != EOF |
| 241 | && isspace (c)) | 270 | && isspace (c)) |
| @@ -254,18 +283,30 @@ read_score (FILE *f, struct score_entry *score) | |||
| 254 | } | 283 | } |
| 255 | #else | 284 | #else |
| 256 | { | 285 | { |
| 257 | int unameread = 0; | 286 | ptrdiff_t unameread = 0; |
| 258 | int unamelen = 30; | 287 | ptrdiff_t unamelen = 30; |
| 259 | char *username = malloc (unamelen); | 288 | char *username = malloc (unamelen); |
| 260 | if (!username) | 289 | if (!username) |
| 261 | return -1; | 290 | return -1; |
| 262 | 291 | ||
| 263 | while ((c = getc (f)) != EOF | 292 | while ((c = getc (f)) != EOF && c != ' ') |
| 264 | && !isspace (c)) | ||
| 265 | { | 293 | { |
| 266 | if (unameread >= unamelen-1) | 294 | if (unameread >= unamelen - 1) |
| 267 | if (!(username = realloc (username, unamelen *= 2))) | 295 | { |
| 268 | return -1; | 296 | ptrdiff_t unamelen_max = min (PTRDIFF_MAX, SIZE_MAX); |
| 297 | if (unamelen <= unamelen_max / 2) | ||
| 298 | unamelen *= 2; | ||
| 299 | else if (unamelen < unamelen_max) | ||
| 300 | unamelen = unamelen_max; | ||
| 301 | else | ||
| 302 | { | ||
| 303 | errno = ENOMEM; | ||
| 304 | return -1; | ||
| 305 | } | ||
| 306 | username = realloc (username, unamelen); | ||
| 307 | if (!username) | ||
| 308 | return -1; | ||
| 309 | } | ||
| 269 | username[unameread] = c; | 310 | username[unameread] = c; |
| 270 | unameread++; | 311 | unameread++; |
| 271 | } | 312 | } |
| @@ -286,8 +327,8 @@ read_score (FILE *f, struct score_entry *score) | |||
| 286 | } | 327 | } |
| 287 | #else | 328 | #else |
| 288 | { | 329 | { |
| 289 | int cur = 0; | 330 | ptrdiff_t cur = 0; |
| 290 | int len = 16; | 331 | ptrdiff_t len = 16; |
| 291 | char *buf = malloc (len); | 332 | char *buf = malloc (len); |
| 292 | if (!buf) | 333 | if (!buf) |
| 293 | return -1; | 334 | return -1; |
| @@ -296,6 +337,11 @@ read_score (FILE *f, struct score_entry *score) | |||
| 296 | { | 337 | { |
| 297 | if (cur >= len-1) | 338 | if (cur >= len-1) |
| 298 | { | 339 | { |
| 340 | if (min (PTRDIFF_MAX, SIZE_MAX) / 2 < len) | ||
| 341 | { | ||
| 342 | errno = ENOMEM; | ||
| 343 | return -1; | ||
| 344 | } | ||
| 299 | if (!(buf = realloc (buf, len *= 2))) | 345 | if (!(buf = realloc (buf, len *= 2))) |
| 300 | return -1; | 346 | return -1; |
| 301 | } | 347 | } |
| @@ -310,35 +356,25 @@ read_score (FILE *f, struct score_entry *score) | |||
| 310 | } | 356 | } |
| 311 | 357 | ||
| 312 | static int | 358 | static int |
| 313 | read_scores (const char *filename, struct score_entry **scores, int *count) | 359 | read_scores (const char *filename, struct score_entry **scores, |
| 360 | ptrdiff_t *count, ptrdiff_t *alloc) | ||
| 314 | { | 361 | { |
| 315 | int readval = -1, scorecount, cursize; | 362 | int readval = -1; |
| 316 | struct score_entry *ret; | 363 | ptrdiff_t scorecount = 0; |
| 364 | ptrdiff_t cursize = 0; | ||
| 365 | struct score_entry *ret = 0; | ||
| 366 | struct score_entry entry; | ||
| 317 | FILE *f = fopen (filename, "r"); | 367 | FILE *f = fopen (filename, "r"); |
| 318 | int retval = -1; | 368 | int retval = -1; |
| 319 | if (!f) | 369 | if (!f) |
| 320 | return -1; | 370 | return -1; |
| 321 | scorecount = 0; | 371 | while ((readval = read_score (f, &entry)) == 0) |
| 322 | cursize = 16; | 372 | if (push_score (&ret, &scorecount, &cursize, &entry) < 0) |
| 323 | ret = (struct score_entry *) malloc (sizeof (struct score_entry) * cursize); | 373 | return -1; |
| 324 | if (ret) | ||
| 325 | { | ||
| 326 | while ((readval = read_score (f, &ret[scorecount])) == 0) | ||
| 327 | { | ||
| 328 | scorecount++; | ||
| 329 | if (scorecount >= cursize) | ||
| 330 | { | ||
| 331 | cursize *= 2; | ||
| 332 | ret = (struct score_entry *) | ||
| 333 | realloc (ret, (sizeof (struct score_entry) * cursize)); | ||
| 334 | if (!ret) | ||
| 335 | break; | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
| 339 | if (readval > 0) | 374 | if (readval > 0) |
| 340 | { | 375 | { |
| 341 | *count = scorecount; | 376 | *count = scorecount; |
| 377 | *alloc = cursize; | ||
| 342 | *scores = ret; | 378 | *scores = ret; |
| 343 | retval = 0; | 379 | retval = 0; |
| 344 | } | 380 | } |
| @@ -357,40 +393,53 @@ score_compare (const void *a, const void *b) | |||
| 357 | static int | 393 | static int |
| 358 | score_compare_reverse (const void *a, const void *b) | 394 | score_compare_reverse (const void *a, const void *b) |
| 359 | { | 395 | { |
| 360 | const struct score_entry *sa = (const struct score_entry *) a; | 396 | return score_compare (b, a); |
| 361 | const struct score_entry *sb = (const struct score_entry *) b; | ||
| 362 | return (sa->score > sb->score) - (sa->score < sb->score); | ||
| 363 | } | 397 | } |
| 364 | 398 | ||
| 365 | int | 399 | int |
| 366 | push_score (struct score_entry **scores, int *count, int newscore, char *username, char *newdata) | 400 | push_score (struct score_entry **scores, ptrdiff_t *count, ptrdiff_t *size, |
| 401 | struct score_entry const *newscore) | ||
| 367 | { | 402 | { |
| 368 | struct score_entry *newscores | 403 | struct score_entry *newscores = *scores; |
| 369 | = (struct score_entry *) realloc (*scores, | 404 | if (*count == *size) |
| 370 | sizeof (struct score_entry) * ((*count) + 1)); | 405 | { |
| 371 | if (!newscores) | 406 | ptrdiff_t newsize = *size; |
| 372 | return -1; | 407 | if (newsize <= 0) |
| 373 | newscores[*count].score = newscore; | 408 | newsize = 1; |
| 374 | newscores[*count].username = username; | 409 | else if (newsize <= MAX_SCORES / 2) |
| 375 | newscores[*count].data = newdata; | 410 | newsize *= 2; |
| 411 | else if (newsize < MAX_SCORES) | ||
| 412 | newsize = MAX_SCORES; | ||
| 413 | else | ||
| 414 | { | ||
| 415 | errno = ENOMEM; | ||
| 416 | return -1; | ||
| 417 | } | ||
| 418 | newscores = realloc (newscores, sizeof *newscores * newsize); | ||
| 419 | if (!newscores) | ||
| 420 | return -1; | ||
| 421 | *scores = newscores; | ||
| 422 | *size = newsize; | ||
| 423 | } | ||
| 424 | newscores[*count] = *newscore; | ||
| 376 | (*count) += 1; | 425 | (*count) += 1; |
| 377 | *scores = newscores; | ||
| 378 | return 0; | 426 | return 0; |
| 379 | } | 427 | } |
| 380 | 428 | ||
| 381 | static void | 429 | static void |
| 382 | sort_scores (struct score_entry *scores, int count, int reverse) | 430 | sort_scores (struct score_entry *scores, ptrdiff_t count, bool reverse) |
| 383 | { | 431 | { |
| 384 | qsort (scores, count, sizeof (struct score_entry), | 432 | qsort (scores, count, sizeof *scores, |
| 385 | reverse ? score_compare_reverse : score_compare); | 433 | reverse ? score_compare_reverse : score_compare); |
| 386 | } | 434 | } |
| 387 | 435 | ||
| 388 | static int | 436 | static int |
| 389 | write_scores (const char *filename, const struct score_entry *scores, int count) | 437 | write_scores (const char *filename, const struct score_entry *scores, |
| 438 | ptrdiff_t count) | ||
| 390 | { | 439 | { |
| 391 | int fd; | 440 | int fd; |
| 392 | FILE *f; | 441 | FILE *f; |
| 393 | int i; | 442 | ptrdiff_t i; |
| 394 | char *tempfile = malloc (strlen (filename) + strlen (".tempXXXXXX") + 1); | 443 | char *tempfile = malloc (strlen (filename) + strlen (".tempXXXXXX") + 1); |
| 395 | if (!tempfile) | 444 | if (!tempfile) |
| 396 | return -1; | 445 | return -1; |
| @@ -403,8 +452,9 @@ write_scores (const char *filename, const struct score_entry *scores, int count) | |||
| 403 | if (! f) | 452 | if (! f) |
| 404 | return -1; | 453 | return -1; |
| 405 | for (i = 0; i < count; i++) | 454 | for (i = 0; i < count; i++) |
| 406 | if (fprintf (f, "%ld %s %s\n", scores[i].score, scores[i].username, | 455 | if (fprintf (f, "%"PRIdMAX" %s %s\n", |
| 407 | scores[i].data) < 0) | 456 | scores[i].score, scores[i].username, scores[i].data) |
| 457 | < 0) | ||
| 408 | return -1; | 458 | return -1; |
| 409 | fclose (f); | 459 | fclose (f); |
| 410 | if (rename (tempfile, filename) < 0) | 460 | if (rename (tempfile, filename) < 0) |
| @@ -459,10 +509,11 @@ static int | |||
| 459 | unlock_file (const char *filename, void *state) | 509 | unlock_file (const char *filename, void *state) |
| 460 | { | 510 | { |
| 461 | char *lockpath = (char *) state; | 511 | char *lockpath = (char *) state; |
| 462 | int ret = unlink (lockpath); | ||
| 463 | int saved_errno = errno; | 512 | int saved_errno = errno; |
| 513 | int ret = unlink (lockpath); | ||
| 514 | int unlink_errno = errno; | ||
| 464 | free (lockpath); | 515 | free (lockpath); |
| 465 | errno = saved_errno; | 516 | errno = ret < 0 ? unlink_errno : saved_errno; |
| 466 | return ret; | 517 | return ret; |
| 467 | } | 518 | } |
| 468 | 519 | ||