aboutsummaryrefslogtreecommitdiffstats
path: root/lib-src
diff options
context:
space:
mode:
authorPaul Eggert2014-05-18 21:56:03 -0700
committerPaul Eggert2014-05-18 21:56:03 -0700
commit60fc70a8e0bf25d7388fb4c2e31d912c203f561d (patch)
tree19edf3db0317cd7c5ab3c7b001214b9b7e33c061 /lib-src
parent337ee6b38ea35823ab77a03b3841cf888904b336 (diff)
downloademacs-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/ChangeLog22
-rw-r--r--lib-src/update-game-score.c315
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 @@
12014-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
12014-05-18 Paul Eggert <eggert@cs.ucla.edu> 232014-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
77struct score_entry 77struct 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)
102static _Noreturn void 101static _Noreturn void
103lose_syserr (const char *msg) 102lose_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
140static char *
141normalize_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
140int 172int
141main (int argc, char **argv) 173main (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
247static int 283static char *
248read_score (FILE *f, struct score_entry *score) 284read_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
354static int 299static int
355read_scores (const char *filename, struct score_entry **scores, 300read_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
380static int 354static 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
388static int 380static 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}