TEXTCOPY.CPP
/////////////////////////////////////////////////////////////////////////////// 
// 
//  FILE: textcopy.cpp 
//               
//      Text and image copy application 
// 
//  FUNCTIONS: 
// 
//      main() - Main application 
// 
//  COMMENTS: 
// 
/////////////////////////////////////////////////////////////////////////////// 
 
#include <afx.h>                // MFC  
#include <iostream.h>           // iostream 
#include <stdlib.h>             // C run-time 
 
#if defined (_DEBUG) 
#undef THIS_FILE 
static char BASED_CODE THIS_FILE[] = __FILE__; 
#endif 
 
#define DBNTWIN32               // Win32 DB-Library for Windows NT 
#include <sqlfront.h>           // DB-Library 
#include <sqldb.h>              // DB-Library 
 
extern "C" 
{ 
    #include "getopt.h"         // GetOption 
} 
 
#include "textcopy.h"           // specific to this program 
 
// GLOBAL VARIABLES 
BOOL bDebug = FALSE;            // debug info 
BYTE* aBuf; 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  FUNCTION: main() 
// 
//      Main application 
// 
//  PARAMETERS: 
// 
//      argc - count of command line arguments 
//      argv - array of command line argument strings 
//      envp - array of environment strings 
// 
//  RETURNS: 0 
// 
//  COMMENTS: 
// 
/////////////////////////////////////////////////////////////////////////////// 
  
