/[eiffelstudio]/trunk/Src/framework/sqlite3/sqlite_statement.e
ViewVC logotype

Contents of /trunk/Src/framework/sqlite3/sqlite_statement.e

Parent Directory Parent Directory | Revision Log Revision Log


Revision 85923 - (show annotations)
Fri Mar 18 15:53:01 2011 UTC (8 years, 9 months ago) by jfiat
File size: 24675 byte(s)
Fixed void-safety issue with sqlite3 library
1 note
2 description: "[
3 An SQLite statement associated with a database connection.
4
5 Note: If a database connection is closed the statement can no longer be used. In some cases,
6 when a connection is reopened the statement will recompile itself for use with the new
7 connection. However, this is not guarenteed to work. As such, please use caution when
8 caching statements when database connections are open and closed.
9 ]"
10 legal: "See notice at end of class."
11 status: "See notice at end of class."
12 date: "$Date$"
13 revision: "$Revision$"
14
15 class
16 SQLITE_STATEMENT
17
18 inherit
19 DISPOSABLE
20
21 SQLITE_BINDING_HELPERS
22
23 SQLITE_SHARED_API
24
25 --inherit {NONE}
26 SQLITE_INTERNALS
27 export
28 {NONE} all
29 end
30
31 SQLITE_DATABASE_EXTERNALS
32 export
33 {NONE} all
34 end
35
36 SQLITE_STATEMENT_EXTERNALS
37 export
38 {NONE} all
39 end
40
41 SQLITE_BIND_ARG_MARSHALLER
42 export
43 {NONE} all
44 end
45
46 create
47 make
48
49 feature {NONE} -- Initialization
50
51 make (a_statement: READABLE_STRING_8; a_db: SQLITE_DATABASE)
52 -- Initializes a SQL statement for a database.
53 --
54 -- `a_statement': SQL statement to compile.
55 -- `a_db': A database to compile the statement with.
56 require
57 is_sqlite_available: is_sqlite_available
58 a_statement_attached: attached a_statement
59 not_a_statement_is_empty: not a_statement.is_empty
60 a_db_attached: attached a_db
61 a_db_is_accessible: a_db.is_accessible
62 a_db_is_readable: a_db.is_readable
63 a_statement_is_complete: a_db.is_complete_statement (a_statement)
64 do
65 create statement_string.make_from_string (a_statement)
66 create string.make_from_string (a_statement)
67 database := a_db
68 if {PLATFORM}.is_thread_capable then
69 internal_thread_id := get_current_thread_id.to_integer_32
70 end
71 compile
72 ensure
73 string_set: string.same_string (a_statement)
74 statement_string_attached: attached statement_string
75 not_statement_string_is_empty: not statement_string.is_empty
76 database_set: database = a_db
77 end
78
79 feature {NONE} -- Clean up
80
81 dispose
82 -- <Precursor>
83 local
84 l_stmt: like internal_stmt
85 l_db: like internal_db
86 l_api: like sqlite_api
87 l_result: INTEGER
88 do
89 if not is_in_final_collect then
90 l_stmt := internal_stmt
91 if l_stmt /= default_pointer and then is_sqlite_available then
92 -- Check to be sure that the statement is still alive in the db.
93 -- Get the statement's database, if not null then it's alive.
94 --
95 -- Normally this cannot happen as SQLite prevents the DB from being closed until all
96 -- prepared statements have been finalized. Because of the GC statements may have not
97 -- yet been collected when a connection is closed. It is SQLite's documentation
98 -- recommendation to clean up all prepared statements when closing the DB, as such a
99 -- statement might not be valid anymore.
100 l_api := sqlite_api
101 l_db := sqlite3_db_handle (l_api, l_stmt)
102 if l_db /= default_pointer then
103 -- The statement is still connected with the database
104 if l_db = internal_db then
105 l_result := sqlite3_finalize (l_api, l_stmt)
106 if (l_result & {SQLITE_RESULT_CODE}.mask) = {SQLITE_RESULT_CODE}.e_misuse then
107 -- Happens when the DB was closed before the statement was finalized.
108 check is_closed: database.is_closed end
109 else
110 check succeess: sqlite_success (l_result) end
111 end
112 else
113 check False end
114 end
115 end
116
117 end
118 internal_stmt := default_pointer
119 internal_db := default_pointer
120 end
121 ensure then
122 not_is_compiled: not is_compiled
123 not_is_connected: not is_connected
124 internal_stmt_is_null: internal_stmt = default_pointer
125 internal_db_is_null: internal_db = default_pointer
126 end
127
128 feature -- Access
129
130 string: IMMUTABLE_STRING_8
131 -- The full statement string passed during initialization.
132 -- May contain multiple SQLite statements.
133
134 database: SQLITE_DATABASE
135 -- Database to execute the SQL statement on.
136
137 statement_string: IMMUTABLE_STRING_8
138 -- The actual compiled database string.
139 -- This may not be the same as `string' because statements string can be composed of
140 -- of multiple statements. After compilation this will contain a single statement.
141
142 next_statement: detachable SQLITE_STATEMENT
143 -- Next statement in the chain, when the supplied SQLite statement contained multiple SQLite
144 -- statements.
145
146 feature -- Access: Error handling
147
148 last_exception: detachable SQLITE_EXCEPTION
149 -- Last occuring error, set during compilation.
150
151 feature -- Access: Cursor
152
153 execute_new: SQLITE_STATEMENT_ITERATION_CURSOR
154 -- Execute the SQLite statement.
155 require
156 is_compiled: is_compiled
157 is_connected: is_connected
158 not_is_executing: not is_executing
159 is_accessible: is_accessible
160 not_has_arguments: not has_arguments
161 do
162 create Result.make (Current)
163 ensure
164 result_attached: attached Result
165 end
166
167 execute_new_with_arguments (a_arguments: TUPLE): SQLITE_STATEMENT_ITERATION_CURSOR
168 -- Executes the SQLite statement with bound set of arguments.
169 --
170 -- `a_arguments': The bound arguments to call the SQLite query statement with.
171 -- Valid arguments are those that descent {SQLITE_BIND_ARG} or
172 -- {READABLE_STRING_8}, or of type {INTEGER_*}, {NATURAL_*} (with the expection
173 -- of {NATURAL_64}), or {MANAGED_POINTER} (for blobs).
174 -- Note: If *not* using {SQLITE_BIND_ARG}, the SQLite statement should use ?NNN
175 -- arguments and not named arguments.
176 -- see http://sqlite.org/c3ref/bind_blob.html
177 require
178 is_compiled: is_compiled
179 is_connected: is_connected
180 not_is_executing: not is_executing
181 is_accessible: is_accessible
182 has_arguments: has_arguments
183 a_arguments_attached: attached a_arguments
184 a_arguments_count_correct: a_arguments.count.as_natural_32 = arguments_count
185 a_arguments_is_valid_arguments: is_valid_arguments (a_arguments)
186 do
187 create Result.make_with_bindings (Current, new_binding_argument_array (a_arguments))
188 ensure
189 result_attached: attached Result
190 end
191
192 feature {NONE} -- Access
193
194 compile_statement_string: STRING
195 -- The statement used to compile with, which may be a modified version of `statement'.
196 do
197 Result := statement_string
198 ensure
199 result_attached: attached Result
200 not_result_is_empty: not Result.is_empty
201 end
202
203 feature -- Measurement
204
205 arguments_count: NATURAL
206 -- Number of arguments required to be bound to execute the statement.
207 do
208 Result := sqlite3_bind_parameter_count (sqlite_api, internal_stmt).as_natural_32
209 end
210
211 changes_count: NATURAL
212 -- The number of changes executing this statement caused
213 do
214 Result := internal_changes_count
215 if attached next_statement as l_next then
216 Result := Result + l_next.changes_count
217 end
218 end
219
220 feature {SQLITE_RESULT_ROW} -- Measurement
221
222 mark: NATURAL
223 -- The edition mark of the statement.
224 -- A new mark is generated for each execution of recompilation.
225
226 feature -- Status report
227
228 is_executing: BOOLEAN
229 -- Indicates if the statement is currently be executed.
230
231 is_compiled: BOOLEAN
232 -- Indicates if the statement was successfully compiled, and if it is compiled with the
233 -- associated database `database'.
234 do
235 Result := internal_stmt /= default_pointer
236 ensure
237 not_internal_stmt_is_null: Result implies internal_stmt /= default_pointer
238 end
239
240 is_connected: BOOLEAN
241 -- Indicates if the statement is still connected to a database.
242 -- Disconnection occurs when a database connection is closed. Even when reopen the statement
243 -- will still remain disconnected. Use `ensure_connected' to attempt to reconnect to the
244 -- associated database.
245 do
246 Result := is_compiled and then
247 internal_db = database.internal_db
248 ensure
249 not_internal_stmt_is_null: Result implies (is_compiled and then
250 internal_db = database.internal_db)
251 end
252
253 is_accessible: BOOLEAN
254 -- Indicates if the statement is accessible on the current thread.
255 do
256 Result := is_interface_usable and then database.is_accessible
257 if Result then
258 if {PLATFORM}.is_thread_capable then
259 Result := internal_thread_id = get_current_thread_id.to_integer_32
260 else
261 Result := True
262 end
263 end
264
265 ensure
266 true_result: not {PLATFORM}.is_thread_capable implies (Result and database.is_accessible)
267 same_internal_thread_id: (Result and {PLATFORM}.is_thread_capable) implies internal_thread_id = get_current_thread_id.to_integer_32
268 end
269
270 has_arguments: BOOLEAN
271 -- Indicates if the statement has required arguments to execute.
272 do
273 Result := arguments_count > 0
274 ensure
275 has_arguments: Result = (arguments_count > 0)
276 end
277
278 has_error: BOOLEAN
279 -- Indicates if an error occured during the last operation.
280 do
281 Result := last_exception /= Void
282 ensure
283 last_exception_attached: Result implies last_exception /= Void
284 end
285
286 feature {NONE} -- Status report
287
288 is_abort_requested: BOOLEAN
289 -- Indicates if an abort is requested for the currently executed statement.
290
291 feature -- Basic operations
292
293 abort
294 -- Aborts an executing statement.
295 -- Note: Unlike most of the routines, this can be called from another thread.
296 require
297 is_sqlite_available: is_sqlite_available
298 is_interface_usable: is_interface_usable
299 is_executing: is_accessible implies is_executing -- Calls on the same thread can guarentee `is_executing'.
300 do
301 is_abort_requested := True
302
303 if attached next_statement as l_next then
304 l_next.abort
305 end
306 ensure
307 is_abort_requested: is_abort_requested
308 end
309
310 ensure_connected
311 -- Attempts to ensure a statement is connected to the associated database.
312 -- Note: This helper routine can be used prior to executing the statement to ensure the
313 -- statement is compiled for use with the associated database. Statements become
314 -- disconnected when the assoicated database is closed. Even when reopening the database
315 -- a reconnection must be made.
316 --
317 -- Calling this routine will not reopen the database!
318 -- Reconnection is not guarenteed!
319 require
320 is_sqlite_available: is_sqlite_available
321 is_interface_usable: is_interface_usable
322 is_accessible: is_accessible
323 database_is_accessible: database.is_accessible
324 database_is_readable: database.is_readable
325 do
326 if not is_connected then
327 -- Wipe out internals
328 reset_compilation_data
329
330 -- Attempt to recompile
331 compile
332 end
333
334 if attached next_statement as l_next then
335 l_next.ensure_connected
336 end
337 ensure
338 mark_increased: (not old is_connected) implies (mark > old mark)
339 end
340
341 feature {SQLITE_STATEMENT} -- Basic operations: Execution
342
343 reset
344 -- Resets any cached information from the last execution.
345 require
346 not_is_executing: not is_executing
347 do
348 is_abort_requested := False
349 internal_changes_count := 0
350 last_exception := Void
351 ensure
352 not_is_abort_requested: not is_abort_requested
353 internal_changes_count_reset: internal_changes_count = 0
354 last_exception_detached: not attached last_exception
355 not_has_error: not has_error
356 end
357
358 reset_all
359 -- Resets any cached information from the last execution for all statements.
360 require
361 not_is_executing: not is_executing
362 do
363 reset
364 if attached next_statement as l_next then
365 l_next.reset_all
366 end
367 ensure
368 not_is_abort_requested: not is_abort_requested
369 internal_changes_count_reset: internal_changes_count = 0
370 last_exception_detached: not attached last_exception
371 not_has_error: not has_error
372 end
373
374 execute_internal (a_callback: detachable FUNCTION [ANY, TUPLE [row: SQLITE_RESULT_ROW], BOOLEAN]; a_bindings: detachable ARRAY [SQLITE_BIND_ARG [ANY]])
375 -- Performs execution of the SQLite statement with a callback routine for each returned result row.
376 --
377 -- `a_callback': A callback routine accepting a result row as its argument.
378 -- Return True from the function to abort further calls when there is more result data.
379 -- `a_bindings': An array or binding arguments, or Void if the statement does not require any arguments.
380 require
381 is_sqlite_available: is_sqlite_available
382 is_interface_usable: is_interface_usable
383 is_compiled: is_compiled
384 is_connected: is_connected
385 not_is_executing: not is_executing
386 is_accessible: is_accessible
387 database_is_readable: database.is_readable
388 a_bindings_attached: has_arguments implies attached a_bindings
389 a_bindings_count_big_enough: not attached a_bindings or else a_bindings.count.as_natural_32 = arguments_count
390 local
391 l_api: like sqlite_api
392 l_stmt: like internal_stmt
393 l_db: detachable like database
394 l_exception: detachable SQLITE_EXCEPTION
395 l_result: INTEGER
396 l_done: BOOLEAN
397 l_locked: BOOLEAN
398 l_row: SQLITE_RESULT_ROW
399 i_upper, i: INTEGER
400 l_arg_variable: IMMUTABLE_STRING_8
401 l_arg_id: C_STRING
402 l_arg_index: INTEGER
403 l_total_count: NATURAL
404 do
405 -- Perform bindings
406 if attached a_bindings as l_bindings then
407 from
408 i := l_bindings.lower
409 i_upper := l_bindings.upper
410 until
411 i > i_upper
412 loop
413 if attached l_bindings[i] as l_arg then
414 l_arg_variable := l_arg.variable
415 create l_arg_id.make (l_arg_variable)
416 l_arg_index := sqlite3_bind_parameter_index (sqlite_api, internal_stmt, l_arg_id.item)
417 if l_arg_index = 0 and then l_arg_variable[1] = '?' then
418 l_arg_variable := l_arg_variable.substring (2, l_arg_variable.count)
419 if l_arg_variable.is_integer_32 then
420 l_arg_index := l_arg_variable.to_integer_32
421 else
422 -- Contracts in SQLITE_BIND_ARG should make this impossible.
423 check should_never_happen: False end
424 end
425 end
426
427 if l_arg_index > 0 then
428 l_arg.bind_to_statement (Current, l_arg_index)
429 else
430 -- Silently ignore unknown variables.
431 end
432 end
433 i := i + 1
434 end
435 end
436
437 -- Change the mark number
438 mark := mark + 1
439
440 l_api := sqlite_api
441 l_stmt := internal_stmt
442 l_db := database
443
444 -- Reset cache information
445 reset_all
446 is_executing := True
447
448 -- Notify pre-execute
449 on_before_execute
450
451 -- Note the locking sequencing, to ensure access to the error messages
452 -- are not affected by other threads.
453 l_db.lock -- (+1) 1
454 l_locked := True
455
456 l_total_count := l_db.total_changes_count
457
458 from
459 create l_row.make (Current)
460 l_result := sqlite3_step (l_api, l_stmt)
461 l_done := l_result = {SQLITE_RESULT_CODE}.done
462 until
463 not sqlite_success (l_result) or l_done
464 loop
465 -- Unlock before any callback because it may need access to the DB using another thread.
466 l_db.unlock -- (-1) 0
467 l_locked := False
468
469 if attached a_callback then
470 -- Increment the row index
471 l_row.index := l_row.index + 1
472 -- Call back with the result row.
473 is_abort_requested := a_callback.item ([l_row]) or is_abort_requested
474 end
475
476
477 if is_connected then
478 -- The callback could have closed the DB connection so the check is needed.
479 l_db.lock -- (+1) 1
480 l_locked := True
481
482 -- Check abort status
483 l_done := is_abort_requested
484 if l_done then
485 -- Abort the last operation
486 l_db.abort
487 else
488 l_done := l_result /= {SQLITE_RESULT_CODE}.row
489 if not l_done then
490 l_result := sqlite3_step (l_api, l_stmt)
491 l_done := l_result = {SQLITE_RESULT_CODE}.done
492 end
493 end
494 else
495 l_done := True
496 end
497 end
498
499 -- Fetch any error information, if any, and report it.
500 if not sqlite_success (l_result) then
501 if is_connected then
502 l_exception := l_db.last_exception
503 if l_locked then
504 l_locked := False
505 l_db.unlock -- (-1) 0
506 end
507 end
508 if not attached l_exception then
509 -- No exception
510 l_exception := sqlite_exception (l_result, Void)
511 end
512 last_exception := l_exception
513
514 -- Notify post execute
515 on_after_executed
516
517 l_exception.raise
518 else
519 -- Set the change count
520 internal_changes_count := l_db.total_changes_count - l_total_count
521
522 if l_locked then
523 l_locked := False
524 l_db.unlock -- (-1) 0
525 end
526 if attached next_statement as l_next then
527 -- There is another statement to execute, process this before unlocking the database.
528 l_next.execute_internal (a_callback, a_bindings)
529 end
530
531 -- Notify post execute
532 on_after_executed
533 end
534
535 -- Reset the statement for repeated use.
536 is_executing := False
537 l_result := sqlite3_reset (l_api, l_stmt)
538 check success: sqlite_success (l_result) end
539 if attached a_bindings then
540 -- `sqlite3_reset' does not reset the bindings! We have to do it as a second stage.
541 -- In a future release, when we might allow bindings to be set arbitrarily we might
542 -- not want to reset them. There is a performance gain for clients, because there is
543 -- not object marshalling required for arguments that do not change. Currently we have
544 -- to pass all arguments again when executing, and they are rebound (involving copy
545 -- operations etc.)
546 l_result := sqlite3_clear_bindings (l_api, l_stmt)
547 check success: sqlite_success (l_result) end
548 end
549 ensure
550 not_is_executing: not is_executing
551 mark_increased: mark > old mark
552 rescue
553 if l_locked then
554 check l_db_attached: attached l_db end
555 l_locked := False
556 l_db.unlock
557 end
558 if is_executing then
559 is_executing := False
560 sqlite3_reset (sqlite_api, internal_stmt).do_nothing
561 end
562 end
563
564 feature {NONE} -- Basic operations: Compilation
565
566 reset_compilation_data
567 -- Clears any cached information related to the preperation/compilation of the statement
568 do
569 internal_stmt := default_pointer
570 internal_db := default_pointer
571 reset
572
573 -- The statement is no longer compiled so invalidate any retained linked
574 -- references.
575 mark := mark + 1
576 ensure
577 not_is_compiled: not is_compiled
578 not_is_connected: not is_connected
579 not_is_abort_requested: not is_abort_requested
580 internal_changes_count_reset: internal_changes_count = 0
581 last_exception_detached: not attached last_exception
582 mark_increased: mark > old mark
583 not_has_error: not has_error
584 end
585
586 compile
587 -- Compiles the current statement.
588 require
589 is_sqlite_available: is_sqlite_available
590 is_interface_usable: is_interface_usable
591 not_is_connected: not is_connected
592 not_has_error: not has_error
593 database_is_readable: database.is_readable
594 database: database.is_readable
595 local
596 l_db: like database
597 l_string: C_STRING
598 l_next_string: STRING
599 l_next_statement: SQLITE_STATEMENT
600 l_stmt_handle: POINTER
601 l_internal_db: POINTER
602 l_tail: POINTER
603 l_locked: BOOLEAN
604 l_result: INTEGER
605 do
606 create l_string.make (compile_statement_string)
607
608 l_db := database
609 l_db.lock
610 l_locked := True
611
612 l_internal_db := l_db.internal_db
613 -- FIXME: SQLITE_BUSY may be returned and so we should retry preparation until a default/specified timeout.
614 l_result := sqlite3_prepare_v2 (sqlite_api, l_internal_db, l_string.item, l_string.count + 1, $l_stmt_handle, $l_tail)
615 if l_stmt_handle /= default_pointer then
616 internal_stmt := l_stmt_handle
617 internal_db := l_internal_db
618 last_exception := Void
619
620 if l_tail /= default_pointer then
621 -- There is more to process, which means there should not already be a next statement because
622 -- this is the first compilation.
623 check next_statement_detached: not attached next_statement end
624 create l_string.make_shared_from_pointer (l_tail)
625 l_next_string := l_string.string
626 if not l_next_string.is_empty then
627 -- Create the next statement string, with only the compiled statement
628 create statement_string.make_from_string (statement_string.substring (1, statement_string.count - l_next_string.count))
629
630 -- Remove ; from the start of the statement, if it exists.
631 l_next_string.prune_all_leading (';')
632 -- Remove extra whitespace
633 l_next_string.left_adjust
634 if not l_next_string.is_empty then
635 create l_next_statement.make (l_next_string, l_db)
636 if l_next_statement.has_error then
637 -- An error occurred so the entire statement should be considered uncompiled
638 reset_compilation_data
639
640 -- Use statement's last exception and not the database's because of future/redefined error
641 -- processing could manipulate this.
642 last_exception := l_next_statement.last_exception
643 else
644 next_statement := l_next_statement
645 end
646 end
647
648 end
649 end
650 else
651 reset_compilation_data
652 if sqlite_success (l_result) then
653 -- Happens when there statement is a empty one.
654 last_exception := sqlite_exception ({SQLITE_RESULT_CODE}.e_misuse, "no statement: syntax error")
655 else
656 last_exception := l_db.last_exception
657 end
658 end
659
660 l_locked := False
661 l_db.unlock
662 ensure
663 not_internal_db_is_null: is_compiled implies internal_db /= default_pointer
664 internal_db_is_null: not is_compiled implies internal_db = default_pointer
665 last_exception_attached: not is_compiled implies attached last_exception
666 last_exception_attached: is_compiled implies not attached last_exception
667 rescue
668 if l_locked then
669 l_locked := False
670 database.unlock
671 end
672 end
673
674 feature {SQLITE_STATEMENT_ITERATION_CURSOR} -- Action handlers
675
676 on_before_execute
677 -- Called before a statement has been executed.
678 require
679 is_sqlite_available: is_sqlite_available
680 is_interface_usable: is_interface_usable
681 is_connected: is_connected
682 not_has_error: not has_error
683 is_accessible: is_accessible
684 database_is_readable: database.is_readable
685 do
686 is_executing := True
687 ensure
688 is_executing: is_executing
689 end
690
691 on_after_executed
692 -- Called after a statement has been executed, successfully or not.
693 require
694 is_sqlite_available: is_sqlite_available
695 is_interface_usable: is_interface_usable
696 is_accessible: is_accessible
697 do
698 is_executing := False
699 ensure
700 not_is_executing: not is_executing
701 end
702
703 feature {SQLITE_INTERNALS} -- Implementation
704
705 internal_stmt: POINTER
706 -- Pointer to the compiled statement
707
708 internal_db: POINTER
709 -- The pointer to the database connection when the statement was compiled.
710 --| Used to determine if the database is the same when executing the statement.
711
712 internal_changes_count: NATURAL
713 -- Mutable version of `changes_count'.
714 -- Note: Do not use directly!
715
716 feature {NONE} -- Implementation
717
718 internal_thread_id: INTEGER
719 -- The thread the database was connected using.
720 --| In non multi-threaded systems this will always be 0.
721
722 feature {NONE} -- Externals
723
724 get_current_thread_id: POINTER
725 -- Returns a pointer to the thread-id of the thread.
726 require
727 is_thread_capable: {PLATFORM}.is_thread_capable
728 external
729 "C use %"eif_threads.h%""
730 alias
731 "eif_thr_thread_id"
732 end
733
734 invariant
735 database_attached: attached database
736 statement_attached: attached statement_string
737 not_statement_is_empty: not statement_string.is_empty
738 string_attached: attached string
739 not_string_is_empty: not string.is_empty
740 not_internal_db_is_null: internal_stmt /= default_pointer implies internal_db /= default_pointer
741 internal_db_is_null: internal_stmt = default_pointer implies internal_db = default_pointer
742 internal_thread_id_set: {PLATFORM}.is_thread_capable implies internal_thread_id /= 0
743
744 ;note
745 copyright: "Copyright (c) 1984-2011, Eiffel Software"
746 license: "GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)"
747 licensing_options: "http://www.eiffel.com/licensing"
748 copying: "[
749 This file is part of Eiffel Software's Eiffel Development Environment.
750
751 Eiffel Software's Eiffel Development Environment is free
752 software; you can redistribute it and/or modify it under
753 the terms of the GNU General Public License as published
754 by the Free Software Foundation, version 2 of the License
755 (available at the URL listed under "license" above).
756
757 Eiffel Software's Eiffel Development Environment is
758 distributed in the hope that it will be useful, but
759 WITHOUT ANY WARRANTY; without even the implied warranty
760 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
761 See the GNU General Public License for more details.
762
763 You should have received a copy of the GNU General Public
764 License along with Eiffel Software's Eiffel Development
765 Environment; if not, write to the Free Software Foundation,
766 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
767 ]"
768 source: "[
769 Eiffel Software
770 5949 Hollister Ave., Goleta, CA 93117 USA
771 Telephone 805-685-1006, Fax 805-685-6869
772 Website http://www.eiffel.com
773 Customer support http://support.eiffel.com
774 ]"
775
776 end

Properties

Name Value
svn:eol-style native
svn:keywords Author Date ID Revision

  ViewVC Help
Powered by ViewVC 1.1.23