diff options
| author | Paul Eggert | 2016-02-21 13:25:24 -0800 |
|---|---|---|
| committer | Paul Eggert | 2016-02-21 13:27:40 -0800 |
| commit | 1f7feecaee0ed3fb79758fe60020aefb30d9ff01 (patch) | |
| tree | b0476096aabea32658be041242caa74e7a5232cd /src | |
| parent | 3e67708d7239cde24b0988d4d1288bc75585cfea (diff) | |
| download | emacs-1f7feecaee0ed3fb79758fe60020aefb30d9ff01.tar.gz emacs-1f7feecaee0ed3fb79758fe60020aefb30d9ff01.zip | |
Use Gnulib filevercmp for version comparison
* admin/merge-gnulib (GNULIB_MODULES): Add filevercmp.
* doc/lispref/strings.texi (Text Comparison):
* etc/NEWS, src/fns.c:
* test/src/fns-tests.el (fns-tests-string-version-lessp):
Rename newly-introduced function to string-version-lessp, by
analogy with strverscmp.
* lib/filevercmp.c, lib/filevercmp.h: New files, copied from gnulib.
* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
* src/fns.c: Include <filevercmp.h>.
(gather_number_from_string): Remove.
(Fstring_version_lessp): Reimplement via filevercmp.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fns.c | 129 |
1 files changed, 30 insertions, 99 deletions
| @@ -21,6 +21,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 21 | #include <config.h> | 21 | #include <config.h> |
| 22 | 22 | ||
| 23 | #include <unistd.h> | 23 | #include <unistd.h> |
| 24 | #include <filevercmp.h> | ||
| 24 | #include <intprops.h> | 25 | #include <intprops.h> |
| 25 | #include <vla.h> | 26 | #include <vla.h> |
| 26 | #include <errno.h> | 27 | #include <errno.h> |
| @@ -332,50 +333,21 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 332 | return i1 < SCHARS (string2) ? Qt : Qnil; | 333 | return i1 < SCHARS (string2) ? Qt : Qnil; |
| 333 | } | 334 | } |
| 334 | 335 | ||
| 335 | /* Return the numerical value of a consecutive run of numerical | 336 | DEFUN ("string-version-lessp", Fstring_version_lessp, |
| 336 | characters from STRING. The ISP and ISP_BYTE address pointer | 337 | Sstring_version_lessp, 2, 2, 0, |
| 337 | pointers are increased and left at the next character after the | 338 | doc: /* Return non-nil if S1 is less than S2, as version strings. |
| 338 | numerical characters. */ | 339 | |
| 339 | static size_t | 340 | This function compares version strings S1 and S2: |
| 340 | gather_number_from_string (Lisp_Object string, | 341 | 1) By prefix lexicographically. |
| 341 | ptrdiff_t *isp, ptrdiff_t *isp_byte) | 342 | 2) Then by version (similarly to version comparison of Debian's dpkg). |
| 342 | { | 343 | Leading zeros in version numbers are ignored. |
| 343 | size_t number = 0; | 344 | 3) If both prefix and version are equal, compare as ordinary strings. |
| 344 | char *s = SSDATA (string); | ||
| 345 | char *end; | ||
| 346 | |||
| 347 | errno = 0; | ||
| 348 | number = strtoumax (s + *isp_byte, &end, 10); | ||
| 349 | if (errno == ERANGE) | ||
| 350 | /* If we have an integer overflow, then we fall back on lexical | ||
| 351 | comparison. */ | ||
| 352 | return -1; | ||
| 353 | else | ||
| 354 | { | ||
| 355 | size_t diff = end - (s + *isp_byte); | ||
| 356 | (*isp) += diff; | ||
| 357 | (*isp_byte) += diff; | ||
| 358 | return number; | ||
| 359 | } | ||
| 360 | } | ||
| 361 | 345 | ||
| 362 | DEFUN ("string-numeric-lessp", Fstring_numeric_lessp, | 346 | For example, \"foo2.png\" compares less than \"foo12.png\". |
| 363 | Sstring_numeric_lessp, 2, 2, 0, | ||
| 364 | doc: /* Return non-nil if STRING1 is less than STRING2 in 'numeric' order. | ||
| 365 | Sequences of non-numerical characters are compared lexicographically, | ||
| 366 | while sequences of numerical characters are converted into numbers, | ||
| 367 | and then the numbers are compared. This means that \"foo2.png\" is | ||
| 368 | less than \"foo12.png\" according to this predicate. | ||
| 369 | Case is significant. | 347 | Case is significant. |
| 370 | Symbols are also allowed; their print names are used instead. */) | 348 | Symbols are also allowed; their print names are used instead. */) |
| 371 | (register Lisp_Object string1, Lisp_Object string2) | 349 | (Lisp_Object string1, Lisp_Object string2) |
| 372 | { | 350 | { |
| 373 | ptrdiff_t end; | ||
| 374 | ptrdiff_t i1, i1_byte, i2, i2_byte; | ||
| 375 | size_t num1, num2; | ||
| 376 | unsigned char *chp; | ||
| 377 | int chlen1, chlen2; | ||
| 378 | |||
| 379 | if (SYMBOLP (string1)) | 351 | if (SYMBOLP (string1)) |
| 380 | string1 = SYMBOL_NAME (string1); | 352 | string1 = SYMBOL_NAME (string1); |
| 381 | if (SYMBOLP (string2)) | 353 | if (SYMBOLP (string2)) |
| @@ -383,67 +355,26 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 383 | CHECK_STRING (string1); | 355 | CHECK_STRING (string1); |
| 384 | CHECK_STRING (string2); | 356 | CHECK_STRING (string2); |
| 385 | 357 | ||
| 386 | i1 = i1_byte = i2 = i2_byte = 0; | 358 | char *p1 = SSDATA (string1); |
| 359 | char *p2 = SSDATA (string2); | ||
| 360 | char *lim1 = p1 + SBYTES (string1); | ||
| 361 | char *lim2 = p2 + SBYTES (string2); | ||
| 362 | int cmp; | ||
| 387 | 363 | ||
| 388 | end = SCHARS (string1); | 364 | while ((cmp = filevercmp (p1, p2)) == 0) |
| 389 | if (end > SCHARS (string2)) | ||
| 390 | end = SCHARS (string2); | ||
| 391 | |||
| 392 | while (i1 < end) | ||
| 393 | { | 365 | { |
| 394 | /* When we find a mismatch, we must compare the | 366 | /* If the strings are identical through their first null bytes, |
| 395 | characters, not just the bytes. */ | 367 | skip past identical prefixes and try again. */ |
| 396 | int c1, c2; | 368 | ptrdiff_t size = strlen (p1) + 1; |
| 397 | 369 | p1 += size; | |
| 398 | if (STRING_MULTIBYTE (string1)) | 370 | p2 += size; |
| 399 | { | 371 | if (lim1 < p1) |
| 400 | chp = &SDATA (string1)[i1_byte]; | 372 | return lim2 < p2 ? Qnil : Qt; |
| 401 | c1 = STRING_CHAR_AND_LENGTH (chp, chlen1); | 373 | if (lim2 < p2) |
| 402 | } | 374 | return Qnil; |
| 403 | else | ||
| 404 | { | ||
| 405 | c1 = SREF (string1, i1_byte); | ||
| 406 | chlen1 = 1; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (STRING_MULTIBYTE (string2)) | ||
| 410 | { | ||
| 411 | chp = &SDATA (string1)[i2_byte]; | ||
| 412 | c2 = STRING_CHAR_AND_LENGTH (chp, chlen2); | ||
| 413 | } | ||
| 414 | else | ||
| 415 | { | ||
| 416 | c2 = SREF (string2, i2_byte); | ||
| 417 | chlen2 = 1; | ||
| 418 | } | ||
| 419 | |||
| 420 | if (c1 >= '0' && c1 <= '9' && | ||
| 421 | c2 >= '0' && c2 <= '9') | ||
| 422 | /* Both strings are numbers, so compare them. */ | ||
| 423 | { | ||
| 424 | num1 = gather_number_from_string (string1, &i1, &i1_byte); | ||
| 425 | num2 = gather_number_from_string (string2, &i2, &i2_byte); | ||
| 426 | /* If we have an integer overflow, then resort to sorting | ||
| 427 | the entire string lexicographically. */ | ||
| 428 | if (num1 == -1 || num2 == -1) | ||
| 429 | return Fstring_lessp (string1, string2); | ||
| 430 | else if (num1 < num2) | ||
| 431 | return Qt; | ||
| 432 | else if (num1 > num2) | ||
| 433 | return Qnil; | ||
| 434 | } | ||
| 435 | else | ||
| 436 | { | ||
| 437 | if (c1 != c2) | ||
| 438 | return c1 < c2 ? Qt : Qnil; | ||
| 439 | |||
| 440 | i1++; | ||
| 441 | i2++; | ||
| 442 | i1_byte += chlen1; | ||
| 443 | i2_byte += chlen2; | ||
| 444 | } | ||
| 445 | } | 375 | } |
| 446 | return i1 < SCHARS (string2) ? Qt : Qnil; | 376 | |
| 377 | return cmp < 0 ? Qt : Qnil; | ||
| 447 | } | 378 | } |
| 448 | 379 | ||
| 449 | DEFUN ("string-collate-lessp", Fstring_collate_lessp, Sstring_collate_lessp, 2, 4, 0, | 380 | DEFUN ("string-collate-lessp", Fstring_collate_lessp, Sstring_collate_lessp, 2, 4, 0, |
| @@ -5164,7 +5095,7 @@ this variable. */); | |||
| 5164 | defsubr (&Sstring_equal); | 5095 | defsubr (&Sstring_equal); |
| 5165 | defsubr (&Scompare_strings); | 5096 | defsubr (&Scompare_strings); |
| 5166 | defsubr (&Sstring_lessp); | 5097 | defsubr (&Sstring_lessp); |
| 5167 | defsubr (&Sstring_numeric_lessp); | 5098 | defsubr (&Sstring_version_lessp); |
| 5168 | defsubr (&Sstring_collate_lessp); | 5099 | defsubr (&Sstring_collate_lessp); |
| 5169 | defsubr (&Sstring_collate_equalp); | 5100 | defsubr (&Sstring_collate_equalp); |
| 5170 | defsubr (&Sappend); | 5101 | defsubr (&Sappend); |