/* iodbc Copyright 1995-2005 - FFE Software, Inc. */ /* Version 1.00 */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation, Version 2. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "iodbc.h" /* command types */ #define CMDGO 1 #define CMDEXIT 2 #define CMDRESET 3 #define CMDQUIT 4 #define CMDHELP 5 static LOOKUP cmdLookup[] = { { "go", CMDGO, "go - execute sql command" }, { "exit", CMDEXIT, "exit - exit iodbc" }, { "reset", CMDRESET, "reset - clear sql command" }, { "quit", CMDQUIT, "quit - quit iodbc" }, { "help", CMDHELP, "help [cmd] - show command help; help ? for conditions" }, { NULL, 0 } } ; int main(int argc, char **argv) { int exitCode ; exitCode = MainProcess(stdin, stdout, (UCHAR **) argv + 1) ; return exitCode ; } int MainProcess(FILE *pFileIn, FILE *pFileOut, UCHAR **pArgV) { static IO io ; static TOGGLE toggle ; static UCHAR buf[1024] ; static ODBC odbc ; UCHAR **pToggle ; UCHAR *pFileName ; BOOL oToggle ; io.pFileIn = pFileIn ; io.pFileOut = pFileOut ; strcpy(odbc.colSeparator, " ") ; odbc.colWidth = 80l ; odbc.connTimeout = odbc.queryTimeout = 8l ; odbc.firstConnect = TRUE ; odbc.errLevel = 16 ; io.pLine = buf ; io.maxLine = 1024 ; io.promptLines = TRUE ; io.echoLines = FALSE ; /* get toggle for output file */ if (pToggle = ToggleFind(pArgV, 'o')) { if (pFileName = ToggleArg(&pToggle)) { if (io.pFileOut = fopen(pFileName, "wt")) oToggle = TRUE ; else { fprintf(pFileOut, "Fatal - Unable to open %s\n", pFileName) ; return EXIT_PRIORERROR ; } } else { fprintf(pFileOut, "Fatal - Invalid value for toggle : %s\n", *(pToggle - 1)) ; return EXIT_PRIORERROR ; } } else oToggle = FALSE ; /* got toggle to turn off logon message? */ if (!ToggleFind(pArgV, 'z')) fprintf(io.pFileOut, "iodbc Ver. 1.00 Copyright 1995-2005 FFE Software, Inc.\n" "This is free software, and you are welcome to redistribute it under certain\n" "conditions; type 'help ?' for details.\n") ; odbc.exitCode = EXIT_PRIORERROR ; if (ProcessToggles(&odbc, &io, &toggle, pArgV) && ODBCStartup(io.pFileOut, &odbc)) { odbc.pBuffer = malloc(odbc.colWidth + 2) ; if (odbc.pBuffer) { /* if no query */ { io.saveLines = FALSE ; io.skipText = TRUE ; InputLine(&io) ; io.skipText = FALSE ; } io.saveLines = io.echoLines ; odbc.exitCode = 0 ; ProcessLines(&odbc, &io) ; } else AllocError(io.pFileOut) ; ODBCShutdown(io.pFileOut, &odbc) ; } if (oToggle) fclose(io.pFileOut) ; return odbc.exitCode ; } /* login to ODBC data source */ BOOL ODBCConnect(FILE *pOut, ODBC *pODBC, UCHAR *pDb) { UCHAR *pConn, connStr[256], *pDSN ; SWORD connOut ; RETCODE ret ; BOOL retFlag ; ODBCERROR errODBC ; pDSN = pODBC->pDSN ? pODBC->pDSN : (UCHAR *) "default" ; if (pODBC->driverConnect || pODBC->firstConnect) { pConn = connStr ; if (pODBC->pCONNECT) { pConn += sprintf(pConn, "%s", pODBC->pCONNECT) ; if (pODBC->pDSN) pConn += sprintf(pConn, ";DSN=%s", pODBC->pDSN) ; } else pConn += sprintf(pConn, "DSN=%s", pDSN) ; if (pODBC->pUID) pConn += sprintf(pConn, ";UID=%s", pODBC->pUID) ; /*if (pODBC->pPWD) */ pConn += sprintf(pConn, ";PWD=%s", pODBC->pPWD ? (char *) pODBC->pPWD : "") ; if (pDb) pConn += sprintf(pConn, ";DATABASE=%s", pDb) ; if (pODBC->pWSID) pConn += sprintf(pConn, ";WSID=%s", pODBC->pWSID) ; ret = SQLDriverConnect(pODBC->hDbc, NULL, connStr, SQL_NTS, connStr, 256, &connOut, SQL_DRIVER_NOPROMPT) ; retFlag = ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ; if (retFlag) { pODBC->firstConnect = FALSE ; pODBC->driverConnect = TRUE ; } else while (ODBCErrorFetch(pOut, pODBC, SQL_NULL_HSTMT, ret, &errODBC)) if (strcmp(errODBC.state, "IM001") || /* SQLDriverConnect support? */ pODBC->pCONNECT) ODBCErrorPrint(pOut, pODBC, &errODBC) ; else pODBC->driverConnect = FALSE ; if (ret == SQL_NO_DATA_FOUND && pODBC->pCONNECT) fprintf(pOut, "Fatal - connection failed on 'No Data Found' (missing attributes?)") ; } else retFlag = FALSE ; if (!retFlag && !pODBC->pCONNECT) { ret = SQLConnect(pODBC->hDbc, pDSN, SQL_NTS, pODBC->pUID, (SWORD) (pODBC->pUID ? SQL_NTS : 0), pODBC->pPWD, (SWORD) (pODBC->pPWD ? SQL_NTS : 0)) ; retFlag = ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ; if (retFlag) pODBC->firstConnect = pODBC->driverConnect = FALSE ; else ODBCError(pOut, pODBC, SQL_NULL_HSTMT, ret) ; } if (retFlag) { pODBC->moreResults = TestODBCFunction(pODBC, SQL_API_SQLMORERESULTS) ; pODBC->setConnectOption = TestODBCFunction(pODBC, SQL_API_SQLSETCONNECTOPTION) ; pODBC->setStmtOption = TestODBCFunction(pODBC, SQL_API_SQLSETSTMTOPTION) ; } return retFlag ; } /* startup ODBC */ BOOL ODBCStartup(FILE *pOut, ODBC *pODBC) { BOOL retFlag, dummy ; retFlag = FALSE ; if (SQLAllocEnv(&pODBC->hEnv) == SQL_SUCCESS) { if (SQLAllocConnect(pODBC->hEnv, &pODBC->hDbc) == SQL_SUCCESS) { pODBC->setConnectOption = TRUE ; SetODBCOption(pOut, pODBC, SQL_NULL_HSTMT, SQL_LOGIN_TIMEOUT, pODBC->connTimeout, &dummy) ; retFlag = ODBCConnect(pOut, pODBC, pODBC->pDB) ; if (!retFlag) SQLFreeConnect(pODBC->hDbc) ; } else AllocError(pOut) ; if (!retFlag) SQLFreeEnv(pODBC->hEnv) ; } else AllocError(pOut) ; return retFlag ; } /* shutdown ODBC */ BOOL ODBCShutdown(FILE *pOut, ODBC *pODBC) { SQLDisconnect(pODBC->hDbc) ; SQLFreeConnect(pODBC->hDbc) ; SQLFreeEnv(pODBC->hEnv) ; return TRUE ; } /* set ODBC connect or stmt option */ BOOL SetODBCOption(FILE *pOut, ODBC *pODBC, HSTMT hStmt, UWORD fOption, UDWORD vParam, BOOL *pOption) { RETCODE ret ; ODBCERROR errODBC ; if (hStmt) { if (!pODBC->setStmtOption) return *pOption = FALSE ; ret = SQLSetStmtOption(hStmt, fOption, vParam) ; } else { if (!pODBC->setConnectOption) return *pOption = FALSE ; ret = SQLSetConnectOption(pODBC->hDbc, fOption, vParam) ; } *pOption = TRUE ; while (ODBCErrorFetch(pOut, pODBC, hStmt, ret, &errODBC)) if (strcmp(errODBC.state, "IM001") && strcmp(errODBC.state, "S1092") && strcmp(errODBC.state, "S1C00")) { if (ret != SQL_SUCCESS_WITH_INFO) *pOption = FALSE ; ODBCErrorPrint(pOut, pODBC, &errODBC) ; } else *pOption = FALSE ; return *pOption ; } /* call SQLGetFunctions to test for existence of a function */ BOOL TestODBCFunction(ODBC *pODBC, UWORD fFunction) { UWORD exists ; return SQLGetFunctions(pODBC->hDbc, fFunction, &exists) == SQL_SUCCESS && exists == 1 ; } /* dump ODBC error */ BOOL ODBCError(FILE *pOut, ODBC *pODBC, HSTMT hStmt, RETCODE ret) { BOOL retFlag ; ODBCERROR errODBC ; retFlag = FALSE ; while (ODBCErrorFetch(pOut, pODBC, hStmt, ret, &errODBC)) { retFlag = TRUE ; ODBCErrorPrint(pOut, pODBC, &errODBC) ; } return retFlag ; } BOOL ODBCErrorFetch(FILE *pOut, ODBC *pODBC, HSTMT hStmt, RETCODE ret, ODBCERROR *pErr) { BOOL retFlag ; pErr->ret = ret ; retFlag = FALSE ; switch (ret) { case SQL_SUCCESS : case SQL_NO_DATA_FOUND : break ; case SQL_INVALID_HANDLE : strcpy(pErr->state, "******") ; strcpy(pErr->msg, "Unknown Error") ; pErr->nativeEr = 0 ; ODBCErrorPrint(pOut, pODBC, pErr) ; break ; default : ret = SQLError(pODBC->hEnv, pODBC->hDbc, hStmt, pErr->state, &pErr->nativeEr, pErr->msg, SQL_MAX_MESSAGE_LENGTH - 1, &pErr->msgSz) ; switch (ret) { case SQL_SUCCESS : case SQL_SUCCESS_WITH_INFO : retFlag = TRUE ; break ; case SQL_ERROR : pErr->ret = ret ; strcpy(pErr->state, "00000") ; strcpy(pErr->msg, "ODBC Failure") ; pErr->nativeEr = 0 ; ODBCErrorPrint(pOut, pODBC, pErr) ; break ; case SQL_NO_DATA_FOUND : break ; default : pErr->ret = SQL_ERROR ; strcpy(pErr->state, "00000") ; strcpy(pErr->msg, "Unknown Error") ; pErr->nativeEr = 0 ; ODBCErrorPrint(pOut, pODBC, pErr) ; break ; } break ; } return retFlag ; } /* dump ODBC error */ void ODBCErrorPrint(FILE *pOut, ODBC *pODBC, ODBCERROR *pErr) { long errLevel ; switch (pErr->ret) { case SQL_SUCCESS : case SQL_NO_DATA_FOUND : errLevel = 0 ; break ; case SQL_SUCCESS_WITH_INFO : errLevel = 1 ; break ; default : pODBC->exitCode = EXIT_PRIORERROR ; errLevel = 16 ; break ; } if (errLevel >= pODBC->errLevel) fprintf(pOut, "Msg %ld, Level %ld, State %s:\n%s\n", pErr->nativeEr, errLevel, pErr->state, pErr->msg) ; } void ExecSQL(ODBC *pODBC, IO *pIo) { HSTMT hStmt ; BOOL success, dummy ; SWORD numCols ; SDWORD numRows ; RETCODE ret ; if (pIo->pODBC && *SkipSpaces(pIo->pODBC)) { success = FALSE ; if (SQLAllocStmt(pODBC->hDbc, &hStmt) == SQL_SUCCESS) { SetODBCOption(pIo->pFileOut, pODBC, hStmt, SQL_QUERY_TIMEOUT, pODBC->queryTimeout, &dummy) ; ret = SQLExecDirect(hStmt, pIo->pODBC, SQL_NTS) ; if (ret == SQL_SUCCESS) success = TRUE ; else ODBCError(pIo->pFileOut, pODBC, hStmt, ret) ; } else { hStmt = SQL_NULL_HSTMT ; AllocError(pIo->pFileOut) ; } if (pIo->echoLines) fprintf(pIo->pFileOut, "%s", pIo->pDisplay) ; while (success) { /* if query, fetch results */ if (SQLNumResultCols(hStmt, &numCols) == SQL_SUCCESS && numCols) numRows = PrintQuery(pIo->pFileOut, pODBC, hStmt, numCols) ; else /* else get number of rows affected */ { numRows = 0l ; SQLRowCount(hStmt, &numRows) ; } fprintf(pIo->pFileOut, "\n(%ld rows affected)\n", numRows) ; if (pODBC->moreResults) { ret = SQLMoreResults(hStmt) ; success = ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ; ODBCError(pIo->pFileOut, pODBC, hStmt, ret) ; } else success = FALSE ; } if (hStmt != SQL_NULL_HSTMT) SQLFreeStmt(hStmt, SQL_DROP) ; } else fprintf(pIo->pFileOut, "empty sql\n") ; FreeSQL(pIo) ; } /* print query results */ SDWORD PrintQuery(FILE *pOut, ODBC *pODBC, HSTMT hStmt, SWORD numCols) { long colDisp ; SDWORD numRows ; UCHAR *pHeading, *pMarker, colName[32], *pText ; UDWORD prec ; SWORD i, len, scale, nullable, type ; int width, lenText ; BOOL leftJustify ; RETCODE ret ; numRows = 0l ; pODBC->colMax = numCols ; pODBC->pCol = malloc(numCols * sizeof (DESCRIBE)) ; if (pODBC->pCol) { /* build heading & markers */ pHeading = pMarker = NULL ; StringAttach(&pHeading, NULL, 0, pODBC->colSeparator) ; StringAttach(&pMarker, NULL, 0, pODBC->colSeparator) ; colDisp = 1 ; for (i = 0 ; i < numCols ; i++) { pText = NULL ; leftJustify = FALSE ; ret = SQLDescribeCol(hStmt, (UWORD) (i + 1), colName, 32, &len, &pODBC->pCol[i].sqlType, &prec, &scale, &nullable) ; if (ret == SQL_SUCCESS) { switch (pODBC->pCol[i].sqlType) { case SQL_INTEGER : case SQL_TINYINT : case SQL_SMALLINT : width = prec + 1 ; lenText = sizeof (long) ; type = SQL_C_LONG ; break ; case SQL_NUMERIC : case SQL_DECIMAL : width = prec + 2 ; lenText = width + 1 ; type = SQL_C_CHAR ; break ; case SQL_FLOAT : case SQL_REAL : case SQL_DOUBLE : width = 20 ; lenText = sizeof (double) ; type = SQL_C_DOUBLE ; break ; case SQL_DATE : width = 12 ; lenText = sizeof (DATE_STRUCT) ; type = SQL_C_DATE ; break ; case SQL_TIME : width = 8 ; lenText = sizeof (TIME_STRUCT) ; type = SQL_C_TIME ; break ; case SQL_TIMESTAMP : width = 20 ; lenText = sizeof (TIMESTAMP_STRUCT) ; type = SQL_C_TIMESTAMP ; break ; case SQL_BINARY : case SQL_VARBINARY : case SQL_LONGVARBINARY : width = prec * 2 ; lenText = width + 1 ; type = SQL_C_CHAR ; break ; case SQL_BIT : width = 1 ; lenText = width + 1 ; type = SQL_C_BIT ; break ; case SQL_BIGINT : width = prec + 1 ; lenText = sizeof (8) ; type = SQL_C_SBIGINT ; break ; case SQL_CHAR : case SQL_VARCHAR : case SQL_LONGVARCHAR : default : width = prec ; lenText = width + 1 ; type = SQL_C_CHAR ; leftJustify = TRUE ; break ; } if (lenText > pODBC->colWidth) lenText = pODBC->colWidth ; pText = malloc(lenText) ; SQLBindCol(hStmt, (UWORD) (i + 1), type, (PTR) pText, (SWORD) lenText, &pODBC->pCol[i].cbValue) ; if (width < (int) strlen(colName)) width = strlen(colName) ; if (width >= pODBC->colWidth) width = pODBC->colWidth - 1 ; sprintf(pODBC->pBuffer, "%-*.*s", width, width, colName) ; if (width + 1 + colDisp > pODBC->colWidth) { StringAttach(&pHeading, NULL, 0, "\n\t") ; StringAttach(&pMarker, NULL, 0, "\n\t") ; colDisp = 8 ; } StringAttach(&pHeading, pODBC->pBuffer, (SWORD) width, pODBC->colSeparator) ; memset(pODBC->pBuffer, '-', width) ; StringAttach(&pMarker, pODBC->pBuffer, (SWORD) width, pODBC->colSeparator) ; colDisp += width + 1 ; } else { ODBCError(pOut, pODBC, hStmt, ret) ; width = 0 ; pODBC->pCol[i].sqlType = 0 ; } pODBC->pCol[i].pData = pText ; pODBC->pCol[i].width = width ; pODBC->pCol[i].leftJustify = leftJustify ; } StringAttach(&pHeading, NULL, 0, "\n") ; StringAttach(&pMarker, NULL, 0, "\n") ; /* fetch and print data */ if (pHeading) fprintf(pOut, pHeading) ; if (pMarker) fprintf(pOut, pMarker) ; while ((ret = SQLFetch(hStmt)) == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { ODBCError(pOut, pODBC, hStmt, ret) ; PrintData(pOut, pODBC) ; ++numRows ; } if (ret != SQL_NO_DATA_FOUND) ODBCError(pOut, pODBC, hStmt, ret) ; for (i = 0 ; i < numCols ; i++) free(pODBC->pCol[i].pData) ; free(pHeading) ; free(pMarker) ; free(pODBC->pCol) ; } return numRows ; } /* format and print data for ODBC fetch */ void PrintData(FILE *pOut, ODBC *pODBC) { SWORD i ; long colDisp ; UCHAR *pText, work[256] ; BOOL leftJustify ; DATE_STRUCT *pDate ; TIME_STRUCT *pTime ; TIMESTAMP_STRUCT *pTimestamp ; struct tm tm ; fprintf(pOut, pODBC->colSeparator) ; for (colDisp = 1l, i = 0 ; i < pODBC->colMax ; ++i) { if (pODBC->pCol[i].width + 1 + colDisp > pODBC->colWidth) { fprintf(pOut, "\n\t") ; colDisp = 8 ; } if (pODBC->pCol[i].sqlType && pODBC->pCol[i].cbValue == SQL_NULL_DATA) { leftJustify = TRUE ; pText = "NULL" ; } else { leftJustify = pODBC->pCol[i].leftJustify ; switch (pODBC->pCol[i].sqlType) { case 0 : continue ; case SQL_INTEGER : case SQL_TINYINT : case SQL_SMALLINT : sprintf(pText = work, "%ld", *((long *) pODBC->pCol[i].pData)) ; break ; case SQL_FLOAT : case SQL_REAL : case SQL_DOUBLE : sprintf(pText = work, "%.6f", *((double *) pODBC->pCol[i].pData)) ; break ; case SQL_DATE : pDate = (DATE_STRUCT *) pODBC->pCol[i].pData ; tm.tm_year = (pDate->year > 1900) ? pDate->year - 1900 : pDate->year ; tm.tm_mon = pDate->month - 1 ; tm.tm_mday = pDate->day ; strftime(pText = work, 127, "%b %d %Y", &tm) ; break ; case SQL_TIME : pTime = (TIME_STRUCT *) pODBC->pCol[i].pData ; tm.tm_hour = pTime->hour ; tm.tm_min = pTime->minute ; tm.tm_sec = pTime->second ; strftime(pText = work, 127, "%I:%M%p", &tm) ; break ; case SQL_TIMESTAMP : pTimestamp = (TIMESTAMP_STRUCT *) pODBC->pCol[i].pData ; tm.tm_hour = pTimestamp->hour ; tm.tm_min = pTimestamp->minute ; tm.tm_sec = pTimestamp->second ; tm.tm_year = (pTimestamp->year > 1900) ? pTimestamp->year - 1900 : pTimestamp->year ; tm.tm_mon = pTimestamp->month - 1 ; tm.tm_mday = pTimestamp->day ; strftime(pText = work, 127, "%b %d %Y %I:%M%p", &tm) ; break ; case SQL_BIT : sprintf(pText = work, "%d", *((UCHAR *) pODBC->pCol[i].pData) != 0) ; break ; case SQL_BINARY : case SQL_VARBINARY : case SQL_LONGVARBINARY : case SQL_BIGINT : case SQL_CHAR : case SQL_VARCHAR : case SQL_LONGVARCHAR : case SQL_NUMERIC : case SQL_DECIMAL : default : pText = pODBC->pCol[i].pData ; break ; } } fprintf(pOut, leftJustify ? "%-*.*s%s" : "%*.*s%s", pODBC->pCol[i].width, pODBC->pCol[i].width, pText, pODBC->colSeparator) ; colDisp += pODBC->pCol[i].width + 1 ; } fprintf(pOut, "\n") ; } void AllocError(FILE *pOut) { fprintf(pOut, "Msg 0, Level 256, State S1000:\nAllocation Error\n") ; } void FreeSQL(IO *pIo) { if (pIo->pODBC) { free(pIo->pODBC) ; pIo->pODBC = NULL ; } if (pIo->pDisplay) { free(pIo->pDisplay) ; pIo->pDisplay = NULL ; } } /* process toggles */ BOOL ProcessToggles(ODBC *pODBC, IO *pIo, TOGGLE *pToggle, UCHAR **pArgV) { BOOL retFlag ; UCHAR *pArg, *pValue ; long value ; retFlag = TRUE ; while (retFlag && (pArg = *pArgV++)) switch (*pArg) { case '/' : case '-' : /* toggle */ switch (*(pArg + 1)) { case 'd' : /* USE database (special for SQL SERVER) */ pValue = ToggleArg(&pArgV) ; if (pValue) pODBC->pDB = pValue ; else ToggleError(pIo->pFileOut, pArg) ; break ; case 'H' : /* workstation name (special for SQL SERVER) */ pValue = ToggleArg(&pArgV) ; if (pValue) pODBC->pWSID = pValue ; else ToggleError(pIo->pFileOut, pArg) ; break ; case 'U' : /* user id (UID) */ pValue = ToggleArg(&pArgV) ; if (pValue) pODBC->pUID = pValue ; else ToggleError(pIo->pFileOut, pArg) ; break ; case 'P' : /* user password (PWD) */ pValue = ToggleArg(&pArgV) ; if (pValue) pODBC->pPWD = pValue ; break ; case 'S' : /* data source name (servername) (DSN) */ pValue = ToggleArg(&pArgV) ; if (pValue) pODBC->pDSN = pValue ; else ToggleError(pIo->pFileOut, pArg) ; break ; case 'C' : /* ODBC connect string */ pValue = ToggleArg(&pArgV) ; if (pValue) pODBC->pCONNECT = pValue ; else ToggleError(pIo->pFileOut, pArg) ; break ; case 'e' : /* echo SQL input */ pIo->echoLines = TRUE ; break ; case 'n' : /* suppress prompt */ pIo->promptLines = FALSE ; break ; case 's' : /* column separator on formatted report */ pValue = ToggleArg(&pArgV) ; if (pValue) *pODBC->colSeparator = *pValue ; else ToggleError(pIo->pFileOut, pArg) ; break ; case 'z' : /* turn off log-in message */ break ; /* already processed */ case 'm' : /* error level */ if (ToggleLong(&pArgV, &value)) pODBC->errLevel = value ; else ToggleError(pIo->pFileOut, pArg) ; break ; case 't' : /* query timeout */ if (ToggleLong(&pArgV, &value)) pODBC->queryTimeout = value ; else ToggleError(pIo->pFileOut, pArg) ; break ; case 'l' : /* login timeout */ if (ToggleLong(&pArgV, &value)) pODBC->connTimeout = value ; else ToggleError(pIo->pFileOut, pArg) ; break ; case 'w' : /* column width */ if (ToggleLong(&pArgV, &value) && value > 8) pODBC->colWidth = value ; else ToggleError(pIo->pFileOut, pArg) ; break ; case '?' : /* dump toggles */ fprintf(pIo->pFileOut, "usage: iodbc [-U login id] [-e echo input]\n" "\t[-n remove numbering]\n" "\t[-w columnwidth] [-s colseparator]\n" "\t[-m errorlevel] [-t query timeout] [-l login timeout]\n" "\t[-H hostname] [-P password]\n" "\t[-S data source name] [-d use database name]\n" "\t[-C odbc connect string]\n" "\t[-i inputfile] [-o outputfile]\n" "\t[-z suppress log-on message]\n" "\t[-? show syntax summary (this screen)]\n") ; retFlag = FALSE ; break ; case 'i' : /* inputfile */ pValue = ToggleArg(&pArgV) ; if (pValue) { if (!(pIo->pFileIn = fopen(pValue, "rt"))) { fprintf(pIo->pFileOut, "unable to open '%s'\n", pValue) ; retFlag = FALSE ; } } else ToggleError(pIo->pFileOut, pArg) ; break ; case 'o' : /* outputfile */ ToggleArg(&pArgV) ; break ; /* already processed */ /* isql toggles not supported */ case 'L' : /* later */ case 'p' : fprintf(pIo->pFileOut, "Unsupported command line toggle : %s (ignored)\n", pArg) ; break ; case 'q' : /* to do */ case 'Q' : /* to do */ case 'h' : /* later */ case 'r' : /* later */ case 'a' : /* later */ case 'c' : fprintf(pIo->pFileOut, "Unsupported command line toggle : %s (ignored)\n", pArg) ; ToggleArg(&pArgV) ; break ; default : fprintf(pIo->pFileOut, "Fatal - Unrecognized command line toggle : %s\n", pArg) ; retFlag = FALSE ; break ; } break ; case '\0' : break ; default : fprintf(pIo->pFileOut, "Fatal - Invalid command line argument : %s\n", pArg) ; retFlag = FALSE ; break ; } return retFlag ; } /* find specific toggle */ /* return NULL if not found */ UCHAR **ToggleFind(UCHAR **pArgV, UCHAR toggleChar) { while (*pArgV && ((**pArgV != '/' && **pArgV != '-') || *(*pArgV + 1) != toggleChar)) ++pArgV ; return *pArgV ? pArgV + 1 : NULL ; } /* collect toggle argument */ /* return NULL if none */ UCHAR *ToggleArg(UCHAR ***ppArgV) { UCHAR *pArg ; /* examine toggle arg itself */ if (*(*(*ppArgV - 1) + 2)) /* argument adjacent */ pArg = *(*ppArgV - 1) + 2 ; else { pArg = **ppArgV ; if (pArg) { /* is argument a toggle? */ if (*pArg == '-' || *pArg == '/') pArg = NULL ; else (*ppArgV)++ ; } } return pArg ; } /* collect toggle argument - long */ BOOL ToggleLong(UCHAR ***ppArgV, long *pValue) { long value ; UCHAR *pArg, *pEnd ; pArg = ToggleArg(ppArgV) ; if (pArg) { value = strtol((char *) pArg, (char **) &pEnd, 10) ; if (errno == ERANGE || *SkipSpaces(pEnd)) pArg = NULL ; else *pValue = value ; } return pArg != NULL ; } /* dump toggle error */ void ToggleError(FILE *pOut, UCHAR *pArg) { fprintf(pOut, "Invalid value for toggle : %s (ignored)\n", pArg) ; } /* process input commands */ void ProcessLines(ODBC *pODBC, IO *pIo) { TOKEN token ; BOOL process ; int cmdId ; LOOKUP *pLook ; process = TRUE ; while (process && InputToken(pIo, &token)) if (token.type == TOKENSYMBOL && token.beginLine && (cmdId = TokenFind(&token, cmdLookup))) { pIo->lineNo = 0 ; switch (cmdId) { case CMDEXIT : process = FALSE ; break ; case CMDQUIT : process = FALSE ; break ; case CMDRESET : process = InputLine(pIo) ; FreeSQL(pIo) ; break ; case CMDGO : pIo->saveLines = FALSE ; ODBCSkip(pIo, pIo->pBegin) ; ExecSQL(pODBC, pIo) ; process = InputLine(pIo) ; ODBCSkip(pIo, NULL) ; break ; case CMDHELP : pIo->saveLines = FALSE ; ODBCSkip(pIo, pIo->pBegin) ; if (*(pIo->pCurrent = SkipSpaces(pIo->pCurrent))) { InputToken(pIo, &token) ; pLook = TokenLookup(&token, cmdLookup) ; if (pLook->type) fprintf(pIo->pFileOut, "%s\n", pLook->pHelp) ; else if (TokenTest(&token, "?")) { fprintf(pIo->pFileOut, "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation, Version 2.\n\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n\n" "You should have received a copy of the GNU General Public License\n" "along with this program; if not, write to the Free Software\n" "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n" "Contact: FFE Software, Inc., P.O. Box 1570, El Cerrito, CA 94530, USA\n" "Internet: support@firstsql.com\n") ; } else fprintf(pIo->pFileOut, "***unrecognized command\n") ; } else { fprintf(pIo->pFileOut, "iodbc - Interactive ODBC Application (type SQL commands; then GO to execute)\n" " help ? - redistribution conditions\n" " help cmd - help on specific command\n" " commands -") ; for (cmdId = 0 ; cmdLookup[cmdId].pText ; ++cmdId) fprintf(pIo->pFileOut, " %s", cmdLookup[cmdId].pText) ; fprintf(pIo->pFileOut, "\n") ; } process = InputLine(pIo) ; ODBCSkip(pIo, NULL) ; break ; } } FreeSQL(pIo) ; } /* test token for value */ BOOL TokenTest(TOKEN *pToken, UCHAR *pTest) { #ifdef unixODBC return !strncasecmp(pTest, pToken->pText, pToken->lenText) && pToken->lenText == (int) strlen(pTest) ; #else return !memicmp(pTest, pToken->pText, pToken->lenText) && pToken->lenText == (int) strlen(pTest) ; #endif } /* look-up token in table */ LOOKUP *TokenLookup(TOKEN *pToken, LOOKUP *pLook) { while (pLook->pText && !TokenTest(pToken, pLook->pText)) ++pLook ; return pLook ; } int TokenFind(TOKEN *pToken, LOOKUP *pLook) { return TokenLookup(pToken, pLook)->type ; } /* parse input line for token (also skip over comments) */ BOOL InputToken(IO *pIo, TOKEN *pToken) { int c ; SWORD depth ; BOOL retFlag ; retFlag = TRUE ; pToken->beginLine = pIo->pCurrent == pIo->pLine ; /* slide over any whitespace */ while (retFlag && !*(pIo->pCurrent = SkipSpaces(pIo->pCurrent))) { pToken->beginLine = TRUE ; retFlag = InputLine(pIo) ; } if (retFlag) { pToken->pText = pIo->pCurrent ; c = *pIo->pCurrent ; if (c == '_' || isalpha(c)) { pToken->type = TOKENSYMBOL ; do c = *++pIo->pCurrent ; while (c == '_' || isalnum(c)) ; } else if (isdigit(c)) { pToken->type = TOKENINTEGER ; do c = *++pIo->pCurrent ; while (isdigit(c)) ; } else switch (c) { case '\'' : do ++pIo->pCurrent ; while ((pIo->pCurrent = strchr(pIo->pCurrent, '\'')) && *++pIo->pCurrent == '\'') ; if (pIo->pCurrent) pToken->type = TOKENQUOTED ; else { pToken->type = TOKENQUOTER ; pIo->pCurrent = pIo->pLine + strlen(pIo->pLine) ; } break ; case '/' : if (*++pIo->pCurrent == '*') { /* scan over nested comments */ /* save previous and start skipping */ retFlag = ODBCSkip(pIo, pIo->pCurrent - 1) ; ++pIo->pCurrent ; pToken->beginLine = FALSE ; for (depth = 1 ; retFlag && depth ;) switch (*pIo->pCurrent) { case '/' : if (*++pIo->pCurrent == '*') { ++depth ; ++pIo->pCurrent ; } break ; case '*' : if (*++pIo->pCurrent == '/') { --depth ; ++pIo->pCurrent ; } break ; case '\0' : retFlag = InputLine(pIo) ; break ; default : ++pIo->pCurrent ; break ; } ODBCSkip(pIo, NULL) ; /* turn off text skipping */ if (retFlag) retFlag = InputToken(pIo, pToken) ; } else pToken->type = TOKENDELIM ; break ; default : pToken->type = TOKENDELIM ; ++pIo->pCurrent ; break ; } if (retFlag) pToken->lenText = pIo->pCurrent - pToken->pText ; } return retFlag ; } /* read command line */ BOOL InputLine(IO *pIo) { BOOL retFlag ; SWORD sizeInput ; /* save previous line if needed */ retFlag = (!pIo->saveLines || StringAttach(&pIo->pDisplay, pIo->pLine, (SWORD) strlen(pIo->pLine), "\n")) && ODBCSave(pIo, pIo->pCurrent) ; if (retFlag) { if (pIo->promptLines) fprintf(pIo->pFileOut, "%d> ", ++pIo->lineNo) ; retFlag = fgets(pIo->pLine, pIo->maxLine, pIo->pFileIn) != NULL ; if (retFlag) { pIo->saveLines = pIo->echoLines ; sizeInput = strlen(pIo->pLine) ; pIo->pBegin = pIo->pCurrent = pIo->pLine ; /* remove terminating \n if any */ if (sizeInput && *(pIo->pLine + sizeInput - 1) == '\n') *(pIo->pLine + sizeInput - 1) = '\0' ; } } else AllocError(pIo->pFileOut) ; return retFlag ; } /* start/end skip text for ODBC */ BOOL ODBCSkip(IO *pIo, UCHAR *pCurrent) { BOOL retFlag ; if (pCurrent) { /* start skipping, save previous */ retFlag = pCurrent == pIo->pBegin || ODBCSave(pIo, pCurrent) ; pIo->skipText = TRUE ; if (!retFlag) AllocError(pIo->pFileOut) ; } else { /* end skipping */ retFlag = TRUE ; if (pIo->skipText) { pIo->pBegin = pIo->pCurrent ; pIo->skipText = FALSE ; } } return retFlag ; } /* save non-comment text for ODBC */ BOOL ODBCSave(IO *pIo, UCHAR *pCurrent) { return pIo->skipText || StringAttach(&pIo->pODBC, pIo->pBegin, (SWORD) (pCurrent - pIo->pBegin), " ") ; } /* scan over spaces (whitespace) */ UCHAR *SkipSpaces(UCHAR *pScan) { while (isspace(*pScan)) ++pScan ; return pScan ; } /* extend allocated string with characters + separator string */ BOOL StringAttach(UCHAR **ppStr, UCHAR *pStr, SWORD len, UCHAR *pSeparator) { SWORD sizeOld, sizeSeparator ; sizeSeparator = strlen(pSeparator) ; if (*ppStr) { sizeOld = strlen(*ppStr) ; *ppStr = realloc(*ppStr, sizeOld + len + sizeSeparator + 1) ; } else { sizeOld = 0 ; *ppStr = malloc(len + sizeSeparator + 1) ; } if (*ppStr) { memcpy(*ppStr + sizeOld, pStr, len) ; strcpy(*ppStr + sizeOld + len, pSeparator) ; } return *ppStr != NULL ; }