home *** CD-ROM | disk | FTP | other *** search
- # $Id: Driver.xst,v 11.14 2003/08/22 21:23:39 timbo Exp $
- # Copyright (c) 1997-2002 Tim Bunce Ireland
- # Copyright (c) 2002 Jonathan Leffler
- #
- # You may distribute under the terms of either the GNU General Public
- # License or the Artistic License, as specified in the Perl README file.
-
-
- #include "Driver_xst.h"
-
-
- MODULE = DBD::~DRIVER~ PACKAGE = DBD::~DRIVER~
-
- REQUIRE: 1.929
- PROTOTYPES: DISABLE
-
- BOOT:
- items = 0; /* avoid 'unused variable' warning */
- DBISTATE_INIT;
- /* XXX this interface will change: */
- DBI_IMP_SIZE("DBD::~DRIVER~::dr::imp_data_size", sizeof(imp_drh_t));
- DBI_IMP_SIZE("DBD::~DRIVER~::db::imp_data_size", sizeof(imp_dbh_t));
- DBI_IMP_SIZE("DBD::~DRIVER~::st::imp_data_size", sizeof(imp_sth_t));
- dbd_init(DBIS);
-
-
- # ------------------------------------------------------------
- # driver level interface
- # ------------------------------------------------------------
- MODULE = DBD::~DRIVER~ PACKAGE = DBD::~DRIVER~::dr
-
-
- #ifdef dbd_discon_all
-
- # disconnect_all renamed and ALIAS'd to avoid length clash on VMS :-(
- void
- discon_all_(drh)
- SV * drh
- ALIAS:
- disconnect_all = 1
- CODE:
- D_imp_drh(drh);
- if (0) ix = ix; /* avoid unused variable warning */
- ST(0) = dbd_discon_all(drh, imp_drh) ? &sv_yes : &sv_no;
-
- #endif /* dbd_discon_all */
-
-
- #ifdef dbd_dr_data_sources
-
- void
- data_sources(drh, attr = Nullsv)
- SV *drh
- SV *attr
- PPCODE:
- {
- D_imp_drh(drh);
- AV *av = dbd_dr_data_sources(drh, imp_drh, attr);
- if (av) {
- int i;
- int n = AvFILL(av)+1;
- EXTEND(sp, n);
- for (i = 0; i < n; ++i) {
- PUSHs(AvARRAY(av)[i]);
- }
- }
- }
-
- #endif
-
-
- # ------------------------------------------------------------
- # database level interface
- # ------------------------------------------------------------
- MODULE = DBD::~DRIVER~ PACKAGE = DBD::~DRIVER~::db
-
-
- void
- _login(dbh, dbname, username, password, attribs=Nullsv)
- SV * dbh
- char * dbname
- SV * username
- SV * password
- SV * attribs
- CODE:
- {
- STRLEN lna;
- D_imp_dbh(dbh);
- char *u = (SvOK(username)) ? SvPV(username,lna) : "";
- char *p = (SvOK(password)) ? SvPV(password,lna) : "";
- #ifdef dbd_db_login6
- ST(0) = dbd_db_login6(dbh, imp_dbh, dbname, u, p, attribs) ? &sv_yes : &sv_no;
- #else
- ST(0) = dbd_db_login( dbh, imp_dbh, dbname, u, p) ? &sv_yes : &sv_no;
- #endif
- }
-
-
- void
- selectall_arrayref(...)
- PREINIT:
- SV *sth;
- SV **maxrows_svp;
- SV **tmp_svp;
- SV *attr = &PL_sv_undef;
- imp_sth_t *imp_sth;
- CODE:
- if (items > 2) {
- attr = ST(2);
- if (SvROK(attr) &&
- (DBD_ATTRIB_TRUE(attr,"Slice",5,tmp_svp) || DBD_ATTRIB_TRUE(attr,"Columns",7,tmp_svp))
- ) {
- /* fallback to perl implementation */
- ST(0) = dbixst_bounce_method("DBD::~DRIVER~::db::SUPER::selectall_arrayref", items);
- XSRETURN(1);
- }
- }
- /* --- prepare --- */
- if (SvROK(ST(1))) {
- sth = ST(1);
- }
- else {
- sth = dbixst_bounce_method("prepare", 3);
- if (!SvROK(sth))
- XSRETURN_UNDEF;
- }
- imp_sth = (imp_sth_t*)(DBIh_COM(sth));
- /* --- bind_param --- */
- if (items > 3) { /* need to bind params before execute */
- if (!dbdxst_bind_params(sth, imp_sth, items-2, ax+2) ) {
- XSRETURN_UNDEF;
- }
- }
- /* --- execute --- */
- DBIc_ROW_COUNT(imp_sth) = 0;
- if ( dbd_st_execute(sth, imp_sth) <= -2 ) { /* -2 == error */
- XSRETURN_UNDEF;
- }
- /* --- fetchall --- */
- maxrows_svp = DBD_ATTRIB_GET_SVP(attr, "MaxRows", 7);
- ST(0) = dbdxst_fetchall_arrayref(sth, &PL_sv_undef, (maxrows_svp) ? *maxrows_svp : &PL_sv_undef);
-
-
- void
- selectrow_arrayref(...)
- ALIAS:
- selectrow_array = 1
- PREINIT:
- imp_sth_t *imp_sth;
- SV *sth;
- AV *row_av;
- PPCODE:
- if (SvROK(ST(1))) {
- sth = ST(1);
- }
- else {
- /* --- prepare --- */
- sth = dbixst_bounce_method("prepare", 3);
- if (!SvROK(sth))
- XSRETURN_UNDEF;
- }
- imp_sth = (imp_sth_t*)(DBIh_COM(sth));
- /* --- bind_param --- */
- if (items > 3) { /* need to bind params before execute */
- if (!dbdxst_bind_params(sth, imp_sth, items-2, ax+2) ) {
- XSRETURN_UNDEF;
- }
- }
- /* --- execute --- */
- DBIc_ROW_COUNT(imp_sth) = 0;
- if ( dbd_st_execute(sth, imp_sth) <= -2 ) { /* -2 == error */
- XSRETURN_UNDEF;
- }
- /* --- fetchrow_arrayref --- */
- row_av = dbd_st_fetch(sth, imp_sth);
- if (!row_av) {
- if (GIMME == G_SCALAR)
- PUSHs(&PL_sv_undef);
- }
- else if (ix == 1) { /* selectrow_array */
- int i;
- int num_fields = AvFILL(row_av)+1;
- if (GIMME == G_SCALAR)
- num_fields = 1; /* return just first field */
- EXTEND(sp, num_fields);
- for(i=0; i < num_fields; ++i) {
- PUSHs(AvARRAY(row_av)[i]);
- }
- }
- else {
- PUSHs( sv_2mortal(newRV((SV *)row_av)) );
- }
- /* --- finish --- */
- #ifdef dbd_st_finish3
- dbd_st_finish3(sth, imp_sth, 0);
- #else
- dbd_st_finish(sth, imp_sth);
- #endif
-
-
- #ifdef dbd_db_do4 /* deebeedee-deebee-doo, deebee-doobee-dah? */
-
- void
- do(dbh, statement, params = Nullsv)
- SV * dbh
- char * statement
- SV * params
- CODE:
- {
- D_imp_dbh(dbh);
- IV retval;
- retval = dbd_db_do4(dbh, imp_dbh, statement, params);
- /* remember that dbd_db_do4 must return <= -2 for error */
- if (retval == 0) /* ok with no rows affected */
- XST_mPV(0, "0E0"); /* (true but zero) */
- else if (retval < -1) /* -1 == unknown number of rows */
- XST_mUNDEF(0); /* <= -2 means error */
- else
- XST_mIV(0, retval); /* typically 1, rowcount or -1 */
- }
-
- #endif
-
-
- #ifdef dbd_db_last_insert_id
-
- void
- last_insert_id(dbh, catalog, schema, table, field, attr=Nullsv)
- SV * dbh
- SV * catalog
- SV * schema
- SV * table
- SV * field
- SV * attr
- CODE:
- {
- D_imp_dbh(dbh);
- ST(0) = dbd_db_last_insert_id(dbh, imp_dbh, catalog, schema, table, field, attr);
- }
-
- #endif
-
-
- void
- commit(dbh)
- SV * dbh
- CODE:
- D_imp_dbh(dbh);
- if (DBIc_has(imp_dbh,DBIcf_AutoCommit) && DBIc_WARN(imp_dbh))
- warn("commit ineffective with AutoCommit enabled");
- ST(0) = dbd_db_commit(dbh, imp_dbh) ? &sv_yes : &sv_no;
-
-
- void
- rollback(dbh)
- SV * dbh
- CODE:
- D_imp_dbh(dbh);
- if (DBIc_has(imp_dbh,DBIcf_AutoCommit) && DBIc_WARN(imp_dbh))
- warn("rollback ineffective with AutoCommit enabled");
- ST(0) = dbd_db_rollback(dbh, imp_dbh) ? &sv_yes : &sv_no;
-
-
- void
- disconnect(dbh)
- SV * dbh
- CODE:
- D_imp_dbh(dbh);
- if ( !DBIc_ACTIVE(imp_dbh) ) {
- XSRETURN_YES;
- }
- /* pre-disconnect checks and tidy-ups */
- if (DBIc_CACHED_KIDS(imp_dbh)) {
- SvREFCNT_dec(DBIc_CACHED_KIDS(imp_dbh)); /* cast them to the winds */
- DBIc_CACHED_KIDS(imp_dbh) = Nullhv;
- }
- /* Check for disconnect() being called whilst refs to cursors */
- /* still exists. This possibly needs some more thought. */
- if (DBIc_ACTIVE_KIDS(imp_dbh) && DBIc_WARN(imp_dbh) && !dirty) {
- STRLEN lna;
- char *plural = (DBIc_ACTIVE_KIDS(imp_dbh)==1) ? "" : "s";
- warn("%s->disconnect invalidates %d active statement handle%s %s",
- SvPV(dbh,lna), (int)DBIc_ACTIVE_KIDS(imp_dbh), plural,
- "(either destroy statement handles or call finish on them before disconnecting)");
- }
- ST(0) = dbd_db_disconnect(dbh, imp_dbh) ? &sv_yes : &sv_no;
- DBIc_ACTIVE_off(imp_dbh); /* ensure it's off, regardless */
-
-
- void
- STORE(dbh, keysv, valuesv)
- SV * dbh
- SV * keysv
- SV * valuesv
- CODE:
- D_imp_dbh(dbh);
- if (SvGMAGICAL(valuesv))
- mg_get(valuesv);
- ST(0) = &sv_yes;
- if (!dbd_db_STORE_attrib(dbh, imp_dbh, keysv, valuesv))
- if (!DBIc_DBISTATE(imp_dbh)->set_attr(dbh, keysv, valuesv))
- ST(0) = &sv_no;
-
-
- void
- FETCH(dbh, keysv)
- SV * dbh
- SV * keysv
- CODE:
- D_imp_dbh(dbh);
- SV *valuesv = dbd_db_FETCH_attrib(dbh, imp_dbh, keysv);
- if (!valuesv)
- valuesv = DBIc_DBISTATE(imp_dbh)->get_attr(dbh, keysv);
- ST(0) = valuesv; /* dbd_db_FETCH_attrib did sv_2mortal */
-
-
- void
- DESTROY(dbh)
- SV * dbh
- PPCODE:
- D_imp_dbh(dbh);
- ST(0) = &sv_yes;
- if (!DBIc_IMPSET(imp_dbh)) { /* was never fully set up */
- STRLEN lna;
- if (DBIc_WARN(imp_dbh) && !dirty && DBIc_DBISTATE(imp_dbh)->debug >= 2)
- PerlIO_printf(DBIc_LOGPIO(imp_dbh),
- " DESTROY for %s ignored - handle not initialised\n",
- SvPV(dbh,lna));
- }
- else {
- /* pre-disconnect checks and tidy-ups */
- if (DBIc_CACHED_KIDS(imp_dbh)) {
- SvREFCNT_dec(DBIc_CACHED_KIDS(imp_dbh)); /* cast them to the winds */
- DBIc_CACHED_KIDS(imp_dbh) = Nullhv;
- }
- if (DBIc_IADESTROY(imp_dbh)) { /* want's ineffective destroy */
- DBIc_ACTIVE_off(imp_dbh);
- }
- if (DBIc_ACTIVE(imp_dbh)) {
- /* The application has not explicitly disconnected. That's bad. */
- /* To ensure integrity we *must* issue a rollback. This will be */
- /* harmless if the application has issued a commit. If it hasn't */
- /* then it'll ensure integrity. Consider a Ctrl-C killing perl */
- /* between two statements that must be executed as a transaction. */
- /* Perl will call DESTROY on the dbh and, if we don't rollback, */
- /* the server may automatically commit! Bham! Corrupt database! */
- if (!DBIc_has(imp_dbh,DBIcf_AutoCommit)) {
- if (DBIc_WARN(imp_dbh)
- && DBIc_is(imp_dbh, DBIcf_Executed)
- && (!dirty || DBIc_DBISTATE(imp_dbh)->debug >= 3)
- )
- warn("Issuing rollback() for database handle being DESTROY'd without explicit disconnect()");
- dbd_db_rollback(dbh, imp_dbh); /* ROLLBACK! */
- }
- dbd_db_disconnect(dbh, imp_dbh);
- DBIc_ACTIVE_off(imp_dbh); /* ensure it's off, regardless */
- }
- dbd_db_destroy(dbh, imp_dbh);
- }
-
-
- #ifdef dbd_take_imp_data
-
- void
- take_imp_data(h)
- SV * h
- CODE:
- D_imp_xxh(h);
- ST(0) = (dbd_take_imp_data(h, imp_xxh, NULL))
- ? dbixst_bounce_method("DBD::~DRIVER~::db::SUPER::take_imp_data", items)
- : &sv_undef;
-
- #endif
-
- #ifdef dbd_db_data_sources
-
- void
- data_sources(dbh, attr = Nullsv)
- SV *dbh
- SV *attr
- PPCODE:
- {
- D_imp_dbh(dbh);
- AV *av = dbd_db_data_sources(dbh, imp_dbh, attr);
- if (av) {
- int i;
- int n = AvFILL(av)+1;
- EXTEND(sp, n);
- for (i = 0; i < n; ++i) {
- PUSHs(AvARRAY(av)[i]);
- }
- }
- }
-
- #endif
-
- # -- end of DBD::~DRIVER~::db
-
- # ------------------------------------------------------------
- # statement interface
- # ------------------------------------------------------------
- MODULE = DBD::~DRIVER~ PACKAGE = DBD::~DRIVER~::st
-
-
- void
- _prepare(sth, statement, attribs=Nullsv)
- SV * sth
- char * statement
- SV * attribs
- CODE:
- {
- D_imp_sth(sth);
- DBD_ATTRIBS_CHECK("_prepare", sth, attribs);
- ST(0) = dbd_st_prepare(sth, imp_sth, statement, attribs) ? &sv_yes : &sv_no;
- }
-
-
- #ifdef dbd_st_rows
-
- void
- rows(sth)
- SV * sth
- CODE:
- D_imp_sth(sth);
- XST_mIV(0, dbd_st_rows(sth, imp_sth));
-
- #endif /* dbd_st_rows */
-
-
- #ifdef dbd_st_bind_col
-
- void
- bind_col(sth, col, ref, attribs=Nullsv)
- SV * sth
- SV * col
- SV * ref
- SV * attribs
- CODE:
- {
- IV sql_type = 0;
- D_imp_sth(sth);
- if (SvGMAGICAL(ref))
- mg_get(ref);
- if (attribs) {
- if (SvNIOK(attribs)) {
- sql_type = SvIV(attribs);
- attribs = Nullsv;
- }
- else {
- SV **svp;
- DBD_ATTRIBS_CHECK("bind_col", sth, attribs);
- /* XXX we should perhaps complain if TYPE is not SvNIOK */
- DBD_ATTRIB_GET_IV(attribs, "TYPE",4, svp, sql_type);
- }
- }
- switch(dbd_st_bind_col(sth, imp_sth, col, ref, sql_type, attribs)) {
- case 2: ST(0) = &sv_yes; /* job done completely */
- break;
- case 1: /* fallback to DBI default */
- ST(0) = (DBIc_DBISTATE(imp_sth)->bind_col(sth, col, ref, attribs))
- ? &sv_yes : &sv_no;
- break;
- default: ST(0) = &sv_no; /* dbd_st_bind_col has called set_err */
- break;
- }
- }
-
- #endif /* dbd_st_bind_col */
-
- void
- bind_param(sth, param, value, attribs=Nullsv)
- SV * sth
- SV * param
- SV * value
- SV * attribs
- CODE:
- {
- IV sql_type = 0;
- D_imp_sth(sth);
- if (SvGMAGICAL(value))
- mg_get(value);
- if (attribs) {
- if (SvNIOK(attribs)) {
- sql_type = SvIV(attribs);
- attribs = Nullsv;
- }
- else {
- SV **svp;
- DBD_ATTRIBS_CHECK("bind_param", sth, attribs);
- /* XXX we should perhaps complain if TYPE is not SvNIOK */
- DBD_ATTRIB_GET_IV(attribs, "TYPE",4, svp, sql_type);
- }
- }
- ST(0) = dbd_bind_ph(sth, imp_sth, param, value, sql_type, attribs, FALSE, 0)
- ? &sv_yes : &sv_no;
- }
-
-
- void
- bind_param_inout(sth, param, value_ref, maxlen, attribs=Nullsv)
- SV * sth
- SV * param
- SV * value_ref
- IV maxlen
- SV * attribs
- CODE:
- {
- IV sql_type = 0;
- D_imp_sth(sth);
- SV *value;
- if (!SvROK(value_ref) || SvTYPE(SvRV(value_ref)) > SVt_PVMG)
- croak("bind_param_inout needs a reference to a scalar value");
- value = SvRV(value_ref);
- if (SvREADONLY(value))
- croak("Modification of a read-only value attempted");
- if (SvGMAGICAL(value))
- mg_get(value);
- if (attribs) {
- if (SvNIOK(attribs)) {
- sql_type = SvIV(attribs);
- attribs = Nullsv;
- }
- else {
- SV **svp;
- DBD_ATTRIBS_CHECK("bind_param", sth, attribs);
- DBD_ATTRIB_GET_IV(attribs, "TYPE",4, svp, sql_type);
- }
- }
- ST(0) = dbd_bind_ph(sth, imp_sth, param, value, sql_type, attribs, TRUE, maxlen)
- ? &sv_yes : &sv_no;
- }
-
-
- void
- execute(sth, ...)
- SV * sth
- CODE:
- D_imp_sth(sth);
- int retval;
- if (items > 1) { /* need to bind params */
- if (!dbdxst_bind_params(sth, imp_sth, items, ax) ) {
- XSRETURN_UNDEF;
- }
- }
- /* XXX this code is duplicated in selectrow_arrayref above */
- if (DBIc_ROW_COUNT(imp_sth) > 0) /* reset for re-execute */
- DBIc_ROW_COUNT(imp_sth) = 0;
- retval = dbd_st_execute(sth, imp_sth);
- /* remember that dbd_st_execute must return <= -2 for error */
- if (retval == 0) /* ok with no rows affected */
- XST_mPV(0, "0E0"); /* (true but zero) */
- else if (retval < -1) /* -1 == unknown number of rows */
- XST_mUNDEF(0); /* <= -2 means error */
- else
- XST_mIV(0, retval); /* typically 1, rowcount or -1 */
-
-
- #ifdef dbd_st_execute_for_fetch
-
- void
- execute_for_fetch(sth, fetch_tuple_sub, tuple_status = Nullsv)
- SV * sth
- SV * fetch_tuple_sub
- SV * tuple_status
- CODE:
- {
- D_imp_sth(sth);
- ST(0) = dbd_st_execute_for_fetch(sth, imp_sth, fetch_tuple_sub, tuple_status);
- }
-
- #endif
-
-
-
- void
- fetchrow_arrayref(sth)
- SV * sth
- ALIAS:
- fetch = 1
- CODE:
- D_imp_sth(sth);
- AV *av;
- if (0) ix = ix; /* avoid unused variable warning */
- av = dbd_st_fetch(sth, imp_sth);
- ST(0) = (av) ? sv_2mortal(newRV((SV *)av)) : &PL_sv_undef;
-
-
- void
- fetchrow_array(sth)
- SV * sth
- ALIAS:
- fetchrow = 1
- PPCODE:
- D_imp_sth(sth);
- AV *av;
- av = dbd_st_fetch(sth, imp_sth);
- if (av) {
- int i;
- int num_fields = AvFILL(av)+1;
- EXTEND(sp, num_fields);
- for(i=0; i < num_fields; ++i) {
- PUSHs(AvARRAY(av)[i]);
- }
- if (0) ix = ix; /* avoid unused variable warning */
- }
-
-
- void
- fetchall_arrayref(sth, slice=&PL_sv_undef, batch_row_count=&PL_sv_undef)
- SV * sth
- SV * slice
- SV * batch_row_count
- CODE:
- if (SvOK(slice)) { /* fallback to perl implementation */
- ST(0) = dbixst_bounce_method("DBD::~DRIVER~::st::SUPER::fetchall_arrayref", 3);
- }
- else {
- ST(0) = dbdxst_fetchall_arrayref(sth, slice, batch_row_count);
- }
-
-
- void
- finish(sth)
- SV * sth
- CODE:
- D_imp_sth(sth);
- D_imp_dbh_from_sth;
- if (!DBIc_ACTIVE(imp_sth)) {
- /* No active statement to finish */
- XSRETURN_YES;
- }
- if (!DBIc_ACTIVE(imp_dbh)) {
- /* Either an explicit disconnect() or global destruction */
- /* has disconnected us from the database. Finish is meaningless */
- DBIc_ACTIVE_off(imp_sth);
- XSRETURN_YES;
- }
- #ifdef dbd_st_finish3
- ST(0) = dbd_st_finish3(sth, imp_sth, 0) ? &sv_yes : &sv_no;
- #else
- ST(0) = dbd_st_finish(sth, imp_sth) ? &sv_yes : &sv_no;
- #endif
-
-
- void
- blob_read(sth, field, offset, len, destrv=Nullsv, destoffset=0)
- SV * sth
- int field
- long offset
- long len
- SV * destrv
- long destoffset
- CODE:
- {
- D_imp_sth(sth);
- if (!destrv)
- destrv = sv_2mortal(newRV(sv_2mortal(newSV(0))));
- if (dbd_st_blob_read(sth, imp_sth, field, offset, len, destrv, destoffset))
- ST(0) = SvRV(destrv);
- else ST(0) = &PL_sv_undef;
- }
-
-
- void
- STORE(sth, keysv, valuesv)
- SV * sth
- SV * keysv
- SV * valuesv
- CODE:
- D_imp_sth(sth);
- if (SvGMAGICAL(valuesv))
- mg_get(valuesv);
- ST(0) = &sv_yes;
- if (!dbd_st_STORE_attrib(sth, imp_sth, keysv, valuesv))
- if (!DBIc_DBISTATE(imp_sth)->set_attr(sth, keysv, valuesv))
- ST(0) = &sv_no;
-
-
- # FETCH renamed and ALIAS'd to avoid case clash on VMS :-(
- void
- FETCH_attrib(sth, keysv)
- SV * sth
- SV * keysv
- ALIAS:
- FETCH = 1
- CODE:
- D_imp_sth(sth);
- SV *valuesv;
- if (0) ix = ix; /* avoid unused variable warning */
- valuesv = dbd_st_FETCH_attrib(sth, imp_sth, keysv);
- if (!valuesv)
- valuesv = DBIc_DBISTATE(imp_sth)->get_attr(sth, keysv);
- ST(0) = valuesv; /* dbd_st_FETCH_attrib did sv_2mortal */
-
-
- void
- DESTROY(sth)
- SV * sth
- PPCODE:
- D_imp_sth(sth);
- ST(0) = &sv_yes;
- if (!DBIc_IMPSET(imp_sth)) { /* was never fully set up */
- STRLEN lna;
- if (DBIc_WARN(imp_sth) && !dirty && DBIc_DBISTATE(imp_sth)->debug >= 2)
- PerlIO_printf(DBIc_LOGPIO(imp_sth),
- " DESTROY for %s ignored - handle not initialised\n",
- SvPV(sth,lna));
- }
- else {
- if (DBIc_IADESTROY(imp_sth)) { /* want's ineffective destroy */
- DBIc_ACTIVE_off(imp_sth);
- }
- if (DBIc_ACTIVE(imp_sth)) {
- D_imp_dbh_from_sth;
- if (!dirty && DBIc_ACTIVE(imp_dbh)) {
- #ifdef dbd_st_finish3
- dbd_st_finish3(sth, imp_sth, 1);
- #else
- dbd_st_finish(sth, imp_sth);
- #endif
- }
- else {
- DBIc_ACTIVE_off(imp_sth);
- }
- }
- dbd_st_destroy(sth, imp_sth);
- }
-
- # end of ~DRIVER~.xst
-