| 1 | #!/usr/bin/python -O |
1 | #!/usr/bin/python -O |
| 2 | """These functions mainly take ebuild info (grabbed from the database and |
2 | """These functions mainly take ebuild info (grabbed from the database and |
| 3 | convert it to HTML. See the "main" function at the bottom.""" |
3 | convert it to HTML. See the "main" function at the bottom.""" |
| 4 | |
4 | |
| 5 | __revision__ = "$Revision: 1.16.2.2 $" |
5 | __revision__ = "$Revision: 1.16.2.3 $" |
| 6 | # $Source: /var/cvsroot/gentoo/src/packages/gentoo.py,v $ |
6 | # $Source: /var/cvsroot/gentoo/src/packages/gentoo.py,v $ |
| 7 | |
7 | |
| 8 | import config |
8 | import config |
| 9 | import os |
9 | import os |
| 10 | import time |
10 | import time |
| … | |
… | |
| 13 | import ebuilddb |
13 | import ebuilddb |
| 14 | import bugs |
14 | import bugs |
| 15 | import changelogs |
15 | import changelogs |
| 16 | from cgi import escape |
16 | from cgi import escape |
| 17 | from urllib import quote |
17 | from urllib import quote |
| 18 | |
18 | from portage_versions import pkgcmp, pkgsplit |
| 19 | endversion={"pre":-2,"p":0,"alpha":-4,"beta":-3,"rc":-1} |
|
|
| 20 | # as there's no reliable way to set {}.keys() order |
|
|
| 21 | # netversion_keys will be used instead of endversion.keys |
|
|
| 22 | # to have fixed search order, so that "pre" is checked |
|
|
| 23 | # before "p" |
|
|
| 24 | endversion_keys = ["pre", "p", "alpha", "beta", "rc"] |
|
|
| 25 | |
19 | |
| 26 | def is_new(db, ebuild): |
20 | def is_new(db, ebuild): |
| 27 | """Check for newness.""" |
21 | """Check for newness.""" |
| 28 | |
22 | |
| 29 | c = db.cursor() |
23 | c = db.cursor() |
| … | |
… | |
| 331 | """Compare two ebuilds""" |
325 | """Compare two ebuilds""" |
| 332 | fields_a = pkgsplit('%s-%s' % (a['name'], a['version'])) |
326 | fields_a = pkgsplit('%s-%s' % (a['name'], a['version'])) |
| 333 | fields_b = pkgsplit('%s-%s' % (b['name'], b['version'])) |
327 | fields_b = pkgsplit('%s-%s' % (b['name'], b['version'])) |
| 334 | return pkgcmp(fields_a, fields_b) |
328 | return pkgcmp(fields_a, fields_b) |
| 335 | |
329 | |
| 336 | pkgcache={} |
|
|
| 337 | |
|
|
| 338 | def pkgsplit(mypkg,silent=1): |
|
|
| 339 | try: |
|
|
| 340 | if not pkgcache[mypkg]: |
|
|
| 341 | return None |
|
|
| 342 | return pkgcache[mypkg][:] |
|
|
| 343 | except KeyError: |
|
|
| 344 | pass |
|
|
| 345 | myparts=string.split(mypkg,'-') |
|
|
| 346 | if len(myparts)<2: |
|
|
| 347 | if not silent: |
|
|
| 348 | print "!!! Name error in",mypkg+": missing a version or name part." |
|
|
| 349 | pkgcache[mypkg]=None |
|
|
| 350 | return None |
|
|
| 351 | for x in myparts: |
|
|
| 352 | if len(x)==0: |
|
|
| 353 | if not silent: |
|
|
| 354 | print "!!! Name error in",mypkg+": empty \"-\" part." |
|
|
| 355 | pkgcache[mypkg]=None |
|
|
| 356 | return None |
|
|
| 357 | #verify rev |
|
|
| 358 | revok=0 |
|
|
| 359 | myrev=myparts[-1] |
|
|
| 360 | if len(myrev) and myrev[0]=="r": |
|
|
| 361 | try: |
|
|
| 362 | int(myrev[1:]) |
|
|
| 363 | revok=1 |
|
|
| 364 | except SystemExit, e: |
|
|
| 365 | raise |
|
|
| 366 | except: |
|
|
| 367 | pass |
|
|
| 368 | if revok: |
|
|
| 369 | if ververify(myparts[-2]): |
|
|
| 370 | if len(myparts)==2: |
|
|
| 371 | pkgcache[mypkg]=None |
|
|
| 372 | return None |
|
|
| 373 | else: |
|
|
| 374 | for x in myparts[:-2]: |
|
|
| 375 | if ververify(x): |
|
|
| 376 | pkgcache[mypkg]=None |
|
|
| 377 | return None |
|
|
| 378 | #names can't have versiony looking parts |
|
|
| 379 | myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]] |
|
|
| 380 | pkgcache[mypkg]=myval |
|
|
| 381 | return myval |
|
|
| 382 | else: |
|
|
| 383 | pkgcache[mypkg]=None |
|
|
| 384 | return None |
|
|
| 385 | |
|
|
| 386 | elif ververify(myparts[-1],silent=silent): |
|
|
| 387 | if len(myparts)==1: |
|
|
| 388 | if not silent: |
|
|
| 389 | print "!!! Name error in",mypkg+": missing name part." |
|
|
| 390 | pkgcache[mypkg]=None |
|
|
| 391 | return None |
|
|
| 392 | else: |
|
|
| 393 | for x in myparts[:-1]: |
|
|
| 394 | if ververify(x): |
|
|
| 395 | if not silent: |
|
|
| 396 | print "!!! Name error in",mypkg+": multiple version parts." |
|
|
| 397 | pkgcache[mypkg]=None |
|
|
| 398 | return None |
|
|
| 399 | myval=[string.join(myparts[:-1],"-"),myparts[-1],"r0"] |
|
|
| 400 | pkgcache[mypkg]=myval[:] |
|
|
| 401 | return myval |
|
|
| 402 | else: |
|
|
| 403 | pkgcache[mypkg]=None |
|
|
| 404 | return None |
|
|
| 405 | |
|
|
| 406 | vercache={} |
|
|
| 407 | def ververify(myorigval,silent=1): |
|
|
| 408 | try: |
|
|
| 409 | return vercache[myorigval] |
|
|
| 410 | except KeyError: |
|
|
| 411 | pass |
|
|
| 412 | if len(myorigval)==0: |
|
|
| 413 | if not silent: |
|
|
| 414 | print "!!! Name error: package contains empty \"-\" part." |
|
|
| 415 | return 0 |
|
|
| 416 | myval=string.split(myorigval,'.') |
|
|
| 417 | if len(myval)==0: |
|
|
| 418 | if not silent: |
|
|
| 419 | print "!!! Name error: empty version string." |
|
|
| 420 | vercache[myorigval]=0 |
|
|
| 421 | return 0 |
|
|
| 422 | #all but the last version must be a numeric |
|
|
| 423 | for x in myval[:-1]: |
|
|
| 424 | if not len(x): |
|
|
| 425 | if not silent: |
|
|
| 426 | print "!!! Name error in",myorigval+": two decimal points in a row" |
|
|
| 427 | vercache[myorigval]=0 |
|
|
| 428 | return 0 |
|
|
| 429 | try: |
|
|
| 430 | foo=int(x) |
|
|
| 431 | except SystemExit, e: |
|
|
| 432 | raise |
|
|
| 433 | except: |
|
|
| 434 | if not silent: |
|
|
| 435 | print "!!! Name error in",myorigval+": \""+x+"\" is not a valid version component." |
|
|
| 436 | vercache[myorigval]=0 |
|
|
| 437 | return 0 |
|
|
| 438 | if not len(myval[-1]): |
|
|
| 439 | if not silent: |
|
|
| 440 | print "!!! Name error in",myorigval+": two decimal points in a row" |
|
|
| 441 | vercache[myorigval]=0 |
|
|
| 442 | return 0 |
|
|
| 443 | try: |
|
|
| 444 | foo=int(myval[-1]) |
|
|
| 445 | vercache[myorigval]=1 |
|
|
| 446 | return 1 |
|
|
| 447 | except SystemExit, e: |
|
|
| 448 | raise |
|
|
| 449 | except: |
|
|
| 450 | pass |
|
|
| 451 | #ok, our last component is not a plain number or blank, let's continue |
|
|
| 452 | if myval[-1][-1] in string.lowercase: |
|
|
| 453 | try: |
|
|
| 454 | foo=int(myval[-1][:-1]) |
|
|
| 455 | vercache[myorigval]=1 |
|
|
| 456 | return 1 |
|
|
| 457 | # 1a, 2.0b, etc. |
|
|
| 458 | except SystemExit, e: |
|
|
| 459 | raise |
|
|
| 460 | except: |
|
|
| 461 | pass |
|
|
| 462 | #ok, maybe we have a 1_alpha or 1_beta2; let's see |
|
|
| 463 | #ep="endpart" |
|
|
| 464 | ep=string.split(myval[-1],"_") |
|
|
| 465 | if len(ep)!=2: |
|
|
| 466 | if not silent: |
|
|
| 467 | print "!!! Name error in",myorigval |
|
|
| 468 | vercache[myorigval]=0 |
|
|
| 469 | return 0 |
|
|
| 470 | try: |
|
|
| 471 | foo=int(ep[0][-1]) |
|
|
| 472 | chk=ep[0] |
|
|
| 473 | except SystemExit, e: |
|
|
| 474 | raise |
|
|
| 475 | except: |
|
|
| 476 | # because it's ok last char is not numeric. example: foo-1.0.0a_pre1 |
|
|
| 477 | chk=ep[0][:-1] |
|
|
| 478 | |
|
|
| 479 | try: |
|
|
| 480 | foo=int(chk) |
|
|
| 481 | except SystemExit, e: |
|
|
| 482 | raise |
|
|
| 483 | except: |
|
|
| 484 | #this needs to be numeric or numeric+single letter, |
|
|
| 485 | #i.e. the "1" in "1_alpha" or "1a_alpha" |
|
|
| 486 | if not silent: |
|
|
| 487 | print "!!! Name error in",myorigval+": characters before _ must be numeric or numeric+single letter" |
|
|
| 488 | vercache[myorigval]=0 |
|
|
| 489 | return 0 |
|
|
| 490 | for mye in endversion_keys: |
|
|
| 491 | if ep[1][0:len(mye)]==mye: |
|
|
| 492 | if len(mye)==len(ep[1]): |
|
|
| 493 | #no trailing numeric; ok |
|
|
| 494 | vercache[myorigval]=1 |
|
|
| 495 | return 1 |
|
|
| 496 | else: |
|
|
| 497 | try: |
|
|
| 498 | foo=int(ep[1][len(mye):]) |
|
|
| 499 | vercache[myorigval]=1 |
|
|
| 500 | return 1 |
|
|
| 501 | except SystemExit, e: |
|
|
| 502 | raise |
|
|
| 503 | except: |
|
|
| 504 | #if no endversions work, *then* we return 0 |
|
|
| 505 | pass |
|
|
| 506 | if not silent: |
|
|
| 507 | print "!!! Name error in",myorigval |
|
|
| 508 | vercache[myorigval]=0 |
|
|
| 509 | return 0 |
|
|
| 510 | |
|
|
| 511 | def relparse(myver): |
|
|
| 512 | "converts last version part into three components" |
|
|
| 513 | number=0 |
|
|
| 514 | suffix=0 |
|
|
| 515 | endtype=0 |
|
|
| 516 | endnumber=0 |
|
|
| 517 | |
|
|
| 518 | mynewver=string.split(myver,"_") |
|
|
| 519 | myver=mynewver[0] |
|
|
| 520 | |
|
|
| 521 | #normal number or number with letter at end |
|
|
| 522 | divider=len(myver)-1 |
|
|
| 523 | if myver[divider:] not in "1234567890": |
|
|
| 524 | #letter at end |
|
|
| 525 | suffix=ord(myver[divider:]) |
|
|
| 526 | number=string.atof(myver[0:divider]) |
|
|
| 527 | else: |
|
|
| 528 | number=string.atof(myver) |
|
|
| 529 | |
|
|
| 530 | if len(mynewver)==2: |
|
|
| 531 | #an endversion |
|
|
| 532 | for x in endversion_keys: |
|
|
| 533 | elen=len(x) |
|
|
| 534 | if mynewver[1][:elen] == x: |
|
|
| 535 | endtype=endversion[x] |
|
|
| 536 | try: |
|
|
| 537 | endnumber=string.atof(mynewver[1][elen:]) |
|
|
| 538 | except: |
|
|
| 539 | endnumber=0 |
|
|
| 540 | break |
|
|
| 541 | return [number,suffix,endtype,endnumber] |
|
|
| 542 | |
|
|
| 543 | # vercmp: |
|
|
| 544 | # ripped from portage.py to prevent having to import |
|
|
| 545 | vcmpcache={} |
|
|
| 546 | def vercmp(val1,val2): |
|
|
| 547 | if val1==val2: |
|
|
| 548 | #quick short-circuit |
|
|
| 549 | return 0 |
|
|
| 550 | valkey=val1+" "+val2 |
|
|
| 551 | try: |
|
|
| 552 | return vcmpcache[valkey] |
|
|
| 553 | try: |
|
|
| 554 | return -vcmpcache[val2+" "+val1] |
|
|
| 555 | except KeyError: |
|
|
| 556 | pass |
|
|
| 557 | except KeyError: |
|
|
| 558 | pass |
|
|
| 559 | |
|
|
| 560 | # consider 1_p2 vc 1.1 |
|
|
| 561 | # after expansion will become (1_p2,0) vc (1,1) |
|
|
| 562 | # then 1_p2 is compared with 1 before 0 is compared with 1 |
|
|
| 563 | # to solve the bug we need to convert it to (1,0_p2) |
|
|
| 564 | # by splitting _prepart part and adding it back _after_expansion |
|
|
| 565 | val1_prepart = val2_prepart = '' |
|
|
| 566 | if val1.count('_'): |
|
|
| 567 | val1, val1_prepart = val1.split('_', 1) |
|
|
| 568 | if val2.count('_'): |
|
|
| 569 | val2, val2_prepart = val2.split('_', 1) |
|
|
| 570 | |
|
|
| 571 | # replace '-' by '.' |
|
|
| 572 | # FIXME: Is it needed? can val1/2 contain '-'? |
|
|
| 573 | val1=string.split(val1,'-') |
|
|
| 574 | if len(val1)==2: |
|
|
| 575 | val1[0]=val1[0]+"."+val1[1] |
|
|
| 576 | val2=string.split(val2,'-') |
|
|
| 577 | if len(val2)==2: |
|
|
| 578 | val2[0]=val2[0]+"."+val2[1] |
|
|
| 579 | |
|
|
| 580 | val1=string.split(val1[0],'.') |
|
|
| 581 | val2=string.split(val2[0],'.') |
|
|
| 582 | |
|
|
| 583 | #add back decimal point so that .03 does not become "3" ! |
|
|
| 584 | for x in range(1,len(val1)): |
|
|
| 585 | if val1[x][0] == '0' : |
|
|
| 586 | val1[x]='.' + val1[x] |
|
|
| 587 | for x in range(1,len(val2)): |
|
|
| 588 | if val2[x][0] == '0' : |
|
|
| 589 | val2[x]='.' + val2[x] |
|
|
| 590 | |
|
|
| 591 | # extend version numbers |
|
|
| 592 | if len(val2)<len(val1): |
|
|
| 593 | val2.extend(["0"]*(len(val1)-len(val2))) |
|
|
| 594 | elif len(val1)<len(val2): |
|
|
| 595 | val1.extend(["0"]*(len(val2)-len(val1))) |
|
|
| 596 | |
|
|
| 597 | # add back _prepart tails |
|
|
| 598 | if val1_prepart: |
|
|
| 599 | val1[-1] += '_' + val1_prepart |
|
|
| 600 | if val2_prepart: |
|
|
| 601 | val2[-1] += '_' + val2_prepart |
|
|
| 602 | #The above code will extend version numbers out so they |
|
|
| 603 | #have the same number of digits. |
|
|
| 604 | for x in range(0,len(val1)): |
|
|
| 605 | cmp1=relparse(val1[x]) |
|
|
| 606 | cmp2=relparse(val2[x]) |
|
|
| 607 | for y in range(0,4): |
|
|
| 608 | myret=cmp1[y]-cmp2[y] |
|
|
| 609 | if myret != 0: |
|
|
| 610 | vcmpcache[valkey]=myret |
|
|
| 611 | return myret |
|
|
| 612 | vcmpcache[valkey]=0 |
|
|
| 613 | return 0 |
|
|
| 614 | |
|
|
| 615 | # pkgcmp: |
|
|
| 616 | # ripped from portage.py to prevent having to import |
|
|
| 617 | def pkgcmp(pkg1,pkg2): |
|
|
| 618 | """if returnval is less than zero, then pkg2 is newer than pkg1, zero if equal and positive if older.""" |
|
|
| 619 | if pkg1[0] != pkg2[0]: |
|
|
| 620 | return None |
|
|
| 621 | mycmp=vercmp(pkg1[1],pkg2[1]) |
|
|
| 622 | if mycmp>0: |
|
|
| 623 | return 1 |
|
|
| 624 | if mycmp<0: |
|
|
| 625 | return -1 |
|
|
| 626 | r1=int(pkg1[2][1:]) |
|
|
| 627 | r2=int(pkg2[2][1:]) |
|
|
| 628 | if r1>r2: |
|
|
| 629 | return 1 |
|
|
| 630 | if r2>r1: |
|
|
| 631 | return -1 |
|
|
| 632 | return 0 |
|
|
| 633 | |
|
|
| 634 | def ebuilds_to_rss(fp, ebuilds, simple=False, subtitle=""): |
330 | def ebuilds_to_rss(fp, ebuilds, simple=False, subtitle=""): |
| 635 | """write out ebuild info to RSS file (fp)""" |
331 | """write out ebuild info to RSS file (fp)""" |
| 636 | |
332 | |
| 637 | # web link for RSS feed |
333 | # web link for RSS feed |
| 638 | if subtitle: |
334 | if subtitle: |