int main ( 
    int argc, 
    char** argv, 
    char** envp) 
{ 
    char chOpt;                 // gotten option character 
    char* pszParam;             // gotten parameter 
    int nChunkSize = 4096;          // chunk size 
    CString strChunkSize = "4096";  // chunk size 
    CString strColumnType; 
 
    CString strServer,          // SQL Server 
        strLogin,               // login 
        strPassword,            // password 
        strDatabase,            // database 
        strTable,               // table 
        strColumn,              // column 
        strWhere,               // where clause 
        strFile;                // file 
    BOOL bServer = FALSE,       // flags for tracking options 
        bLogin = FALSE, 
        bPassword = FALSE, 
        bDatabase = FALSE, 
        bTable = FALSE, 
        bColumn = FALSE, 
        bWhere = FALSE, 
        bFile = FALSE, 
        bIn = FALSE, 
        bOut = FALSE; 
 
    // banner 
    cout << "TEXTCOPY Version 1.0" << endl; 
 
    // initialize DB-Library, get DB-Library version number 
    CString strDblibVersion; 
    strDblibVersion = dbinit(); 
    if (strDblibVersion.IsEmpty()) 
    { 
        cout << err << "Could not initialize DB-Library" << endl; 
        return (1); 
    } 
    cout << strDblibVersion << endl; 
     
    // get command-line options and parameters 
    while (TRUE) 
    { 
        // all the following command-line options are valid, they 
        // are neither case sensitive nor white-space sensitive, and 
        // both '/' and '-' are valid switch characters 
        // 
        //  OPTION  PARAMETER           DESCRIPTION 
        // 
        //  /S      [sqlserver]         SQL Server 
        //  /U      [login]             Login 
        //  /P      [password]          Password 
        //  /D      [database]          Database 
        //  /T      table               Table 
        //  /C      column              Column 
        //  /W      "where clause"      Where clause in double quotes 
        //  /F      file                File 
        //  /I                          Into SQL Server from file 
        //  /O                          Out of SQL Server into file 
        //  /K      chunksize           Chunk size in bytes 
        //  /Z                          Debug information 
        //  /?                          Usage information 
        // 
        // for example, all of the following are valid ways to specify 
        // a connection to a SQL Server named 'gizmo' 
        // 
        //  /Sgizmo     /sgizmo     -Sgizmo     -sgizmo 
        //  /S gizmo    /s gizmo    -S gizmo    -s gizmo 
 
        chOpt = GetOption(argc, argv, "s:S:u:U:p:P:d:D:t:T:c:C:w:W:f:F:k:K:iIoOzZ?", &pszParam); 
        if (chOpt > 1) 
        { 
            // chOpt is valid argument 
            switch (chOpt) 
            { 
            case 's':   // SQL Server 
            case 'S': 
                bServer = TRUE; 
                strServer = pszParam; 
                break; 
            case 'u':   // login 
            case 'U': 
                bLogin = TRUE; 
                strLogin = pszParam; 
                break; 
            case 'p':   // password 
            case 'P': 
                bPassword = TRUE; 
                strPassword = pszParam; 
                break; 
            case 'd':   // database 
            case 'D': 
                bDatabase = TRUE; 
                strDatabase = pszParam; 
                break; 
            case 't':   // table 
            case 'T': 
                bTable = TRUE; 
                strTable = pszParam; 
                break; 
            case 'c':   // column 
            case 'C': 
                bColumn = TRUE; 
                strColumn = pszParam; 
                break; 
            case 'w':   // where clause 
            case 'W': 
                bWhere = TRUE; 
                strWhere = pszParam; 
                break; 
            case 'f':   // file 
            case 'F': 
                bFile = TRUE; 
                strFile = pszParam; 
                break; 
            case 'i':   // direction: into SQL Server from file 
            case 'I': 
                bIn = TRUE; 
                break; 
            case 'o':   // direction: out of SQL Server into file 
            case 'O': 
                bOut = TRUE; 
                break; 
            case 'k':   // chunk size in bytes 
            case 'K': 
                if (pszParam != NULL) 
                { 
                    nChunkSize = atoi (pszParam); 
                    _itoa (nChunkSize, strChunkSize.GetBuffer(20), 10); 
                    strChunkSize.ReleaseBuffer(); 
                    if (strChunkSize != pszParam) 
                    { 
                        cout << err << "Converted chunk size '" << pszParam << "' to " << nChunkSize << endl; 
                        return (0); 
                    } 
                } 
                break; 
            case 'z':   // debug 
            case 'Z': 
                bDebug = TRUE; 
                break; 
            case '?':   // usage info 
                DisplayUsage(); 
                return(0); 
                break; 
            } 
        } 
        if (chOpt == 0) 
        { 
            // end of argument list 
            break; 
        } 
        if ((chOpt == 1) || (chOpt == -1)) 
        { 
            // standalone param or error 
            cout << err << "Argument '" << pszParam << "' not recognized" << endl; 
            break; 
        } 
    } 
 
    if ((chOpt == 1) || (chOpt == -1)) 
    { 
        // exit on error 
        return (1); 
    } 
 
    // prompt the user for any unspecified options 
    if (!bServer) 
    { 
        cout << "Type the SQL Server to connect to: "; 
        cinstr (strServer); 
    } 
 
    if (!bLogin) 
    { 
        cout << "Type your login: "; 
        cinstr (strLogin); 
    } 
 
    if (!bPassword) 
    { 
        cout << "Type your password: "; 
        cinstr (strPassword); 
    } 
 
    if (!bDatabase) 
    { 
        cout << "Type the database: "; 
        cinstr (strDatabase); 
    } 
 
    if (!bTable) 
    { 
        cout << "Type the table: "; 
        cinstr (strTable); 
    } 
     
    if (!bColumn) 
    { 
        cout << "Type the text or image column: "; 
        cinstr (strColumn); 
    } 
     
    if (!bWhere) 
    { 
        cout << "Type the where clause: "; 
        cinstr (strWhere); 
    } 
     
    if (!bFile) 
    { 
        cout << "Type the file: "; 
        cinstr (strFile); 
    } 
 
    if (!bIn && !bOut) 
    { 
        while (TRUE) 
        { 
            CString strDirection; 
            cout << "Type the direction ('I' for in, 'O' for out): "; 
            cinstr (strDirection); 
            if (strDirection.CompareNoCase("i") == 0) 
            { 
                bIn = TRUE; 
                break; 
            } 
            else 
            { 
                if (strDirection.CompareNoCase("o") == 0) 
                { 
                    bOut = TRUE; 
                    break; 
                } 
                else 
                { 
                    cout << err << "The value '" << strDirection << "' is invalid." << endl; 
                } 
            } 
        } 
    } 
 
    // parameter validation 
    if (strTable.IsEmpty()) 
    { 
        cout << err << "You did not specify a table." << endl; 
        return (1); 
    } 
 
    if (strColumn.IsEmpty()) 
    { 
        cout << err << "You did not specify a column." << endl; 
        return (1); 
    } 
 
    if (strWhere.IsEmpty()) 
    { 
        cout << err << "You did not specify a where clause." << endl; 
        return (1); 
    } 
 
    /* 
    CString strLowerWhere = strWhere; 
    strLowerWhere.MakeLower(); 
    if (strLowerWhere.Find ("where ") == -1) 
    { 
        cout << err << "Your where clause '" << strWhere << "' did not contain the keyword 'where'." << endl; 
        return (1); 
    } 
    */ 
 
    if (strFile.IsEmpty()) 
    { 
        cout << err << "You did not specify a file." << endl; 
        return (1); 
    } 
 
    if (bIn && bOut) 
    { 
        cout << err << "You cannot specify both 'in' and 'out' directions." << endl; 
        return (1); 
    } 
 
    if (nChunkSize < 1024) 
    { 
        cout << err << "Your specified chunk size of " << nChunkSize << " bytes is too small." << endl; 
        return (1); 
    } 
 
    if (bDebug) 
    { 
        cout << dbg << "Final parameters:" << endl; 
        cout << dbg << "  Server: " << strServer << endl; 
        cout << dbg << "  Login: " << strLogin << endl; 
        cout << dbg << "  Password: " << strPassword << endl; 
        cout << dbg << "  Database: " << strDatabase << endl; 
        cout << dbg << "  Table: " << strTable << endl; 
        cout << dbg << "  Column: " << strColumn << endl; 
        cout << dbg << "  Where clause: " << strWhere << endl; 
        cout << dbg << "  File: " << strFile << endl; 
        cout << dbg << "  Direction: "; 
        if (bIn) 
        { 
            cout << "Into SQL Server from file." << endl; 
        } 
        if (bOut) 
        { 
            cout << "Out of SQL Server into file." << endl; 
        } 
        cout << dbg << "  Chunk size: " << nChunkSize << " bytes" << endl; 
    } 
 
    // install error and message handlers 
    dberrhandle (ErrorHandler); 
    dbmsghandle (MessageHandler); 
 
    // set DB-Library options 
    dbsettime(30); 
    dbsetlogintime(10); 
 
    // get login record 
    PLOGINREC pLoginRec; 
    pLoginRec = dblogin(); 
    if (pLoginRec == NULL) 
    { 
        cout << err << "Could not allocate a login record" << endl; 
        return (1); 
    } 
 
    // fill the login record  
    DBSETLUSER (pLoginRec, strLogin);       // set the login 
    DBSETLPWD (pLoginRec, strPassword);     // set the password 
    DBSETLAPP (pLoginRec, "textcopy");      // set the app name 
    DBSETLHOST (pLoginRec, "textcopy");     // set the host name 
 
    // attempt to connect to SQL Server 
    PDBPROCESS pDbproc; 
    pDbproc = dbopen (pLoginRec, strServer); 
    dbfreelogin (pLoginRec); 
    if (pDbproc == NULL) 
    { 
        cout << err << "Could not connect to SQL Server '" << strServer << "'" << endl; 
        return (1); 
    } 
 
    // re-used DB-Library return code 
    RETCODE r; 
 
    // set textlimit and textsize options for this connection 
    if (bOut) 
    { 
        dbsetopt (pDbproc, DBTEXTLIMIT, strChunkSize); 
        dbsetopt (pDbproc, DBTEXTSIZE, "2147483647"); 
    } 
 
    if (bIn) 
    { 
        dbsetopt (pDbproc, DBTEXTSIZE, "1024"); 
    } 
 
    // must send and execute batch to set options 
    r = dbsqlexec (pDbproc); 
    if (r == FAIL) 
    { 
        cout << err << "Query execution failed." << endl; 
        Cleanup (pDbproc); 
        return (1); 
    } 
 
    // get empty result set(s) from setting options 
    while (TRUE) 
    { 
        r = dbresults (pDbproc); 
        if (r == FAIL) 
        { 
            cout << err << "Query results failed." << endl; 
            Cleanup (pDbproc); 
            return (1); 
        } 
        if (r == NO_MORE_RESULTS) 
            break; // while loop 
    } 
 
    // use specified database 
    if (!strDatabase.IsEmpty()) 
    { 
        r = dbuse (pDbproc, strDatabase); 
        if (r == FAIL) 
        { 
            cout << err << "Could not use database '" << strDatabase << "'" << endl; 
            Cleanup(pDbproc); 
            return (1); 
        } 
    } 
 
    // build query 
    CString strQuery; 
 
    strQuery = "select " + strColumn + " from " + strTable + " " + strWhere; 
    D(cout << "Query: " << strQuery << endl); 
 
    r = dbcmd (pDbproc, strQuery); 
 
    // send and execute query  
    r = dbsqlexec (pDbproc); 
    if (r == FAIL) 
    { 
        cout << err << "Query execution failed." << endl; 
        Cleanup (pDbproc); 
        return (1); 
    } 
 
    // get first result set 
    r = dbresults (pDbproc); 
    if (r != SUCCEED) 
    { 
        cout << err << "Query results failed." << endl; 
        Cleanup (pDbproc); 
        return (1); 
    } 
 
    // verify that only a single column was selected     
    if (dbnumcols (pDbproc) != 1) 
    { 
        cout << err << "More than one column specified." << endl; 
        Cleanup (pDbproc); 
        return (1); 
    } 
 
    // verify that the single column selected is either text or image 
    int nColumnType; 
    nColumnType = dbcoltype (pDbproc, 1); 
    if ((nColumnType != SQLTEXT) && (nColumnType != SQLIMAGE)) 
    { 
        cout << err << "Specified column is not a text or image column." << endl; 
        Cleanup (pDbproc); 
        return (1); 
    } 
    else 
    { 
        if (nColumnType == SQLTEXT) 
        { 
            strColumnType = "text"; 
        } 
        if (nColumnType == SQLIMAGE) 
        { 
            strColumnType = "image"; 
        } 
    } 
 
    // buffer for data transfer between DB-Library and file 
    aBuf = new BYTE[nChunkSize]; 
    if (aBuf == 0) 
    { 
        cout << err << "Unable to allocate transfer buffer of '" << nChunkSize << "' bytes." << endl; 
        Cleanup (pDbproc); 
        return (1); 
    } 
 
    // if the data is coming out of SQL Server and into a file, use dbreadtext 
    // (instead of dbnextrow) to read the text or image data from the row 
    // in chunks 
    if (bOut) 
    { 
    try 
    { 
        // set up file  
        CFile file (strFile, CFile::modeCreate | CFile::modeWrite); 
        D(cout << "File '" << strFile << "' opened for write" << endl); 
 
        DBINT lWriteBytes; 
        while (TRUE) 
        { 
            // read chunk of text or image data from SQL Server into aBuf 
            lWriteBytes = dbreadtext (pDbproc, aBuf, nChunkSize); 
            switch (lWriteBytes) 
            { 
            case 0: 
                // end of text or image row 
                //D(cout << "End of row" << endl); 
                break; 
            case -1: 
                // dbreadtext failed 
                cout << err << "Text or image data retrieval failed." << endl; 
                Cleanup (pDbproc); 
                return (1); 
                break; 
            case NO_MORE_ROWS: 
                //D(cout << "No more rows" << endl); 
                break; 
            default: 
                // dbreadtext has placed lBytes of text or image data 
                // into aBuf, now write that chunk to the file 
                file.Write (aBuf, lWriteBytes); 
                D(cout << "Wrote " << lWriteBytes << " bytes to file" << endl); 
                break; 
            } 
            if ((lWriteBytes == -1) || (lWriteBytes == NO_MORE_ROWS)) 
                break; // while loop 
        } 
        file.Close(); 
        D(cout << "File closed" << endl); 
    } 
    catch (CFileException* e) 
    { 
        //if (e->m_cause == CFileException::fileNotFound) 
        cout << err << "Problem with file '" << strFile << "'." << endl; 
        e->Delete(); 
        Cleanup (pDbproc); 
        return (1); 
    } 
 
    } 
 
    DBBINARY aTextPointer[DBTXPLEN], aTextTimestamp[DBTXTSLEN]; 
    DBBINARY *pTextPointer, *pTextTimestamp; 
     
    // if the data is going into SQL Server from a file, copy the text 
    // pointer and text timestamp values into private buffers 
    if (bIn) 
    { 
        // get the single row 
        STATUS s; 
        s = dbnextrow (pDbproc); 
        if (s != REG_ROW) 
        { 
            cout << err << "Row retrieval failed." << endl; 
            Cleanup (pDbproc); 
            return (1); 
        } 
 
        // get the pointers to the text pointer and text timestamp values 
        pTextPointer = dbtxptr (pDbproc, 1); 
        pTextTimestamp = dbtxtimestamp (pDbproc, 1); 
        if ((pTextPointer == NULL) || (pTextTimestamp == NULL)) 
        { 
            cout << err << "Text or image pointer and timestamp retrieval failed." << endl; 
            Cleanup (pDbproc); 
            return (1); 
        } 
 
        // copy the actual text pointer and text timestamp values into 
        // private buffers 
        memcpy (aTextPointer, pTextPointer, DBTXPLEN); 
        memcpy (aTextTimestamp, pTextTimestamp, DBTXTSLEN); 
 
        // we should only have received one row, so call dbnextrow 
        // to get NO_MORE_ROWS 
        s = dbnextrow (pDbproc); 
        switch (s) 
        { 
        case NO_MORE_ROWS: 
            // this is what we expect 
            break; 
        case REG_ROW: 
            cout << err << "Where clause returned more than one row." << endl; 
            Cleanup (pDbproc); 
            return (1); 
            break; 
        default: 
            cout << err << "Row retrieval failed." << endl; 
            Cleanup (pDbproc); 
            return (1); 
            break; 
        } 
    } 
     
    // get NO_MORE_RESULTS to clear out all results 
    r = dbresults (pDbproc); 
    if (r != NO_MORE_RESULTS) 
    { 
        cout << err << "Query results failed." << endl; 
        Cleanup (pDbproc); 
        return (1); 
    } 
 
    // if the data is going into SQL Server from a file, use dbwritetext 
    // (with the private copies of the text or image pointer and timestamp 
    // values) and dbmoretext to write the text or image data to 
    // SQL Server in chunks 
    if (bIn) 
    { 
    try 
    { 
        // set up file 
        CFile file (strFile, CFile::modeRead); 
        D(cout << "File '" << strFile << "' opened for read" << endl); 
        D(cout << "File is " << file.GetLength() << " bytes long" << endl); 
 
        // call dbwritetext will NULL text value to indicate that the text 
        // values will be sent in chunks using dbmoretext 
        r = dbwritetext (pDbproc, strTable + "." + strColumn, 
            aTextPointer, DBTXPLEN, aTextTimestamp, 
            TRUE, file.GetLength(), NULL); 
        if (r == FAIL) 
        { 
            cout << err << "Text or image write failed." << endl; 
            Cleanup (pDbproc); 
            return (1); 
        } 
         
        // call dbsqlok and dbresults to prepare for calling dbmoretext 
        r = dbsqlok (pDbproc); 
        if (r == FAIL) 
        { 
            cout << err << "Text or image write failed." << endl; 
            Cleanup (pDbproc); 
            return (1); 
        } 
        r = dbresults (pDbproc); 
        if (r == FAIL) 
        { 
            cout << err << "Query results failed." << endl; 
            Cleanup (pDbproc); 
            return (1); 
        } 
 
        // read each chunk from the file and write it to SQL Server 
        UINT nReadBytes = nChunkSize; 
        while (nReadBytes == UINT(nChunkSize)) 
        { 
            // read chunk from file into aBuf 
            nReadBytes = file.Read (aBuf, nChunkSize); 
            D(cout << "Read "<< nReadBytes << " bytes from file" << endl); 
             
            // write chunk of text or image data from aBuf to SQL Server 
            r = dbmoretext (pDbproc, nReadBytes, aBuf); 
            if (r == FAIL) 
            { 
                cout << err << "Text or image write failed." << endl; 
                Cleanup (pDbproc); 
                return (1); 
            } 
        } 
 
        file.Close(); 
        D(cout << "File closed" << endl); 
 
        // call dbsqlok and dbresults to signal end of calls to 
        // dbmoretext and completion of text or image write 
        r = dbsqlok (pDbproc); 
        if (r == FAIL) 
        { 
            cout << err << "Text or image write failed." << endl; 
            Cleanup (pDbproc); 
            return (1); 
        } 
        r = dbresults (pDbproc); 
        if (r == FAIL) 
        { 
            cout << err << "Query results failed." << endl; 
            Cleanup (pDbproc); 
            return (1); 
        } 
    } 
    catch (CFileException* e) 
    { 
        //if (e->m_cause == CFileException::fileNotFound) 
        cout << err << "Problem with file '" << strFile << "'." << endl; 
        e->Delete(); 
        Cleanup (pDbproc); 
        return (1); 
    } 
 
    } 
 
    // display success message 
    if (bIn) 
    { 
        cout << "Data copied into SQL Server " << strColumnType << " column from file '" << strFile << "'." << endl; 
    } 
    if (bOut) 
    { 
        cout << "Data copied out of SQL Server " << strColumnType << " column into file '" << strFile << "'." << endl; 
    } 
 
    // prepare DB-Library for exit 
    Cleanup (pDbproc); 
 
    return (0); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  FUNCTION: Cleanup() 
// 
//      Prepare DB-Library for exit 
// 
//  PARAMETERS: 
// 
//      pDbproc - pointer to DBPROCESS connection 
// 
//  RETURNS: 0 
// 
//  COMMENTS: 
// 
/////////////////////////////////////////////////////////////////////////////// 
 
int Cleanup (PDBPROCESS pDbproc) 
{ 
    delete [] aBuf; 
    dbcancel (pDbproc); 
    dbclose (pDbproc); 
    return (0); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  FUNCTION: ErrorHandler() 
// 
//      Called when a DB-Library error occurs 
// 
//  PARAMETERS: 
// 
//      pDbproc - pointer to DBPROCESS connection 
//      nDblibSeverity - DB-Library error severity 
//      nDblibErrorNo - DB-Library error number 
//      nOSErrorNo - operating system error number 
//      lpstrDblibError - DB-Library error string 
//      lpstrOSError - operating system error string 
// 
//  RETURNS: One of three DB-Library return continuation codes: 
// 
//      INT_CANCEL 
//      INT_CONTINUE 
//      INT_EXIT 
// 
//  COMMENTS: 
// 
/////////////////////////////////////////////////////////////////////////////// 
 
extern "C" int ErrorHandler ( 
    PDBPROCESS pDbproc, 
    INT nSeverity, 
    INT nDBLibError, 
    INT nOSError, 
    LPCSTR pszDBLibError, 
    LPCSTR pszOSError) 
{ 
    // display DB-Library error information 
    cout << "DB-Library Error " << nDBLibError << ": " << pszDBLibError << endl; 
 
    if ((pDbproc == NULL) || (DBDEAD(pDbproc))) 
    { 
        return(INT_EXIT); 
    } 
    else 
    { 
        if (nOSError != DBNOERR) 
        { 
            // this DB-Library error was caused by an operating system 
            // error, so display OS error information 
            cout << "Operating System Error " << nOSError << ": " << pszOSError << endl; 
        } 
 
        return(INT_CANCEL); 
    } 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  FUNCTION: MessageHandler() 
// 
//      Called when a SQL Server message is received 
// 
//  PARAMETERS: 
// 
//      pDbproc - pointer to DBPROCESS connection 
//      lMessage - SQL Server message number 
//      nState - SQL Server message state 
//      nSeverity - SQL Server message severity 
//      pszMessage - SQL Server message string 
//      pszServer - the SQL Server that sent this message 
//      pszProcedure - the stored procedure (if any) where the message 
//                     occurred 
//      usLine - the line number (if any) of the batch or stored procedure 
//             where the message occurred 
// 
//  RETURNS: 0 
// 
//  COMMENTS: 
// 
/////////////////////////////////////////////////////////////////////////////// 
 
extern "C" int MessageHandler ( 
    PDBPROCESS pDbproc, 
    DBINT lMessage, 
    INT nState, 
    INT nSeverity, 
    LPCSTR pszMessage, 
    LPCSTR pszServer, 
    LPCSTR pszProcedure, 
    DBUSMALLINT usLine) 
{ 
    // only display certain SQL Serve messages when debug info enabled 
    switch (lMessage) 
    { 
    case 5701: 
        if (!bDebug) 
        { 
            return (0); 
        } 
        break; 
    } 
 
    // display SQL Server message information 
    cout << "SQL Server"; 
 
    if (pszServer != NULL) 
    { 
        if (*pszServer != '\0') 
        { 
            cout << " '" << pszServer << "'"; 
        } 
    } 
 
    cout << " Message " << lMessage << ": " << pszMessage; 
 
    if (usLine != 0) 
    { 
        cout << " (Concerning line " << usLine; 
        if (pszProcedure != NULL) 
        { 
            cout << " of stored procedure '" << pszProcedure << "'"; 
        } 
        cout << ")"; 
    } 
 
    cout << endl; 
 
    return(0); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  FUNCTION: DisplayUsage() 
// 
//      Display usage information 
// 
//  PARAMETERS: 
// 
//  RETURNS: 0 
// 
//  COMMENTS: 
// 
/////////////////////////////////////////////////////////////////////////////// 
 
int DisplayUsage (void) 
{ 
    cout << endl; 
    cout << "Copies a single text or image value into or out of SQL Server. The value" << endl; 
    cout << "is a specified text or image 'column' of a single row (specified by the" << endl; 
    cout << "\"where clause\") of the specified 'table'." << endl; 
    cout << endl; 
    cout << "If the direction is IN (/I) then the data from the specified 'file' is" << endl; 
    cout << "copied into SQL Server, replacing the existing text or image value. If the" << endl; 
    cout << "direction is OUT (/O) then the text or image value is copied from" << endl; 
    cout << "SQL Server into the specified 'file', replacing any existing file." << endl; 
    cout << endl; 
    cout << "TEXTCOPY [/S [sqlserver]] [/U [login]] [/P [password]]" << endl; 
    cout << "  [/D [database]] [/T table] [/C column] [/W\"where clause\"]" << endl; 
    cout << "  [/F file] [{/I | /O}] [/K chunksize] [/Z] [/?]" << endl; 
    cout << endl; 
    cout << "  /S sqlserver       The SQL Server to connect to. If 'sqlserver' is not" << endl; 
    cout << "                     specified, the local SQL Server is used." << endl; 
    cout << "  /U login           The login to connect with. If 'login' is not specified," << endl; 
    cout << "                     a trusted connection will be used." << endl; 
    cout << "  /P password        The password for 'login'. If 'password' is not" << endl; 
    cout << "                     specified, a NULL password will be used." << endl; 
    cout << "  /D database        The database that contains the table with the text or" << endl; 
    cout << "                     image data. If 'database' is not specified, the default" << endl; 
    cout << "                     database of 'login' is used." << endl; 
    cout << "  /T table           The table that contains the text or image value." << endl; 
    cout << "  /C column          The text or image column of 'table'." << endl; 
    cout << "  /W \"where clause\"  A complete where clause (including the WHERE keyword)" << endl; 
    cout << "                     that specifies a single row of 'table'." << endl; 
    cout << "  /F file            The file name." << endl; 
    cout << "  /I                 Copy text or image value into SQL Server from 'file'." << endl; 
    cout << "  /O                 Copy text or image value out of SQL Server into 'file'." << endl; 
    cout << "  /K chunksize       Size of the data transfer buffer in bytes. Minimum" << endl; 
    cout << "                     value is 1024 bytes, default value is 4096 bytes." << endl; 
    cout << "  /Z                 Display debug information while running." << endl; 
    cout << "  /?                 Display this usage information and exit." << endl; 
    cout << endl; 
    cout << "You will be prompted for any required options you did not specify." << endl; 
 
    return (0); 
}