Changeset 8021 in project


Ignore:
Timestamp:
02/01/08 05:03:19 (12 years ago)
Author:
Kon Lovett
Message:

Save.

Location:
release/3/source-xref/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • release/3/source-xref/trunk/source-xref-single.scm

    r6436 r8021  
    22;;;; Kon Lovett, Aug '07
    33
    4 (use srfi-1 utils)
     4(use srfi-1 srfi-69 utils)
    55
    66(eval-when (compile)
     
    99    (inline)
    1010    (import
    11       ##compiler#export-list
     11      #;##compiler#export-list
    1212      ##compiler#block-globals
     13      user-preprocessor-pass
     14      user-pass
    1315      user-post-analysis-pass
    1416      ##sys#hash-table-for-each)
    1517    (always-bound
    16       ##compiler#export-list
     18      #;##compiler#export-list
    1719      ##compiler#block-globals)
    1820    (bound-to-procedure
     21      user-preprocessor-pass
     22      user-pass
    1923      user-post-analysis-pass
    2024      ##sys#hash-table-for-each) ) )
     
    2630       #t ) )
    2731
    28 ;;;
     32(define (load-sexpr-file pn #!optional (def '()))
     33  (if (file-exists? pn)
     34      (read-file pn)
     35      def ) )
     36
     37(define (arguments-for-each proc args)
     38  (let loop ([args args])
     39    (when (pair? args)
     40      (proc (car args))
     41      (loop (cdr args)) ) ) )
     42
     43;;; Syntax forms
     44
     45(define XREF-LIBRARY-FORMS-PATHNAME (make-pathname (chicken-home) "xref-library-forms"))
     46
     47(define *library-forms* (load-sexpr-file XREF-LIBRARY-FORMS-PATHNAME))
     48
     49(define (library-form? sym)
     50  (->boolean (memq sym *library-forms*)) )
     51
     52(define (known-form? sym)
     53  (library-form? sym) )
     54
     55(define (syntax-define-expression-symbol name args)
     56  (let ([getsym
     57          (lambda ()
     58            (let ([dcl (and (pair? args) (car args))])
     59              (if (pair? dcl)
     60                  (car dcl)
     61                  dcl ) ) ) ] )
     62    (case name
     63      [(define-macro)   (getsym)]
     64      [(define-syntax)  (getsym)]
     65      [else             #f] ) ) )
     66
     67(define *frm-sym-tab* (make-hash-table eq? hash-by-identity))
     68
     69;Cannot handle 'identifier' macros
     70
     71(define (store-form-names expr)
     72  (when (pair? expr)
     73    (let ([name (car expr)]
     74          [args (cdr expr)])
     75      (let ([macsym (syntax-define-expression-symbol name args)])
     76        (if macsym
     77            (hash-table-update! *frm-sym-tab* macsym
     78                                (lambda (lst)
     79                                  (set-car! lst #t)
     80                                  lst )
     81                                (lambda () (list #t #f)))
     82            (unless (known-form? name)
     83              (hash-table-update! *frm-sym-tab* name
     84                                  (lambda (lst)
     85                                    (set-car! (cdr lst) #t)
     86                                    lst )
     87                                  (lambda () (list #f #t))) ) )
     88        (arguments-for-each store-form-names args) ) ) ) )
     89
     90(define (remove-form-names expr)
     91  (when (pair? expr)
     92    (let ([name (car expr)]
     93          [args (cdr expr)])
     94      (hash-table-delete! *frm-sym-tab* name)
     95      (arguments-for-each remove-form-names args) ) ) )
     96
     97(define (dump-macro-names)
     98  (write  (hash-table-fold *frm-sym-tab* alist-cons '()))
     99  (newline) )
     100
     101;;; Analysis database ignore set
     102
     103(define XREF-LIBRARY-BINDINGS-PATHNAME (make-pathname (chicken-home) "xref-library-bindings"))
     104
     105(define *library-bindings* (load-sexpr-file XREF-LIBRARY-BINDINGS-PATHNAME))
    29106
    30107(define XREF-OTHER-BINDINGS-PATHNAME (make-pathname (chicken-home) "xref-other-bindings"))
    31108
    32 (define *other-bindings*
    33         (and (file-exists? XREF-OTHER-BINDINGS-PATHNAME)
    34              (read-file XREF-OTHER-BINDINGS-PATHNAME) ) )
     109(define *other-bindings* (load-sexpr-file XREF-OTHER-BINDINGS-PATHNAME))
    35110
    36 ;;;
     111;;; Analysis database item test
    37112
    38113(define (other-binding? k v)
    39   (->boolean (and *other-bindings* (memq v *other-bindings*))) )
     114  (->boolean (memq v *other-bindings*)) )
    40115
    41116(define (library-binding? k v)
    42   #f )
     117  (->boolean (memq v *library-bindings*)) )
    43118
    44119(define (standard-binding? k v)
     
    54129      (other-binding? k v) ) )
    55130
    56 ;;;
    57 
    58131(define (global-binding? k v)
    59132  (alist-ref 'global v eq?) )
    60133
    61 ;;;
     134;;; Analysis database report
    62135
    63136(define (get-tags k v)
     
    77150    (let ([tags (get-tags k v)])
    78151      (unless (null? tags)
    79         (write `(,k ,@tags)) (newline) ) ) ) )
     152        (write `(,k ,@tags))
     153        (newline) ) ) ) )
    80154
    81 ;;; Hook
     155;;; Hooks
     156
     157#;
     158(user-preprocessor-pass
     159  (lambda (expr)
     160    (store-form-names expr)
     161    expr ) )
     162
     163#;
     164(user-pass
     165  (lambda (expr)
     166    (remove-form-names expr)
     167    expr ) )
    82168
    83169(user-post-analysis-pass
    84170  (lambda (pass db prg get set no contf)
    85171    (unless contf
     172      (dump-macro-names)
    86173      (##sys#hash-table-for-each
    87174        (lambda (k v)
  • release/3/source-xref/trunk/source-xref.scm

    r6458 r8021  
    66(use srfi-37 tool)
    77(use sqlite3 sql-null)
    8 (use stack lookup-table)
    9 (use miscmacros misc-extn-dsssl tabular-list)
     8(use stack lookup-table miscmacros misc-extn-dsssl)
     9(use #;fmt tabular-list)
    1010(use utf8 utf8-srfi-13)
     11
    1112(import utf8)
    1213(import utf8-srfi-13)
     
    3940        (current-directory (stack-pop! *directory-stack*)) ) ) ) )
    4041
     42;;; SQLite3 Aux
     43
     44;; Return one & only one result from query or #f.
     45
     46(define (sqlite3:atomic-result stmt . params)
     47  (apply sqlite3:bind-parameters! stmt params)
     48  (and (sqlite3:step! stmt)
     49       (let* ([val (sqlite3:column-data stmt 0)]
     50              [more? (sqlite3:step! stmt)])
     51         (sqlite3:reset! stmt)
     52         (and (not more?)
     53              val ) ) ) )
     54
     55;; Works for <string> (SQL source) & <statement>
     56
     57(define (sqlite3:exec/transaction db #!rest queries #!key (lock 'exclusive))
     58  (sqlite3:with-transaction
     59   db
     60   (lambda ()
     61     (for-each (cut sqlite3:exec db <>)
     62               (fixup-extended-lambda-list-rest '(#:lock) queries))
     63     #t )
     64   lock) )
     65
     66;;; Aux
     67
     68(define (display-error-message msg args #!optional (port (current-error-port)))
     69  (display "Error: " port)
     70  (display msg port)
     71  (unless (null? args)
     72    (display ": " port)
     73    (for-each (cut display <> port) (intersperse args ", ")) )
     74  (newline port) )
     75
    4176;;; SQLite3 Prepared Statement Table
    4277
    43 ;;
     78;; Table Operations
    4479
    4580(define %sqlite3:statements-dict
     
    6196  (dict-delete! (%sqlite3:statements-dict db) tag) )
    6297
    63 ;;
     98(define (%sqlite3:statement-tags db)
     99  (dict-keys (%sqlite3:statements-dict db)) )
     100
     101;; Statement Operations
    64102
    65103(define (sqlite3:statement db tag)
     
    67105
    68106(define (sqlite3:prepare-statement db tag sql)
     107(newline)
     108(print tag)
     109(print sql)
     110(flush-output)
    69111  (%sqlite3:statement-set! db tag (sqlite3:prepare db sql)) )
    70112
    71 
    72 #; ;UNUSED
    73113(define (sqlite3:drop-statement db tag)
    74114  (and-let* ([stmt (sqlite3:statement db tag)])
     
    76116    (%sqlite3:statement-delete! db tag) ) )
    77117
    78 ;;
    79 
    80 (define (sqlite3:prepare-statements db . lst)
    81   (for-each (lambda (stmspc)
    82               (sqlite3:prepare-statement db (car stmspc) (cadr stmspc)) )
    83             lst) )
    84 
    85 (define (sqlite3:drop-statements db)
    86   (let ([tags '()])
    87     (dict-for-each (%sqlite3:statements-dict db)
    88                    (lambda (k v)
    89                      (set! tags (cons k tags))
    90                      (sqlite3:finalize! v)))
    91     (for-each (cute %sqlite3:statement-delete! db <>) tags) ) )
    92 
    93 ;;
    94 
    95 (define (sqlite3:with-transaction/statements db #!rest stmts #!key (lock 'exclusive))
    96   (sqlite3:with-transaction
    97    db
    98    (lambda ()
    99      (for-each
    100       (cute sqlite3:exec db <>)
    101       (fixup-extended-lambda-list-rest '(#:lock) stmts))
    102      #t )
    103    lock) )
    104 
    105 ;;
    106 
    107 ;;; SQLite3 Database Open & Close
    108 
    109 (define (sqlite3:initialize-db dbpn #!optional sql-initializers sql-statements)
     118;; Statements Operations
     119
     120(define (sqlite3:prepare-statements db tags queries)
     121(newline)
     122(pp tags)
     123(pp queries)
     124  (for-each (cut sqlite3:prepare-statement db <> <>) tags queries) )
     125
     126(define (sqlite3:drop-statements db #!optional (tags (%sqlite3:statement-tags db)))
     127  (for-each (cut sqlite3:drop-statement db <>) tags) )
     128
     129;; Statement Specifications Operations
     130;; alst is (tag sql) ... - note proper list
     131
     132(define (sqlite3:prepare-tagged-queries db . alst)
     133  (let-values ([(tags queries) (unzip2 alst)])
     134    (sqlite3:prepare-statements db tags queries) ) )
     135
     136;; Database Open & Close
     137
     138(define (sqlite3:initialize-db dbpn #!optional initial-queries tagged-queries)
    110139  (let ([db (sqlite3:open dbpn)])
    111     (when (list? sql-initializers)
    112       (apply sqlite3:with-transaction/statements db #:lock 'exclusive sql-initializers) )
    113     (when (list? sql-statements)
    114       (apply sqlite3:prepare-statements db sql-statements) )
     140    (when initial-queries
     141      #;(check-list 'sqlite3:initialize-db initial-queries)
     142      (apply sqlite3:exec/transaction db #:lock 'exclusive initial-queries) )
     143    (when tagged-queries
     144      #;(check-alist 'sqlite3:initialize-db tagged-queries)
     145      (apply sqlite3:prepare-tagged-queries db tagged-queries) )
    115146    db ) )
    116147
     
    119150  (sqlite3:finalize! db) )
    120151
    121 ;;; SQLite3 Database Miscellaneous
    122 
    123 (define (sqlite3:atomic-result stmt . args)
    124   (let ([lst (apply sqlite3:map-row identity stmt args)])
    125     (and (not (null? lst))
    126          (car lst) ) ) )
    127 
    128 ;;; Xref Item Accessors
    129 
    130 (define (xref-symbol xr)
    131   (car xr) )
    132 
    133 (define (xref-usage-list xr)
    134   (cdr xr) )
    135 
    136 (define (xref-usage-how pr)
    137   (car pr) )
    138 
    139 (define (xref-usage-what pr)
    140   (cadr pr) )
    141 
    142 ;;; SQLite3 Templates
     152;;; SQLite3 SQL Templates
     153
     154;; mak- CREATE TABLE/...
     155;; sel- SELECT ...
     156;; ins- INSERT ...
     157;; rpl- INSERT OR REPLACE ...
     158;; del- DELETE ...
     159;; upd- UPDATE ...
    143160
    144161; Note - sqlite3 extn doesn't expose the full sqlite3_exec functionality,
     
    202219)
    203220
    204 (define *ins-assign-entry-sql*
     221(define *rpl-assign-entry-sql*
    205222#<<EOS
    206223INSERT OR REPLACE INTO assign (source_id, symbol_id, what)
     
    209226)
    210227
    211 (define *ins-reference-entry-sql*
     228(define *rpl-reference-entry-sql*
    212229#<<EOS
    213230INSERT OR REPLACE INTO reference (source_id, symbol_id, what)
     
    277294
    278295(define *assigned-xref-widths*)
    279 (define *assigned-xref-formatting* '("~A" "~A"))
     296#;(define *assigned-xref-formatting* '("~A" "~A"))
    280297
    281298(define *referenced-xref-widths*)
    282 (define *referenced-xref-formatting* '("~A" "~A" "~A"))
    283 
    284 (define DEFAULT-XREF-DB-PATHNAME (make-pathname (repository-path) "sourcexref" "db"))
     299#;(define *referenced-xref-formatting* '("~A" "~A" "~A"))
     300
     301;;
     302
     303(define DEFAULT-XREF-DB-FILE "sourcexref")
     304(define DEFAULT-XREF-DB-PATHNAME (make-pathname (repository-path) DEFAULT-XREF-DB-FILE "db"))
     305
     306;;
    285307
    286308(define *xref-db* #f)
     309
     310(define-macro (xref-statement ?tag)
     311  `(sqlite3:statement *xref-db* ',?tag) )
     312
     313;;
    287314
    288315(define *xref-tables-sql*
     
    303330  `((ins-source-entry       ,*ins-source-entry-sql*)
    304331    (ins-symbol-entry       ,*ins-symbol-entry-sql*)
    305     (ins-assign-entry       ,*ins-assign-entry-sql*)
    306     (ins-reference-entry    ,*ins-reference-entry-sql*)
     332    (ins-assign-entry       ,*rpl-assign-entry-sql*)
     333    (ins-reference-entry    ,*rpl-reference-entry-sql*)
    307334    (sel-source-oid-entry   ,*sel-source-oid-entry-sql*)
    308335    (sel-symbol-oid-entry   ,*sel-symbol-oid-entry-sql*) ) )
    309336
    310 (define-macro (xref-statement ?tag)
    311   `(sqlite3:statement *xref-db* ',?tag) )
    312 
    313337;;; Tool Globals
    314338
    315339;; Report Mode
    316340
     341(define *update-mode* #f)
    317342(define *report-mode* #f)
     343(define *delete-mode* #f)
     344(define *delete-all-mode* #f)
     345
     346(define-flag "update" "Update" *update-mode*)
    318347
    319348(define-flag "report" "Report" *report-mode*)
     349
     350(define-flag "delete" "Delete" *delete-mode*)
     351
     352(define-flag "delete-all" "Delete All" *delete-all-mode*)
    320353
    321354;; Compiler Options
     
    357390EOS
    358391)
     392
     393;;; Xref Item Accessors
     394
     395(define (xref-symbol xr)
     396  (car xr) )
     397
     398(define (xref-usage-list xr)
     399  (cdr xr) )
     400
     401(define (xref-usage-how pr)
     402  (car pr) )
     403
     404(define (xref-usage-what pr)
     405  (cadr pr) )
    359406
    360407;;; Internal Procedures
     
    375422        (if (and enorm (zero? ecode))
    376423            lst
    377             (begin
    378               (display "Chicken Compiler Error Exit: " (current-error-port))
    379               (display ecode (current-error-port))
    380               (newline (current-error-port))
    381               (newline (current-error-port))
    382               (display errs (current-error-port))
    383               (newline (current-error-port))
     424            (let ([port (current-error-port)])
     425              (display "Chicken Compiler Error: " port) (display ecode port) (newline port)
     426              (display errs port) (newline port)
    384427              #f ) ) ) ) ) )
    385428
     
    398441
    399442(define (close-xref)
    400   (sqlite3:shutdown-db *xref-db*) )
     443  (when *xref-db*
     444    (sqlite3:shutdown-db *xref-db*)) )
     445
     446;;
     447
     448(define (error-exit loc msg . args)
     449  (close-xref)
     450  (display-error-message msg args)
     451  (tool-exit 1) )
    401452
    402453;; Atomic Results
     
    435486  (print "  ----------") )
    436487
    437 #;
    438488(define (display-spaces cnt)
    439489  (do ([cnt cnt (sub1 cnt)])
     
    441491    (display #\space) ) )
    442492
    443 #;
    444493(define (print-columns . cols)
    445494  (display #\space)
     
    457506                              (values 'left #f 1st (cdr cols))]
    458507                            [else
    459                               (error 'print-columns "invalid field specification" 1st)])])
     508                              (error-exit 'print-columns "invalid field specification" 1st)])])
    460509          (display #\space)
    461510          (when (eq? 'left just) (display str) )
     
    467516;; Report Body
    468517
    469 #;
    470518(define (report-assigned-xref source-oid)
    471519  (let ([1st #t])
     
    475523          (print-define-header)
    476524          (set! 1st #f) )
    477         (print-columns *maximum-symbol-length* sym knd) )
     525        (print-columns *assigned-xref-widths* sym knd) )
    478526      (xref-statement sel-source-assign-entries)
    479527      source-oid) ) )
    480528
    481529#;
     530(define (report-assigned-xref source-oid)
     531  (print-define-header)
     532  (sqlite3:for-each-row
     533   (lambda (sym knd)
     534     (fmt #t
     535      (columnar (with-width *assigned-xref-widths* (dsp sym)) (dsp knd)) nl) )
     536   (xref-statement sel-source-assign-entries)
     537   source-oid) )
     538
    482539(define (report-referenced-xref source-oid)
    483540  (let ([1st #t])
     
    487544          (print-reference-header)
    488545          (set! 1st #f) )
    489         (print-columns *maximum-symbol-length* sym usd)
     546        (print-columns *referenced-xref-widths* sym usd)
    490547        (unless (sql-null? dir)
    491548          (display "    in ") (display-file-name dir fil ext) (newline) ) )
     
    493550      source-oid) ) )
    494551
    495 (define (report-assigned-xref source-oid)
    496   (let ([1st #t])
    497     (sqlite3:for-each-row
    498       (lambda (sym knd)
    499          )
    500       (xref-statement sel-source-assign-entries)
    501       source-oid) ) )
     552#;
     553(define (report-referenced-xref source-oid)
     554  (print-reference-header)
     555  (sqlite3:for-each-row
     556   (lambda (sym usd dir fil ext)
     557     (fmt #t
     558      (columnar (with-width *referenced-xref-widths* (dsp sym)) (dsp usd)) nl)
     559     (unless (sql-null? dir)
     560       (display "    in ") (display-file-name dir fil ext) (newline) ) )
     561   (xref-statement sel-source-reference-assigned-entries)
     562   source-oid) )
    502563
    503564(define (report-body-xref source-oid)
     
    509570  (report-body-xref source-oid) )
    510571
    511 ;; Report Toplevel
     572;; Report tables
    512573
    513574(define (report-all-xref)
     
    555616                      source-oid symbol-oid (symbol->string what))]
    556617      [else
    557         (sqlite3:interrupt! *xref-db*)
    558         (close-xref)
    559         (error 'update-xref-use "unknown xref-usage label" xref-use)]) ) )
     618        (error-exit 'update-xref-use "unknown xref-usage label" xref-use)]) ) )
     619
     620(define (update-xref-macro-use source-oid symbol-oid deff usef)
     621  ; Macro definition
     622  (when deff
     623    (sqlite3:exec (xref-statement ins-assign-entry)
     624                  source-oid symbol-oid "syntax") )
     625  ; Macro reference
     626  (when usef
     627    (sqlite3:exec (xref-statement ins-reference-entry)
     628                  source-oid symbol-oid "expanded") ) )
    560629
    561630;; Update tables for single source file
     
    565634    (lambda ()
    566635      (let ([source-oid (ensure-entry-source srcpn)])
    567         (for-each
    568           (lambda (xref)
    569             (let ([symbol-oid (ensure-entry-symbol (xref-symbol xref))])
    570               (for-each (cut update-xref-use source-oid symbol-oid <>)
    571                         (xref-usage-list xref)) ) )
    572           xrefs) )
     636        ; Macro symbols
     637        (for-each (lambda (macuse)
     638                    (let ([symbol-oid (ensure-entry-symbol (car macuse))])
     639                      (update-xref-macro-use source-oid symbol-oid
     640                                             (cadr macuse) (caddr macuse)) ) )
     641                  (car xrefs))
     642        ; Variable symbols
     643        (for-each (lambda (xref)
     644                    (let ([symbol-oid (ensure-entry-symbol (xref-symbol xref))])
     645                      (for-each (cut update-xref-use source-oid symbol-oid <>)
     646                                (xref-usage-list xref)) ) )
     647                  (cdr xrefs)) )
    573648      #t )
    574649    'exclusive) )
    575650
     651
     652;;; Mode operations
     653
     654(define (perform-report srcpn argcnt argidx)
     655  (when (= 0 argidx)
     656    (open-xref) )
     657  (setup-report-formatting)
     658  (if (= 0 argcnt)
     659      (report-all-xref)
     660      (report-xref srcpn) )
     661  (when (or (= 0 argcnt) (= (sub1 argcnt) argidx))
     662    (close-xref) ) )
     663
     664(define (perform-update srcpn argcnt argidx)
     665  (when (= 0 argcnt)
     666    (tool-error "missing source file") )
     667  (when (= 0 argidx)
     668    (open-xref) )
     669  (and-let* ([xrefs (source-xref-list srcpn *compiler-options*)])
     670    (update-xref srcpn xrefs) )
     671  (when (= (sub1 argcnt) argidx)
     672    (close-xref) ) )
     673
     674(define (perform-delete srcpn argcnt argidx)
     675  (error-exit 'main "delete mode unimplemented") )
     676
     677(define (perform-delete-all srcpn argcnt argidx)
     678  (delete-file* *xref-pathname*) )
     679
     680(define (mode-perform)
     681  (cond [*report-mode*        perform-report]
     682        [*update-mode*        perform-update]
     683        [*delete-mode*        perform-delete]
     684        [*delete-all-mode*    perform-delete-all]
     685        [else
     686          perform-update] ) )
     687
    576688;;; Main
    577689
    578690(tool-exit
    579691  (define-tool (chicken-xref srcpn argcnt argidx) *xref-help*
    580     (if *report-mode*
    581         ; then report mode
    582         (begin
    583           (when (= 0 argidx)
    584             (open-xref) )
    585           (setup-report-formatting)
    586           (if (= 0 argcnt)
    587               (report-all-xref)
    588               (report-xref srcpn) )
    589           (when (or (= 0 argcnt) (= (sub1 argcnt) argidx))
    590             (close-xref) ) )
    591         ; else update mode
    592         (begin
    593           (when (= 0 argcnt)
    594             (tool-error "missing source file") )
    595           (when (= 0 argidx)
    596             (open-xref) )
    597           (and-let* ([xrefs (source-xref-list srcpn *compiler-options*)])
    598             (update-xref srcpn xrefs) )
    599           (when (= (sub1 argcnt) argidx)
    600             (close-xref) ) ) ) ) )
     692    ((mode-perform) srcpn argcnt argidx) ) )
  • release/3/source-xref/trunk/source-xref.setup

    r6453 r8021  
    55
    66#|
    7 (compile -s -O3 -d0 -block -check-imports source-xref-single.scm)
     7(compile -s #;-O3 #;-d0 -block -check-imports source-xref-single.scm)
    88(install-extension 'source-xref-single
    99  `("source-xref-single.so")
     
    1111|#
    1212
    13 #;(compile -O3 -d0 -block -check-imports source-xref.scm -o chicken-xref)
    14 (compile -d2 -check-imports source-xref.scm -o chicken-xref)
     13(compile #;-O3 #;-d0 -block -check-imports source-xref.scm -o chicken-xref)
    1514(install-program 'source-xref
    1615        '("chicken-xref")
  • release/3/source-xref/trunk/source-xref.wiki

    r5421 r8021  
    22[[toc:]]
    33
    4 == source-xref
     4== Introduction
    55
    6 === Introduction
     6== Requirements
    77
    8 === Requirements
     8== Usage
    99
    10 === Usage
     10== Documentation
    1111
    12 === Documentation
     12== Examples
    1313
    14 === Examples
     14== Notes
    1515
    16 === Notes
     16== Bugs and limitations
    1717
    18 === Bugs and limitations
     18== Version history
    1919
    20 === Version history
    21 
    22 === Author
     20== Author
    2321
    2422Kon Lovett
    2523
    26 === License
     24== License
    2725
    2826Open Source
Note: See TracChangeset for help on using the changeset viewer.