Annotation of embedaddon/php/ext/pdo_odbc/odbc_driver.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | PHP Version 5                                                        |
                      4:   +----------------------------------------------------------------------+
                      5:   | Copyright (c) 1997-2012 The PHP Group                                |
                      6:   +----------------------------------------------------------------------+
                      7:   | This source file is subject to version 3.0 of the PHP license,       |
                      8:   | that is bundled with this package in the file LICENSE, and is        |
                      9:   | available through the world-wide-web at the following url:           |
                     10:   | http://www.php.net/license/3_0.txt.                                  |
                     11:   | If you did not receive a copy of the PHP license and are unable to   |
                     12:   | obtain it through the world-wide-web, please send a note to          |
                     13:   | license@php.net so we can mail you a copy immediately.               |
                     14:   +----------------------------------------------------------------------+
                     15:   | Author: Wez Furlong <wez@php.net>                                    |
                     16:   +----------------------------------------------------------------------+
                     17: */
                     18: 
                     19: /* $Id: odbc_driver.c 321634 2012-01-01 13:15:04Z felipe $ */
                     20: 
                     21: #ifdef HAVE_CONFIG_H
                     22: #include "config.h"
                     23: #endif
                     24: 
                     25: #include "php.h"
                     26: #include "php_ini.h"
                     27: #include "ext/standard/info.h"
                     28: #include "pdo/php_pdo.h"
                     29: #include "pdo/php_pdo_driver.h"
                     30: #include "php_pdo_odbc.h"
                     31: #include "php_pdo_odbc_int.h"
                     32: #include "zend_exceptions.h"
                     33: 
                     34: static int pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC)
                     35: {
                     36:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
                     37:        pdo_odbc_errinfo *einfo = &H->einfo;
                     38:        pdo_odbc_stmt *S = NULL;
                     39:        char *message = NULL;
                     40: 
                     41:        if (stmt) {
                     42:                S = (pdo_odbc_stmt*)stmt->driver_data;
                     43:                einfo = &S->einfo;
                     44:        }
                     45: 
                     46:        spprintf(&message, 0, "%s (%s[%ld] at %s:%d)",
                     47:                                einfo->last_err_msg,
                     48:                                einfo->what, einfo->last_error,
                     49:                                einfo->file, einfo->line);
                     50: 
                     51:        add_next_index_long(info, einfo->last_error);
                     52:        add_next_index_string(info, message, 0);
                     53:        add_next_index_string(info, einfo->last_state, 1);
                     54: 
                     55:        return 1;
                     56: }
                     57: 
                     58: 
                     59: void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line TSRMLS_DC) /* {{{ */
                     60: {
                     61:        SQLRETURN rc;
                     62:        SQLSMALLINT     errmsgsize = 0;
                     63:        SQLHANDLE eh;
                     64:        SQLSMALLINT htype, recno = 1;
                     65:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
                     66:        pdo_odbc_errinfo *einfo = &H->einfo;
                     67:        pdo_odbc_stmt *S = NULL;
                     68:        pdo_error_type *pdo_err = &dbh->error_code;
                     69: 
                     70:        if (stmt) {
                     71:                S = (pdo_odbc_stmt*)stmt->driver_data;
                     72: 
                     73:                einfo = &S->einfo;
                     74:                pdo_err = &stmt->error_code;
                     75:        }
                     76: 
                     77:        if (statement == SQL_NULL_HSTMT && S) {
                     78:                statement = S->stmt;
                     79:        }
                     80: 
                     81:        if (statement) {
                     82:                htype = SQL_HANDLE_STMT;
                     83:                eh = statement;
                     84:        } else if (H->dbc) {
                     85:                htype = SQL_HANDLE_DBC;
                     86:                eh = H->dbc;
                     87:        } else {
                     88:                htype = SQL_HANDLE_ENV;
                     89:                eh = H->env;
                     90:        }
                     91: 
                     92:        rc = SQLGetDiagRec(htype, eh, recno++, einfo->last_state, &einfo->last_error,
                     93:                        einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize);
                     94: 
                     95:        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                     96:                errmsgsize = 0;
                     97:        }
                     98: 
                     99:        einfo->last_err_msg[errmsgsize] = '\0';
                    100:        einfo->file = file;
                    101:        einfo->line = line;
                    102:        einfo->what = what;
                    103: 
                    104:        strcpy(*pdo_err, einfo->last_state);
                    105: /* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */
                    106:        if (!dbh->methods) {
                    107:                zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error TSRMLS_CC, "SQLSTATE[%s] %s: %d %s",
                    108:                                *pdo_err, what, einfo->last_error, einfo->last_err_msg);
                    109:        }
                    110: 
                    111:        /* just like a cursor, once you start pulling, you need to keep
                    112:         * going until the end; SQL Server (at least) will mess with the
                    113:         * actual cursor state if you don't finish retrieving all the
                    114:         * diagnostic records (which can be generated by PRINT statements
                    115:         * in the query, for instance). */
                    116:        while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
                    117:                char discard_state[5];
                    118:                char discard_buf[1024];
                    119:                SQLINTEGER code;
                    120:                rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code,
                    121:                                discard_buf, sizeof(discard_buf)-1, &errmsgsize);
                    122:        }
                    123: 
                    124: }
                    125: /* }}} */
                    126: 
                    127: static int odbc_handle_closer(pdo_dbh_t *dbh TSRMLS_DC)
                    128: {
                    129:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
                    130: 
                    131:        if (H->dbc != SQL_NULL_HANDLE) {
                    132:                SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
                    133:                SQLDisconnect(H->dbc);
                    134:                SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);
                    135:                H->dbc = NULL;
                    136:        }
                    137:        SQLFreeHandle(SQL_HANDLE_ENV, H->env);
                    138:        H->env = NULL;
                    139:        pefree(H, dbh->is_persistent);
                    140:        dbh->driver_data = NULL;
                    141: 
                    142:        return 0;
                    143: }
                    144: 
                    145: static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
                    146: {
                    147:        RETCODE rc;
                    148:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
                    149:        pdo_odbc_stmt *S = ecalloc(1, sizeof(*S));
                    150:        enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;
                    151:        int ret;
                    152:        char *nsql = NULL;
                    153:        int nsql_len = 0;
                    154: 
                    155:        S->H = H;
                    156:        S->assume_utf8 = H->assume_utf8;
                    157: 
                    158:        /* before we prepare, we need to peek at the query; if it uses named parameters,
                    159:         * we want PDO to rewrite them for us */
                    160:        stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
                    161:        ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC);
                    162:        
                    163:        if (ret == 1) {
                    164:                /* query was re-written */
                    165:                sql = nsql;
                    166:        } else if (ret == -1) {
                    167:                /* couldn't grok it */
                    168:                strcpy(dbh->error_code, stmt->error_code);
                    169:                efree(S);
                    170:                return 0;
                    171:        }
                    172:        
                    173:        rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);
                    174: 
                    175:        if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {
                    176:                efree(S);
                    177:                if (nsql) {
                    178:                        efree(nsql);
                    179:                }
                    180:                pdo_odbc_drv_error("SQLAllocStmt");
                    181:                return 0;
                    182:        }
                    183: 
                    184:        cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY TSRMLS_CC);
                    185:        if (cursor_type != PDO_CURSOR_FWDONLY) {
                    186:                rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0);
                    187:                if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                    188:                        pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE");
                    189:                        SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
                    190:                        if (nsql) {
                    191:                                efree(nsql);
                    192:                        }
                    193:                        return 0;
                    194:                }
                    195:        }
                    196:        
                    197:        rc = SQLPrepare(S->stmt, (char*)sql, SQL_NTS);
                    198:        if (nsql) {
                    199:                efree(nsql);
                    200:        }
                    201: 
                    202:        stmt->driver_data = S;
                    203:        stmt->methods = &odbc_stmt_methods;
                    204: 
                    205:        if (rc != SQL_SUCCESS) {
                    206:                pdo_odbc_stmt_error("SQLPrepare");
                    207:         if (rc != SQL_SUCCESS_WITH_INFO) {
                    208:             /* clone error information into the db handle */
                    209:             strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);
                    210:             H->einfo.file = S->einfo.file;
                    211:             H->einfo.line = S->einfo.line;
                    212:             H->einfo.what = S->einfo.what;
                    213:             strcpy(dbh->error_code, stmt->error_code);
                    214:         }
                    215:        }
                    216: 
                    217:        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                    218:                return 0;
                    219:        }
                    220:        return 1;
                    221: }
                    222: 
                    223: static long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
                    224: {
                    225:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
                    226:        RETCODE rc;
                    227:        SQLLEN row_count = -1;
                    228:        PDO_ODBC_HSTMT  stmt;
                    229:        
                    230:        rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);
                    231:        if (rc != SQL_SUCCESS) {
                    232:                pdo_odbc_drv_error("SQLAllocHandle: STMT");
                    233:                return -1;
                    234:        }
                    235: 
                    236:        rc = SQLExecDirect(stmt, (char *)sql, sql_len);
                    237: 
                    238:        if (rc == SQL_NO_DATA) {
                    239:                /* If SQLExecDirect executes a searched update or delete statement that
                    240:                 * does not affect any rows at the data source, the call to
                    241:                 * SQLExecDirect returns SQL_NO_DATA. */
                    242:                row_count = 0;
                    243:                goto out;
                    244:        }
                    245: 
                    246:        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                    247:                pdo_odbc_doer_error("SQLExecDirect");
                    248:                goto out;
                    249:        }
                    250: 
                    251:        rc = SQLRowCount(stmt, &row_count);
                    252:        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                    253:                pdo_odbc_doer_error("SQLRowCount");
                    254:                goto out;
                    255:        }
                    256:        if (row_count == -1) {
                    257:                row_count = 0;
                    258:        }
                    259: out:
                    260:        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                    261:        return row_count;
                    262: }
                    263: 
                    264: static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type param_type  TSRMLS_DC)
                    265: {
                    266:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
                    267:        /* TODO: figure it out */
                    268:        return 0;
                    269: }
                    270: 
                    271: static int odbc_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
                    272: {
                    273:        if (dbh->auto_commit) {
                    274:                /* we need to disable auto-commit now, to be able to initiate a transaction */
                    275:                RETCODE rc;
                    276:                pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
                    277: 
                    278:                rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);
                    279:                if (rc != SQL_SUCCESS) {
                    280:                        pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF");
                    281:                        return 0;
                    282:                }
                    283:        }
                    284:        return 1;
                    285: }
                    286: 
                    287: static int odbc_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
                    288: {
                    289:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
                    290:        RETCODE rc;
                    291: 
                    292:        rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);
                    293: 
                    294:        if (rc != SQL_SUCCESS) {
                    295:                pdo_odbc_drv_error("SQLEndTran: Commit");
                    296: 
                    297:                if (rc != SQL_SUCCESS_WITH_INFO) {
                    298:                        return 0;
                    299:                }
                    300:        }
                    301: 
                    302:        if (dbh->auto_commit) {
                    303:                /* turn auto-commit back on again */
                    304:                rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
                    305:                if (rc != SQL_SUCCESS) {
                    306:                        pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
                    307:                        return 0;
                    308:                }
                    309:        }
                    310:        return 1;
                    311: }
                    312: 
                    313: static int odbc_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
                    314: {
                    315:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
                    316:        RETCODE rc;
                    317: 
                    318:        rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
                    319: 
                    320:        if (rc != SQL_SUCCESS) {
                    321:                pdo_odbc_drv_error("SQLEndTran: Rollback");
                    322: 
                    323:                if (rc != SQL_SUCCESS_WITH_INFO) {
                    324:                        return 0;
                    325:                }
                    326:        }
                    327:        if (dbh->auto_commit && H->dbc) {
                    328:                /* turn auto-commit back on again */
                    329:                rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
                    330:                if (rc != SQL_SUCCESS) {
                    331:                        pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
                    332:                        return 0;
                    333:                }
                    334:        }
                    335: 
                    336:        return 1;
                    337: }
                    338: 
                    339: static int odbc_handle_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
                    340: {
                    341:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
                    342:        switch (attr) {
                    343:                case PDO_ODBC_ATTR_ASSUME_UTF8:
                    344:                        H->assume_utf8 = zval_is_true(val);
                    345:                        return 1;
                    346:                default:
                    347:                        strcpy(H->einfo.last_err_msg, "Unknown Attribute");
                    348:                        H->einfo.what = "setAttribute";
                    349:                        strcpy(H->einfo.last_state, "IM001");
                    350:                        return -1;
                    351:        }
                    352: }
                    353: 
                    354: static int odbc_handle_get_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
                    355: {
                    356:        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
                    357:        switch (attr) {
                    358:                case PDO_ATTR_CLIENT_VERSION:
                    359:                        ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE, 1);
                    360:                        return 1;
                    361: 
                    362:                case PDO_ATTR_SERVER_VERSION:
                    363:                case PDO_ATTR_PREFETCH:
                    364:                case PDO_ATTR_TIMEOUT:
                    365:                case PDO_ATTR_SERVER_INFO:
                    366:                case PDO_ATTR_CONNECTION_STATUS:
                    367:                        break;
                    368:                case PDO_ODBC_ATTR_ASSUME_UTF8:
                    369:                        ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0);
                    370:                        return 1;
                    371: 
                    372:        }
                    373:        return 0;
                    374: }
                    375: 
                    376: static struct pdo_dbh_methods odbc_methods = {
                    377:        odbc_handle_closer,
                    378:        odbc_handle_preparer,
                    379:        odbc_handle_doer,
                    380:        odbc_handle_quoter,
                    381:        odbc_handle_begin,
                    382:        odbc_handle_commit,
                    383:        odbc_handle_rollback,
                    384:        odbc_handle_set_attr,
                    385:        NULL,   /* last id */
                    386:        pdo_odbc_fetch_error_func,
                    387:        odbc_handle_get_attr,   /* get attr */
                    388:        NULL,   /* check_liveness */
                    389: };
                    390: 
                    391: static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
                    392: {
                    393:        pdo_odbc_db_handle *H;
                    394:        RETCODE rc;
                    395:        int use_direct = 0;
                    396:        SQLUINTEGER cursor_lib;
                    397: 
                    398:        H = pecalloc(1, sizeof(*H), dbh->is_persistent);
                    399: 
                    400:        dbh->driver_data = H;
                    401:        
                    402:        SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);
                    403:        rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
                    404: 
                    405:        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                    406:                pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3");
                    407:                goto fail;
                    408:        }
                    409: 
                    410: #ifdef SQL_ATTR_CONNECTION_POOLING
                    411:        if (pdo_odbc_pool_on != SQL_CP_OFF) {
                    412:                rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0);
                    413:                if (rc != SQL_SUCCESS) {
                    414:                        pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH");
                    415:                        goto fail;
                    416:                }
                    417:        }
                    418: #endif
                    419:        
                    420:        rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);
                    421:        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                    422:                pdo_odbc_drv_error("SQLAllocHandle (DBC)");
                    423:                goto fail;
                    424:        }
                    425: 
                    426:        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT,
                    427:                (SQLPOINTER)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER);
                    428:        if (rc != SQL_SUCCESS) {
                    429:                pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT");
                    430:                goto fail;
                    431:        }
                    432: 
                    433:        /* set up the cursor library, if needed, or if configured explicitly */
                    434:        cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED TSRMLS_CC);
                    435:        rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER);
                    436:        if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {
                    437:                pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS");
                    438:                goto fail;
                    439:        }
                    440: 
                    441:        if (strchr(dbh->data_source, ';')) {
                    442:                char dsnbuf[1024];
                    443:                short dsnbuflen;
                    444: 
                    445:                use_direct = 1;
                    446: 
                    447:                /* Force UID and PWD to be set in the DSN */
                    448:                if (dbh->username && *dbh->username && !strstr(dbh->data_source, "uid")
                    449:                                && !strstr(dbh->data_source, "UID")) {
                    450:                        char *dsn;
                    451:                        spprintf(&dsn, 0, "%s;UID=%s;PWD=%s", dbh->data_source, dbh->username, dbh->password);
                    452:                        pefree((char*)dbh->data_source, dbh->is_persistent);
                    453:                        dbh->data_source = dsn;
                    454:                }
                    455: 
                    456:                rc = SQLDriverConnect(H->dbc, NULL, (char*)dbh->data_source, strlen(dbh->data_source),
                    457:                                dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT);
                    458:        }
                    459:        if (!use_direct) {
                    460:                rc = SQLConnect(H->dbc, (char*)dbh->data_source, SQL_NTS, dbh->username, SQL_NTS, dbh->password, SQL_NTS);
                    461:        }
                    462: 
                    463:        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                    464:                pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect");
                    465:                goto fail;
                    466:        }
                    467: 
                    468:        /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */
                    469: 
                    470:        dbh->methods = &odbc_methods;
                    471:        dbh->alloc_own_columns = 1;
                    472:        
                    473:        return 1;
                    474: 
                    475: fail:
                    476:        dbh->methods = &odbc_methods;
                    477:        return 0;
                    478: }
                    479: /* }}} */
                    480: 
                    481: pdo_driver_t pdo_odbc_driver = {
                    482:        PDO_DRIVER_HEADER(odbc),
                    483:        pdo_odbc_handle_factory
                    484: };
                    485: 
                    486: /*
                    487:  * Local variables:
                    488:  * tab-width: 4
                    489:  * c-basic-offset: 4
                    490:  * End:
                    491:  * vim600: noet sw=4 ts=4 fdm=marker
                    492:  * vim<600: noet sw=4 ts=4
                    493:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>