diff options
| author | Paul Eggert | 2014-05-18 21:56:03 -0700 |
|---|---|---|
| committer | Paul Eggert | 2014-05-18 21:56:03 -0700 |
| commit | 60fc70a8e0bf25d7388fb4c2e31d912c203f561d (patch) | |
| tree | 19edf3db0317cd7c5ab3c7b001214b9b7e33c061 /lib-src | |
| parent | 337ee6b38ea35823ab77a03b3841cf888904b336 (diff) | |
| download | emacs-60fc70a8e0bf25d7388fb4c2e31d912c203f561d.tar.gz emacs-60fc70a8e0bf25d7388fb4c2e31d912c203f561d.zip | |
Remove dependencies on getdelim and getline.
Also, remove update-game-scores's limits on game scores and
simplify its file-locking code.
* configure.ac (getdelim, getline): Remove.
* lib-src/update-game-score.c (struct score_entry): Unify the username and
data members to a single user_data member, since they don't need to be
changed independently and getdelim and getline aren't helpful.
Make the score member char *, not intmax_t, so that scores are not
limited to intmax_t. All uses changed.
(lose_syserr): A zero errno stands for invalid data in score file.
(normalize_integer): New function.
(main): Use it. Check for invalid scores. Omit redundant stat check.
(read_score): First arg is now a string, not a FILE *. All uses
changed. Do not use getdelim or getline; that's way simpler.
(read_scores): Read the whole file, and let read_score handle each
line.
(score_compare): Compare strings representing integers, not integers.
(write_scores) [DOS_NT]: Eliminate unnecessary chmod.
(lock_file): Simplify locking code, eliminating goto.
Check for unlink failure.
Diffstat (limited to 'lib-src')
| -rw-r--r-- | lib-src/ChangeLog | 22 | ||||
| -rw-r--r-- | lib-src/update-game-score.c | 315 |
2 files changed, 171 insertions, 166 deletions
diff --git a/lib-src/ChangeLog b/lib-src/ChangeLog index 3fac70b3f73..3ac401b16a3 100644 --- a/lib-src/ChangeLog +++ b/lib-src/ChangeLog | |||
| @@ -1,3 +1,25 @@ | |||
| 1 | 2014-05-19 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | Remove dependencies on getline and getdelim. | ||
| 4 | Also, remove update-game-scores's limits on game scores and | ||
| 5 | simplify its file-locking code. | ||
| 6 | * update-game-score.c (struct score_entry): Unify the username and | ||
| 7 | data members to a single user_data member, since they don't need to be | ||
| 8 | changed independently and getdelim and getline aren't helpful. | ||
| 9 | Make the score member char *, not intmax_t, so that scores are not | ||
| 10 | limited to intmax_t. All uses changed. | ||
| 11 | (lose_syserr): A zero errno stands for invalid data in score file. | ||
| 12 | (normalize_integer): New function. | ||
| 13 | (main): Use it. Check for invalid scores. Omit redundant stat check. | ||
| 14 | (read_score): First arg is now a string, not a FILE *. All uses | ||
| 15 | changed. Do not use getdelim or getline; that's way simpler. | ||
| 16 | (read_scores): Read the whole file, and let read_score handle each | ||
| 17 | line. | ||
| 18 | (score_compare): Compare strings representing integers, not integers. | ||
| 19 | (write_scores) [DOS_NT]: Eliminate unnecessary chmod. | ||
| 20 | (lock_file): Simplify locking code, eliminating goto. | ||
| 21 | Check for unlink failure. | ||
| 22 | |||
| 1 | 2014-05-18 Paul Eggert <eggert@cs.ucla.edu> | 23 | 2014-05-18 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 24 | ||
| 3 | Port ctags+etags build to Sun C 5.12. | 25 | Port ctags+etags build to Sun C 5.12. |
diff --git a/lib-src/update-game-score.c b/lib-src/update-game-score.c index cb6fdf73590..7a64cd04f47 100644 --- a/lib-src/update-game-score.c +++ b/lib-src/update-game-score.c | |||
| @@ -76,9 +76,8 @@ static int unlock_file (const char *filename, void *state); | |||
| 76 | 76 | ||
| 77 | struct score_entry | 77 | struct score_entry |
| 78 | { | 78 | { |
| 79 | intmax_t score; | 79 | char *score; |
| 80 | char *username; | 80 | char *user_data; |
| 81 | char *data; | ||
| 82 | }; | 81 | }; |
| 83 | 82 | ||
| 84 | #define MAX_SCORES min (PTRDIFF_MAX, SIZE_MAX / sizeof (struct score_entry)) | 83 | #define MAX_SCORES min (PTRDIFF_MAX, SIZE_MAX / sizeof (struct score_entry)) |
| @@ -102,7 +101,8 @@ lose (const char *msg) | |||
| 102 | static _Noreturn void | 101 | static _Noreturn void |
| 103 | lose_syserr (const char *msg) | 102 | lose_syserr (const char *msg) |
| 104 | { | 103 | { |
| 105 | fprintf (stderr, "%s: %s\n", msg, strerror (errno)); | 104 | fprintf (stderr, "%s: %s\n", msg, |
| 105 | errno ? strerror (errno) : "Invalid data in score file"); | ||
| 106 | exit (EXIT_FAILURE); | 106 | exit (EXIT_FAILURE); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| @@ -137,6 +137,38 @@ get_prefix (bool running_suid, const char *user_prefix) | |||
| 137 | return user_prefix; | 137 | return user_prefix; |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | static char * | ||
| 141 | normalize_integer (char *num) | ||
| 142 | { | ||
| 143 | bool neg; | ||
| 144 | char *p; | ||
| 145 | while (*num != '\n' && isspace (*num)) | ||
| 146 | num++; | ||
| 147 | neg = *num == '-'; | ||
| 148 | num += neg || *num == '-'; | ||
| 149 | |||
| 150 | if (*num == '0') | ||
| 151 | { | ||
| 152 | while (*++num == '0') | ||
| 153 | continue; | ||
| 154 | neg &= !!*num; | ||
| 155 | num -= !*num; | ||
| 156 | } | ||
| 157 | |||
| 158 | for (p = num; '0' <= *p && *p <= '9'; p++) | ||
| 159 | continue; | ||
| 160 | |||
| 161 | if (*p || p == num) | ||
| 162 | { | ||
| 163 | errno = 0; | ||
| 164 | return 0; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (neg) | ||
| 168 | *--num = '-'; | ||
| 169 | return num; | ||
| 170 | } | ||
| 171 | |||
| 140 | int | 172 | int |
| 141 | main (int argc, char **argv) | 173 | main (int argc, char **argv) |
| 142 | { | 174 | { |
| @@ -144,9 +176,8 @@ main (int argc, char **argv) | |||
| 144 | bool running_suid; | 176 | bool running_suid; |
| 145 | void *lockstate; | 177 | void *lockstate; |
| 146 | char *scorefile; | 178 | char *scorefile; |
| 147 | char *nl; | 179 | char *end, *nl, *user, *data; |
| 148 | const char *prefix, *user_prefix = NULL; | 180 | const char *prefix, *user_prefix = NULL; |
| 149 | struct stat buf; | ||
| 150 | struct score_entry *scores; | 181 | struct score_entry *scores; |
| 151 | struct score_entry newscore; | 182 | struct score_entry newscore; |
| 152 | bool reverse = false; | 183 | bool reverse = false; |
| @@ -169,8 +200,8 @@ main (int argc, char **argv) | |||
| 169 | break; | 200 | break; |
| 170 | case 'm': | 201 | case 'm': |
| 171 | { | 202 | { |
| 172 | intmax_t m = strtoimax (optarg, 0, 10); | 203 | intmax_t m = strtoimax (optarg, &end, 10); |
| 173 | if (m < 0) | 204 | if (optarg == end || *end || m < 0) |
| 174 | usage (EXIT_FAILURE); | 205 | usage (EXIT_FAILURE); |
| 175 | max_scores = min (m, MAX_SCORES); | 206 | max_scores = min (m, MAX_SCORES); |
| 176 | } | 207 | } |
| @@ -194,21 +225,26 @@ main (int argc, char **argv) | |||
| 194 | strcat (scorefile, "/"); | 225 | strcat (scorefile, "/"); |
| 195 | strcat (scorefile, argv[optind]); | 226 | strcat (scorefile, argv[optind]); |
| 196 | 227 | ||
| 197 | newscore.score = strtoimax (argv[optind + 1], 0, 10); | 228 | newscore.score = normalize_integer (argv[optind + 1]); |
| 229 | if (! newscore.score) | ||
| 230 | { | ||
| 231 | fprintf (stderr, "%s: Invalid score\n", argv[optind + 1]); | ||
| 232 | return EXIT_FAILURE; | ||
| 233 | } | ||
| 198 | 234 | ||
| 199 | newscore.data = argv[optind + 2]; | 235 | user = get_user_id (); |
| 200 | if (strlen (newscore.data) > MAX_DATA_LEN) | 236 | if (! user) |
| 201 | newscore.data[MAX_DATA_LEN] = '\0'; | 237 | lose_syserr ("Couldn't determine user id"); |
| 202 | nl = strchr (newscore.data, '\n'); | 238 | data = argv[optind + 2]; |
| 239 | if (strlen (data) > MAX_DATA_LEN) | ||
| 240 | data[MAX_DATA_LEN] = '\0'; | ||
| 241 | nl = strchr (data, '\n'); | ||
| 203 | if (nl) | 242 | if (nl) |
| 204 | *nl = '\0'; | 243 | *nl = '\0'; |
| 205 | 244 | newscore.user_data = malloc (strlen (user) + 1 + strlen (data) + 1); | |
| 206 | newscore.username = get_user_id (); | 245 | if (! newscore.user_data |
| 207 | if (! newscore.username) | 246 | || sprintf (newscore.user_data, "%s %s", user, data) < 0) |
| 208 | lose_syserr ("Couldn't determine user id"); | 247 | lose_syserr ("Memory exhausted"); |
| 209 | |||
| 210 | if (stat (scorefile, &buf) < 0) | ||
| 211 | lose_syserr ("Failed to access scores file"); | ||
| 212 | 248 | ||
| 213 | if (lock_file (scorefile, &lockstate) < 0) | 249 | if (lock_file (scorefile, &lockstate) < 0) |
| 214 | lose_syserr ("Failed to lock scores file"); | 250 | lose_syserr ("Failed to lock scores file"); |
| @@ -244,137 +280,75 @@ main (int argc, char **argv) | |||
| 244 | exit (EXIT_SUCCESS); | 280 | exit (EXIT_SUCCESS); |
| 245 | } | 281 | } |
| 246 | 282 | ||
| 247 | static int | 283 | static char * |
| 248 | read_score (FILE *f, struct score_entry *score) | 284 | read_score (char *p, struct score_entry *score) |
| 249 | { | 285 | { |
| 250 | int c; | 286 | score->score = p; |
| 251 | if ((c = getc (f)) != EOF) | 287 | p = strchr (p, ' '); |
| 252 | ungetc (c, f); | 288 | if (!p) |
| 253 | if (feof (f)) | 289 | return p; |
| 254 | return 1; | 290 | *p++ = 0; |
| 255 | for (score->score = 0; (c = getc (f)) != EOF && isdigit (c); ) | 291 | score->user_data = p; |
| 256 | { | 292 | p = strchr (p, '\n'); |
| 257 | if (INTMAX_MAX / 10 < score->score) | 293 | if (!p) |
| 258 | return -1; | 294 | return p; |
| 259 | score->score *= 10; | 295 | *p++ = 0; |
| 260 | if (INTMAX_MAX - (c - '0') < score->score) | 296 | return p; |
| 261 | return -1; | ||
| 262 | score->score += c - '0'; | ||
| 263 | } | ||
| 264 | while ((c = getc (f)) != EOF | ||
| 265 | && isspace (c)) | ||
| 266 | ; | ||
| 267 | if (c == EOF) | ||
| 268 | return -1; | ||
| 269 | ungetc (c, f); | ||
| 270 | #ifdef HAVE_GETDELIM | ||
| 271 | { | ||
| 272 | size_t count = 0; | ||
| 273 | score->username = 0; | ||
| 274 | if (getdelim (&score->username, &count, ' ', f) < 1 | ||
| 275 | || score->username == NULL) | ||
| 276 | return -1; | ||
| 277 | /* Trim the space */ | ||
| 278 | score->username[strlen (score->username)-1] = '\0'; | ||
| 279 | } | ||
| 280 | #else | ||
| 281 | { | ||
| 282 | ptrdiff_t unameread = 0; | ||
| 283 | ptrdiff_t unamelen = 30; | ||
| 284 | char *username = malloc (unamelen); | ||
| 285 | if (!username) | ||
| 286 | return -1; | ||
| 287 | |||
| 288 | while ((c = getc (f)) != EOF && c != ' ') | ||
| 289 | { | ||
| 290 | if (unameread >= unamelen - 1) | ||
| 291 | { | ||
| 292 | ptrdiff_t unamelen_max = min (PTRDIFF_MAX, SIZE_MAX); | ||
| 293 | if (unamelen <= unamelen_max / 2) | ||
| 294 | unamelen *= 2; | ||
| 295 | else if (unamelen < unamelen_max) | ||
| 296 | unamelen = unamelen_max; | ||
| 297 | else | ||
| 298 | { | ||
| 299 | errno = ENOMEM; | ||
| 300 | return -1; | ||
| 301 | } | ||
| 302 | username = realloc (username, unamelen); | ||
| 303 | if (!username) | ||
| 304 | return -1; | ||
| 305 | } | ||
| 306 | username[unameread] = c; | ||
| 307 | unameread++; | ||
| 308 | } | ||
| 309 | if (c == EOF) | ||
| 310 | return -1; | ||
| 311 | username[unameread] = '\0'; | ||
| 312 | score->username = username; | ||
| 313 | } | ||
| 314 | #endif | ||
| 315 | #ifdef HAVE_GETLINE | ||
| 316 | score->data = NULL; | ||
| 317 | errno = 0; | ||
| 318 | { | ||
| 319 | size_t len; | ||
| 320 | if (getline (&score->data, &len, f) < 0) | ||
| 321 | return -1; | ||
| 322 | score->data[strlen (score->data)-1] = '\0'; | ||
| 323 | } | ||
| 324 | #else | ||
| 325 | { | ||
| 326 | ptrdiff_t cur = 0; | ||
| 327 | ptrdiff_t len = 16; | ||
| 328 | char *buf = malloc (len); | ||
| 329 | if (!buf) | ||
| 330 | return -1; | ||
| 331 | while ((c = getc (f)) != EOF | ||
| 332 | && c != '\n') | ||
| 333 | { | ||
| 334 | if (cur >= len-1) | ||
| 335 | { | ||
| 336 | if (min (PTRDIFF_MAX, SIZE_MAX) / 2 < len) | ||
| 337 | { | ||
| 338 | errno = ENOMEM; | ||
| 339 | return -1; | ||
| 340 | } | ||
| 341 | if (!(buf = realloc (buf, len *= 2))) | ||
| 342 | return -1; | ||
| 343 | } | ||
| 344 | buf[cur] = c; | ||
| 345 | cur++; | ||
| 346 | } | ||
| 347 | score->data = buf; | ||
| 348 | score->data[cur] = '\0'; | ||
| 349 | } | ||
| 350 | #endif | ||
| 351 | return 0; | ||
| 352 | } | 297 | } |
| 353 | 298 | ||
| 354 | static int | 299 | static int |
| 355 | read_scores (const char *filename, struct score_entry **scores, | 300 | read_scores (const char *filename, struct score_entry **scores, |
| 356 | ptrdiff_t *count, ptrdiff_t *alloc) | 301 | ptrdiff_t *count, ptrdiff_t *alloc) |
| 357 | { | 302 | { |
| 358 | int readval = -1; | 303 | char *p, *filedata; |
| 359 | ptrdiff_t scorecount = 0; | 304 | ptrdiff_t filesize, nread; |
| 360 | ptrdiff_t cursize = 0; | 305 | struct stat st; |
| 361 | struct score_entry *ret = 0; | ||
| 362 | struct score_entry entry; | ||
| 363 | FILE *f = fopen (filename, "r"); | 306 | FILE *f = fopen (filename, "r"); |
| 364 | int retval = -1; | ||
| 365 | if (!f) | 307 | if (!f) |
| 366 | return -1; | 308 | return -1; |
| 367 | while ((readval = read_score (f, &entry)) == 0) | 309 | if (fstat (fileno (f), &st) != 0) |
| 368 | if (push_score (&ret, &scorecount, &cursize, &entry) < 0) | 310 | return -1; |
| 311 | if (! (0 <= st.st_size && st.st_size < min (PTRDIFF_MAX, SIZE_MAX))) | ||
| 312 | { | ||
| 313 | errno = EOVERFLOW; | ||
| 314 | return -1; | ||
| 315 | } | ||
| 316 | filesize = st.st_size; | ||
| 317 | filedata = malloc (filesize + 1); | ||
| 318 | if (! filedata) | ||
| 319 | return -1; | ||
| 320 | nread = fread (filedata, 1, filesize + 1, f); | ||
| 321 | if (filesize < nread) | ||
| 322 | { | ||
| 323 | errno = 0; | ||
| 369 | return -1; | 324 | return -1; |
| 370 | if (readval > 0 && fclose (f) == 0) | 325 | } |
| 326 | if (nread < filesize) | ||
| 327 | filesize = nread; | ||
| 328 | if (ferror (f) || fclose (f) != 0) | ||
| 329 | return -1; | ||
| 330 | filedata[filesize] = 0; | ||
| 331 | if (strlen (filedata) != filesize) | ||
| 371 | { | 332 | { |
| 372 | *count = scorecount; | 333 | errno = 0; |
| 373 | *alloc = cursize; | 334 | return -1; |
| 374 | *scores = ret; | ||
| 375 | retval = 0; | ||
| 376 | } | 335 | } |
| 377 | return retval; | 336 | |
| 337 | *scores = 0; | ||
| 338 | *count = *alloc = 0; | ||
| 339 | for (p = filedata; p < filedata + filesize; ) | ||
| 340 | { | ||
| 341 | struct score_entry entry; | ||
| 342 | p = read_score (p, &entry); | ||
| 343 | if (!p) | ||
| 344 | { | ||
| 345 | errno = 0; | ||
| 346 | return -1; | ||
| 347 | } | ||
| 348 | if (push_score (scores, count, alloc, &entry) < 0) | ||
| 349 | return -1; | ||
| 350 | } | ||
| 351 | return 0; | ||
| 378 | } | 352 | } |
| 379 | 353 | ||
| 380 | static int | 354 | static int |
| @@ -382,7 +356,25 @@ score_compare (const void *a, const void *b) | |||
| 382 | { | 356 | { |
| 383 | const struct score_entry *sa = (const struct score_entry *) a; | 357 | const struct score_entry *sa = (const struct score_entry *) a; |
| 384 | const struct score_entry *sb = (const struct score_entry *) b; | 358 | const struct score_entry *sb = (const struct score_entry *) b; |
| 385 | return (sb->score > sa->score) - (sb->score < sa->score); | 359 | char *sca = sa->score; |
| 360 | char *scb = sb->score; | ||
| 361 | size_t lena, lenb; | ||
| 362 | bool nega = *sca == '-'; | ||
| 363 | bool negb = *scb == '-'; | ||
| 364 | int diff = nega - negb; | ||
| 365 | if (diff) | ||
| 366 | return diff; | ||
| 367 | if (nega) | ||
| 368 | { | ||
| 369 | char *tmp = sca; | ||
| 370 | sca = scb + 1; | ||
| 371 | scb = tmp + 1; | ||
| 372 | } | ||
| 373 | lena = strlen (sca); | ||
| 374 | lenb = strlen (scb); | ||
| 375 | if (lena != lenb) | ||
| 376 | return lenb < lena ? -1 : 1; | ||
| 377 | return strcmp (scb, sca); | ||
| 386 | } | 378 | } |
| 387 | 379 | ||
| 388 | static int | 380 | static int |
| @@ -451,18 +443,12 @@ write_scores (const char *filename, const struct score_entry *scores, | |||
| 451 | if (! f) | 443 | if (! f) |
| 452 | return -1; | 444 | return -1; |
| 453 | for (i = 0; i < count; i++) | 445 | for (i = 0; i < count; i++) |
| 454 | if (fprintf (f, "%"PRIdMAX" %s %s\n", | 446 | if (fprintf (f, "%s %s\n", scores[i].score, scores[i].user_data) < 0) |
| 455 | scores[i].score, scores[i].username, scores[i].data) | ||
| 456 | < 0) | ||
| 457 | return -1; | 447 | return -1; |
| 458 | if (fclose (f) != 0) | 448 | if (fclose (f) != 0) |
| 459 | return -1; | 449 | return -1; |
| 460 | if (rename (tempfile, filename) != 0) | 450 | if (rename (tempfile, filename) != 0) |
| 461 | return -1; | 451 | return -1; |
| 462 | #ifdef DOS_NT | ||
| 463 | if (chmod (filename, 0644) < 0) | ||
| 464 | return -1; | ||
| 465 | #endif | ||
| 466 | return 0; | 452 | return 0; |
| 467 | } | 453 | } |
| 468 | 454 | ||
| @@ -479,30 +465,27 @@ lock_file (const char *filename, void **state) | |||
| 479 | strcpy (lockpath, filename); | 465 | strcpy (lockpath, filename); |
| 480 | strcat (lockpath, lockext); | 466 | strcat (lockpath, lockext); |
| 481 | *state = lockpath; | 467 | *state = lockpath; |
| 482 | trylock: | 468 | |
| 483 | attempts++; | 469 | while ((fd = open (lockpath, O_CREAT | O_EXCL, 0600)) < 0) |
| 484 | /* If the lock is over an hour old, delete it. */ | ||
| 485 | if (stat (lockpath, &buf) == 0 | ||
| 486 | && 60 * 60 < time (0) - buf.st_ctime) | ||
| 487 | unlink (lockpath); | ||
| 488 | fd = open (lockpath, O_CREAT | O_EXCL, 0600); | ||
| 489 | if (fd < 0) | ||
| 490 | { | 470 | { |
| 491 | if (errno == EEXIST) | 471 | if (errno != EEXIST) |
| 472 | return -1; | ||
| 473 | attempts++; | ||
| 474 | |||
| 475 | /* Break the lock if it is over an hour old, or if we've tried | ||
| 476 | more than MAX_ATTEMPTS times. We won't corrupt the file, but | ||
| 477 | we might lose some scores. */ | ||
| 478 | if (MAX_ATTEMPTS < attempts | ||
| 479 | || (stat (lockpath, &buf) == 0 && 60 * 60 < time (0) - buf.st_ctime)) | ||
| 492 | { | 480 | { |
| 493 | /* Break the lock; we won't corrupt the file, but we might | 481 | if (unlink (lockpath) != 0 && errno != ENOENT) |
| 494 | lose some scores. */ | 482 | return -1; |
| 495 | if (attempts > MAX_ATTEMPTS) | 483 | attempts = 0; |
| 496 | { | ||
| 497 | unlink (lockpath); | ||
| 498 | attempts = 0; | ||
| 499 | } | ||
| 500 | sleep ((rand () % 2)+1); | ||
| 501 | goto trylock; | ||
| 502 | } | 484 | } |
| 503 | else | 485 | |
| 504 | return -1; | 486 | sleep ((rand () & 1) + 1); |
| 505 | } | 487 | } |
| 488 | |||
| 506 | close (fd); | 489 | close (fd); |
| 507 | return 0; | 490 | return 0; |
| 508 | } | 491 | } |