summaryrefslogtreecommitdiffstats
path: root/src/gui/util/qdesktopservices_win.cpp
blob: eaaab7db88d3f278bba8fbc0f88dbec7caf748a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights.  These rights are described in the Nokia Qt LGPL
** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <qsettings.h>
#include <qdir.h>
#include <qurl.h>
#include <qstringlist.h>
#include <qprocess.h>
#include <qtemporaryfile.h>
#include <qcoreapplication.h>

#include <qt_windows.h>
#include <shlobj.h>
#if !defined(Q_OS_WINCE)
#  include <intshcut.h>
#else
#  include <qguifunctions_wince.h>
#  if !defined(STANDARDSHELL_UI_MODEL)
#    include <winx.h>
#  endif
#endif

#ifndef QT_NO_DESKTOPSERVICES

QT_BEGIN_NAMESPACE

static bool openDocument(const QUrl &file)
{
    if (!file.isValid())
        return false;
    QString filePath = file.toLocalFile();
    if (filePath.isEmpty())
        filePath = file.toString();
    quintptr returnValue = (quintptr)ShellExecute(0, 0, (wchar_t*)filePath.utf16(), 0, 0, SW_SHOWNORMAL);
    return (returnValue > 32); //ShellExecute returns a value greater than 32 if successful
}

static QString expandEnvStrings(const QString &command)
{
#if defined(Q_OS_WINCE)
    return command;
#else
    wchar_t buffer[MAX_PATH];
    if (ExpandEnvironmentStrings((wchar_t*)command.utf16(), buffer, MAX_PATH))
        return QString::fromWCharArray(buffer);
    else
        return command;
#endif
}

static bool launchWebBrowser(const QUrl &url)
{
    if (url.scheme() == QLatin1String("mailto")) {
        //Retrieve the commandline for the default mail client
        //the default key used below is the command line for the mailto: shell command
        DWORD  bufferSize = 2 * MAX_PATH;
        long  returnValue =  -1;
        QString command;

        HKEY handle;
        LONG res;
        wchar_t keyValue[2 * MAX_PATH] = {0};
        QString keyName(QLatin1String("mailto"));

        //Check if user has set preference, otherwise use default.
        res = RegOpenKeyExW(HKEY_CURRENT_USER,
                            L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\mailto\\UserChoice",
                            0, KEY_READ, &handle);
        if (res == ERROR_SUCCESS) {
            returnValue = RegQueryValueEx(handle, L"Progid", 0, 0, reinterpret_cast<unsigned char*>(keyValue), &bufferSize);
            if (!returnValue)
                keyName = QString::fromUtf16((const ushort*)keyValue);
            RegCloseKey(handle);
        }
        keyName += QLatin1String("\\Shell\\Open\\Command");
        res = RegOpenKeyExW(HKEY_CLASSES_ROOT, (const wchar_t*)keyName.utf16(), 0, KEY_READ, &handle);
        if (res != ERROR_SUCCESS)
            return false;

        bufferSize = 2 * MAX_PATH;
        returnValue = RegQueryValueExW(handle, L"", 0, 0, reinterpret_cast<unsigned char*>(keyValue), &bufferSize);
        if (!returnValue)
            command = QString::fromRawData((QChar*)keyValue, bufferSize);
        RegCloseKey(handle);

        if (returnValue)
            return false;

        command = expandEnvStrings(command);
        command = command.trimmed();
        //Make sure the path for the process is in quotes
        int index = -1 ;
        if (command[0]!= QLatin1Char('\"')) {
            index = command.indexOf(QLatin1String(".exe "), 0, Qt::CaseInsensitive);
            command.insert(index+4, QLatin1Char('\"'));
            command.insert(0, QLatin1Char('\"'));
        }
        //pass the url as the parameter
        index =  command.lastIndexOf(QLatin1String("%1"));
        if (index != -1){
            command.replace(index, 2, url.toString());
        }
        //start the process
        PROCESS_INFORMATION pi;
        ZeroMemory(&pi, sizeof(pi));
        STARTUPINFO si;
        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);

        returnValue = CreateProcess(NULL, (wchar_t*)command.utf16(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

        if (!returnValue)
            return false;

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        return true;
    }

    if (!url.isValid())
        return false;

    if (url.scheme().isEmpty())
        return openDocument(url);

    quintptr returnValue = (quintptr)ShellExecute(0, 0, (wchar_t *)QString::fromUtf8(url.toEncoded().constData()).utf16(),
                                                  0, 0, SW_SHOWNORMAL);
    return (returnValue > 32);
}

QString QDesktopServices::storageLocation(StandardLocation type)
{
#if !defined(QT_NO_SETTINGS)	
    QSettings settings(QSettings::UserScope, QLatin1String("Microsoft"), QLatin1String("Windows"));
    settings.beginGroup(QLatin1String("CurrentVersion/Explorer/Shell Folders"));
    switch (type) {
    case CacheLocation:
        // Although Microsoft has a Cache key it is a pointer to IE's cache, not a cache
        // location for everyone.  Most applications seem to be using a
        // cache directory located in their AppData directory
        return storageLocation(DataLocation) + QLatin1String("\\cache");
    case DataLocation:
        if (!settings.contains(QLatin1String("Local AppData")))
            break;
        return settings.value(QLatin1String("Local AppData")).toString()
            + QLatin1String("\\") + QCoreApplication::organizationName()
            + QLatin1String("\\") + QCoreApplication::applicationName();
        break;
    case DesktopLocation:
        return settings.value(QLatin1String("Desktop")).toString();
        break;

    case DocumentsLocation:
        return settings.value(QLatin1String("Personal")).toString();
        break;

    case FontsLocation:
        return settings.value(QLatin1String("Fonts")).toString();
        break;

    case ApplicationsLocation:
        return settings.value(QLatin1String("Programs")).toString();
        break;

    case MusicLocation:
        return settings.value(QLatin1String("My Music")).toString();
        break;

    case MoviesLocation:
        return settings.value(QLatin1String("My Video")).toString();
        break;

    case PicturesLocation:
        return settings.value(QLatin1String("My Pictures")).toString();
        break;

    case QDesktopServices::HomeLocation:
        return QDir::homePath(); break;

    case QDesktopServices::TempLocation:
        return QDir::tempPath(); break;

    default:
        break;
    }
#endif
    return QString();
}

QString QDesktopServices::displayName(StandardLocation type)
{
    Q_UNUSED(type);
    return QString();
}

QT_END_NAMESPACE

#endif // QT_NO_DESKTOPSERVICES
n1093' href='#n1093'>1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
/*=========================================================================

  Program:   CMake - Cross-Platform Makefile Generator
  Module:    $RCSfile$
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "cmCTestUpdateHandler.h"

#include "cmCTest.h"
#include "cmake.h"
#include "cmMakefile.h"
#include "cmLocalGenerator.h"
#include "cmGlobalGenerator.h"
#include "cmVersion.h"
#include "cmGeneratedFileStream.h"
#include "cmXMLParser.h"

//#include <cmsys/RegularExpression.hxx>
#include <cmsys/Process.h>

// used for sleep
#ifdef _WIN32
#include "windows.h"
#endif

#include <stdlib.h>
#include <math.h>
#include <float.h>

//----------------------------------------------------------------------
static const char* cmCTestUpdateHandlerUpdateStrings[] =
{
  "Unknown",
  "CVS",
  "SVN"
};

static const char* cmCTestUpdateHandlerUpdateToString(int type)
{
  if ( type < cmCTestUpdateHandler::e_UNKNOWN ||
    type >= cmCTestUpdateHandler::e_LAST )
    {
    return cmCTestUpdateHandlerUpdateStrings[cmCTestUpdateHandler::e_UNKNOWN];
    }
  return cmCTestUpdateHandlerUpdateStrings[type];
}

//----------------------------------------------------------------------
//**********************************************************************
class cmCTestUpdateHandlerSVNXMLParser : public cmXMLParser
{
public:
  struct t_CommitLog
    {
    int Revision;
    std::string Author;
    std::string Date;
    std::string Message;
    };
  cmCTestUpdateHandlerSVNXMLParser(cmCTestUpdateHandler* up)
    : cmXMLParser(), UpdateHandler(up), MinRevision(-1), MaxRevision(-1)
    {
    }

  int Parse(const char* str)
    {
    this->MinRevision = -1;
    this->MaxRevision = -1;
    int res = this->cmXMLParser::Parse(str);
    if ( this->MinRevision == -1 || this->MaxRevision == -1 )
      {
      return 0;
      }
    return res;
    }

  typedef std::vector<t_CommitLog> t_VectorOfCommits;

  t_VectorOfCommits* GetCommits() { return &this->Commits; }
  int GetMinRevision() { return this->MinRevision; }
  int GetMaxRevision() { return this->MaxRevision; }

protected:
  void StartElement(const char* name, const char** atts)
    {
    if ( strcmp(name, "logentry") == 0 )
      {
      this->CommitLog = t_CommitLog();
      const char* rev = this->FindAttribute(atts, "revision");
      if ( rev)
        {
        this->CommitLog.Revision = atoi(rev);
        if ( this->MinRevision < 0 ||
          this->MinRevision > this->CommitLog.Revision )
          {
          this->MinRevision = this->CommitLog.Revision;
          }
        if ( this->MaxRevision < 0 ||
          this->MaxRevision < this->CommitLog.Revision )
          {
          this->MaxRevision = this->CommitLog.Revision;
          }
        }
      }
    this->CharacterData.erase(
      this->CharacterData.begin(), this->CharacterData.end());
    }
  void EndElement(const char* name)
    {
    if ( strcmp(name, "logentry") == 0 )
      {
      cmCTestLog(this->UpdateHandler->GetCTestInstance(),
        HANDLER_VERBOSE_OUTPUT,
        "\tRevision: " << this->CommitLog.Revision<< std::endl
        << "\tAuthor:   " << this->CommitLog.Author.c_str() << std::endl
        << "\tDate:     " << this->CommitLog.Date.c_str() << std::endl
        << "\tMessage:  " << this->CommitLog.Message.c_str() << std::endl);
      this->Commits.push_back(this->CommitLog);
      }
    else if ( strcmp(name, "author") == 0 )
      {
      this->CommitLog.Author.assign(&(*(this->CharacterData.begin())),
        this->CharacterData.size());
      }
    else if ( strcmp(name, "date") == 0 )
      {
      this->CommitLog.Date.assign(&(*(this->CharacterData.begin())),
        this->CharacterData.size());
      }
    else if ( strcmp(name, "msg") == 0 )
      {
      this->CommitLog.Message.assign(&(*(this->CharacterData.begin())),
        this->CharacterData.size());
      }
    this->CharacterData.erase(this->CharacterData.begin(),
      this->CharacterData.end());
    }
  void CharacterDataHandler(const char* data, int length)
    {
    this->CharacterData.insert(this->CharacterData.end(), data, data+length);
    }
  const char* FindAttribute( const char** atts, const char* attribute )
    {
    if ( !atts || !attribute )
      {
      return 0;
      }
    const char **atr = atts;
    while ( *atr && **atr && **(atr+1) )
      {
      if ( strcmp(*atr, attribute) == 0 )
        {
        return *(atr+1);
        }
      atr+=2;
      }
    return 0;
    }

private:
  std::vector<char> CharacterData;
  cmCTestUpdateHandler* UpdateHandler;
  t_CommitLog CommitLog;

  t_VectorOfCommits Commits;
  int MinRevision;
  int MaxRevision;
};
//**********************************************************************
//----------------------------------------------------------------------

class cmCTestUpdateHandlerLocale
{
public:
  cmCTestUpdateHandlerLocale();
  ~cmCTestUpdateHandlerLocale();
private:
  std::string saveLCMessages;
};

cmCTestUpdateHandlerLocale::cmCTestUpdateHandlerLocale()
{
  const char* lcmess = cmSystemTools::GetEnv("LC_MESSAGES");
  if(lcmess)
    {
    saveLCMessages = lcmess;
    }
  // if LC_MESSAGES is not set to C, then
  // set it, so that svn/cvs info will be in english ascii
  if(! (lcmess && strcmp(lcmess, "C") == 0))
    {
    cmSystemTools::PutEnv("LC_MESSAGES=C");
    }
}

cmCTestUpdateHandlerLocale::~cmCTestUpdateHandlerLocale()
{
  // restore the value of LC_MESSAGES after running the version control
  // commands
  if(saveLCMessages.size())
    {
    std::string put = "LC_MESSAGES=";
    put += saveLCMessages;
    cmSystemTools::PutEnv(put.c_str());
    }
  else
    {
    cmSystemTools::UnsetEnv("LC_MESSAGES");
    }
}

//----------------------------------------------------------------------
cmCTestUpdateHandler::cmCTestUpdateHandler()
{
}

//----------------------------------------------------------------------
void cmCTestUpdateHandler::Initialize()
{
  this->Superclass::Initialize();
}

//----------------------------------------------------------------------
int cmCTestUpdateHandler::DetermineType(const char* cmd, const char* type)
{
  cmCTestLog(this->CTest, DEBUG, "Determine update type from command: " << cmd
    << " and type: " << type << std::endl);
  if ( type && *type )
    {
    cmCTestLog(this->CTest, DEBUG, "Type specified: " << type << std::endl);
    std::string stype = cmSystemTools::LowerCase(type);
    if ( stype.find("cvs") != std::string::npos )
      {
      return cmCTestUpdateHandler::e_CVS;
      }
    if ( stype.find("svn") != std::string::npos )
      {
      return cmCTestUpdateHandler::e_SVN;
      }
    }
  else
    {
    cmCTestLog(this->CTest, DEBUG, "Type not specified, check command: "
      << cmd << std::endl);
    std::string stype = cmSystemTools::LowerCase(cmd);
    if ( stype.find("cvs") != std::string::npos )
      {
      return cmCTestUpdateHandler::e_CVS;
      }
    if ( stype.find("svn") != std::string::npos )
      {
      return cmCTestUpdateHandler::e_SVN;
      }
    }
  std::string sourceDirectory = this->GetOption("SourceDirectory");
  cmCTestLog(this->CTest, DEBUG, "Check directory: "
    << sourceDirectory.c_str() << std::endl);
  sourceDirectory += "/.svn";
  if ( cmSystemTools::FileExists(sourceDirectory.c_str()) )
    {
    return cmCTestUpdateHandler::e_SVN;
    }
  sourceDirectory = this->GetOption("SourceDirectory");
  sourceDirectory += "/CVS";
  if ( cmSystemTools::FileExists(sourceDirectory.c_str()) )
    {
    return cmCTestUpdateHandler::e_CVS;
    }
  return cmCTestUpdateHandler::e_UNKNOWN;
}

//----------------------------------------------------------------------
//clearly it would be nice if this were broken up into a few smaller
//functions and commented...
int cmCTestUpdateHandler::ProcessHandler()
{
  int count = 0;
  int updateType = e_CVS;
  std::string::size_type cc, kk;
  bool updateProducedError = false;
  std::string goutput;
  std::string errors;

  // Make sure VCS tool messages are in English so we can parse them.
  cmCTestUpdateHandlerLocale fixLocale;
  static_cast<void>(fixLocale);

  std::string checkoutErrorMessages;
  int retVal = 0;

  // Get source dir
  const char* sourceDirectory = this->GetOption("SourceDirectory");
  if ( !sourceDirectory )
    {
    cmCTestLog(this->CTest, ERROR_MESSAGE,
      "Cannot find SourceDirectory  key in the DartConfiguration.tcl"
      << std::endl);
    return -1;
    }

  cmGeneratedFileStream ofs;
  if ( !this->CTest->GetShowOnly() )
    {
    this->StartLogFile("Update", ofs);
    }

  cmCTestLog(this->CTest, HANDLER_OUTPUT,
    "Updating the repository" << std::endl);

  const char* initialCheckoutCommand = this->GetOption("InitialCheckout");
  if ( initialCheckoutCommand )
    {
    cmCTestLog(this->CTest, HANDLER_OUTPUT,
      "   First perform the initial checkout: " << initialCheckoutCommand
      << std::endl);
    cmStdString parent = cmSystemTools::GetParentDirectory(sourceDirectory);
    if ( parent.empty() )
      {
      cmCTestLog(this->CTest, ERROR_MESSAGE,
        "Something went wrong when trying "
        "to determine the parent directory of " << sourceDirectory
        << std::endl);
      return -1;
      }
    cmCTestLog(this->CTest, HANDLER_OUTPUT,
      "   Perform checkout in directory: " << parent.c_str() << std::endl);
    if ( !cmSystemTools::MakeDirectory(parent.c_str()) )
      {
      cmCTestLog(this->CTest, ERROR_MESSAGE,
        "Cannot create parent directory: " << parent.c_str()
        << " of the source directory: " << sourceDirectory << std::endl);
      return -1;
      }
    ofs << "* Run initial checkout" << std::endl;
    ofs << "  Command: " << initialCheckoutCommand << std::endl;
    cmCTestLog(this->CTest, DEBUG, "   Before: "
      << initialCheckoutCommand << std::endl);
    bool retic = this->CTest->RunCommand(initialCheckoutCommand, &goutput,
      &errors, &retVal, parent.c_str(), 0 /* Timeout */);
    cmCTestLog(this->CTest, DEBUG, "   After: "
      << initialCheckoutCommand << std::endl);
    ofs << "  Output: " << goutput.c_str() << std::endl;
    ofs << "  Errors: " << errors.c_str() << std::endl;
    if ( !retic || retVal )
      {
      cmOStringStream ostr;
      ostr << "Problem running initial checkout Output [" << goutput
        << "] Errors [" << errors << "]";
      cmCTestLog(this->CTest, ERROR_MESSAGE, ostr.str().c_str() << std::endl);
      checkoutErrorMessages += ostr.str();
      updateProducedError = true;
      }
    if(!this->CTest->InitializeFromCommand(this->Command))
      {
      cmCTestLog(this->CTest, HANDLER_OUTPUT,
                 " Fatal Error in initialize: "
                 << std::endl);
      cmSystemTools::SetFatalErrorOccured();
      return -1;
      }
    }
  cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Updating the repository: "
    << sourceDirectory << std::endl);

  // Get update command
  std::string updateCommand
    = this->CTest->GetCTestConfiguration("UpdateCommand");
  if ( updateCommand.empty() )
    {
    updateCommand = this->CTest->GetCTestConfiguration("CVSCommand");
    if ( updateCommand.empty() )
      {
      updateCommand = this->CTest->GetCTestConfiguration("SVNCommand");
      if ( updateCommand.empty() )
        {
        cmCTestLog(this->CTest, ERROR_MESSAGE,
          "Cannot find CVSCommand, SVNCommand, or UpdateCommand key in the "
          "DartConfiguration.tcl" << std::endl);
        return -1;
        }
      else
        {
        updateType = e_SVN;
        }
      }
    else
      {
      updateType = e_CVS;
      }
    }
  else
    {
    updateType = this->DetermineType(updateCommand.c_str(),
      this->CTest->GetCTestConfiguration("UpdateType").c_str());
    }

  cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Use "
    << cmCTestUpdateHandlerUpdateToString(updateType) << " repository type"
    << std::endl;);

  // And update options
  std::string updateOptions
    = this->CTest->GetCTestConfiguration("UpdateOptions");
  if ( updateOptions.empty() )
    {
    switch (updateType)
      {
    case cmCTestUpdateHandler::e_CVS:
      updateOptions = this->CTest->GetCTestConfiguration("CVSUpdateOptions");
      if ( updateOptions.empty() )
        {
        updateOptions = "-dP";
        }
      break;
    case cmCTestUpdateHandler::e_SVN:
      updateOptions = this->CTest->GetCTestConfiguration("SVNUpdateOptions");
      break;
      }
    }

  // Get update time
  std::string extra_update_opts;
  if ( this->CTest->GetTestModel() == cmCTest::NIGHTLY )
    {
    struct tm* t = this->CTest->GetNightlyTime(
      this->CTest->GetCTestConfiguration("NightlyStartTime"),
      this->CTest->GetTomorrowTag());
    char current_time[1024];
    sprintf(current_time, "%04d-%02d-%02d %02d:%02d:%02d",
      t->tm_year + 1900,
      t->tm_mon + 1,
      t->tm_mday,
      t->tm_hour,
      t->tm_min,
      t->tm_sec);
    std::string today_update_date = current_time;

    // TODO: SVN
    switch ( updateType )
      {
    case cmCTestUpdateHandler::e_CVS:
      extra_update_opts += "-D \"" + today_update_date +" UTC\"";
      break;
    case cmCTestUpdateHandler::e_SVN:
      extra_update_opts += "-r \"{" + today_update_date +" +0000}\"";
      break;
      }
    }

  bool res = true;

  updateCommand = "\"" + updateCommand + "\"";

  // First, check what the current state of repository is
  std::string command = "";
  switch( updateType )
    {
  case cmCTestUpdateHandler::e_CVS:
    // TODO: CVS - for now just leave empty
    break;
  case cmCTestUpdateHandler::e_SVN:
    command = updateCommand + " cleanup";
    break;
    }

  //
  // Get initial repository information if that is possible. With subversion,
  // this will check the current revision.
  //
  if ( !command.empty() )
    {
    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
      "* Cleanup repository: " << command.c_str() << std::endl);
    if ( !this->CTest->GetShowOnly() )
      {
      ofs << "* Cleanup repository" << std::endl;
      ofs << "  Command: " << command.c_str() << std::endl;
      res = this->CTest->RunCommand(command.c_str(), &goutput, &errors,
        &retVal, sourceDirectory, 0 /*this->TimeOut*/);

      ofs << "  Output: " << goutput.c_str() << std::endl;
      ofs << "  Errors: " << errors.c_str() << std::endl;
      if ( ofs )
        {
        ofs << "--- Cleanup ---" << std::endl;
        ofs << goutput << std::endl;
        }
      }
    else
      {
      cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
        "Cleanup with command: " << command << std::endl);
      }
    }

  // First, check what the current state of repository is
  command = "";
  switch( updateType )
    {
  case cmCTestUpdateHandler::e_CVS:
    // TODO: CVS - for now just leave empty
    break;
  case cmCTestUpdateHandler::e_SVN:
    command = updateCommand + " info";
    break;
    }

  // CVS variables
  // SVN variables
  int svn_current_revision = 0;
  int svn_latest_revision = 0;
  int svn_use_status = 0;

  //
  // Get initial repository information if that is possible. With subversion,
  // this will check the current revision.
  //
  if ( !command.empty() )
    {
    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
      "* Get repository information: " << command.c_str() << std::endl);
    if ( !this->CTest->GetShowOnly() )
      {
      ofs << "* Get repository information" << std::endl;
      ofs << "  Command: " << command.c_str() << std::endl;
      res = this->CTest->RunCommand(command.c_str(), &goutput, &errors,
        &retVal, sourceDirectory, 0 /*this->TimeOut*/);

      ofs << "  Output: " << goutput.c_str() << std::endl;
      ofs << "  Errors: " << errors.c_str() << std::endl;
      if ( ofs )
        {
        ofs << "--- Update information ---" << std::endl;
        ofs << goutput << std::endl;
        }
      switch ( updateType )
        {
      case cmCTestUpdateHandler::e_CVS:
        // TODO: CVS - for now just leave empty
        break;
      case cmCTestUpdateHandler::e_SVN:
          {
          cmsys::RegularExpression current_revision_regex(
            "Revision: ([0-9]+)");
          if ( current_revision_regex.find(goutput.c_str()) )
            {
            std::string currentRevisionString
              = current_revision_regex.match(1);
            svn_current_revision = atoi(currentRevisionString.c_str());
            cmCTestLog(this->CTest, HANDLER_OUTPUT,
              "   Old revision of repository is: " << svn_current_revision
              << std::endl);
            }
          }
        break;
        }
      }
    else
      {
      cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
        "Get information with command: " << command << std::endl);
      }
    }


  //
  // Now update repository and remember what files were updated
  //
  cmGeneratedFileStream os;
  if(!this->StartResultingXML(cmCTest::PartUpdate, "Update", os))
    {
    cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open log file"
      << std::endl);
    return -1;
    }
  std::string start_time = this->CTest->CurrentTime();
  unsigned int start_time_time =
    static_cast<unsigned int>(cmSystemTools::GetTime());
  double elapsed_time_start = cmSystemTools::GetTime();

  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "* Update repository: "
    << command.c_str() << std::endl);
  if ( !this->CTest->GetShowOnly() )
    {
    command = "";
    switch( updateType )
      {
    case cmCTestUpdateHandler::e_CVS:
      command = updateCommand + " -z3 update " + updateOptions +
        " " + extra_update_opts;
      ofs << "* Update repository: " << std::endl;
      ofs << "  Command: " << command.c_str() << std::endl;
      res = this->CTest->RunCommand(command.c_str(), &goutput, &errors,
        &retVal, sourceDirectory, 0 /*this->TimeOut*/);
      ofs << "  Output: " << goutput.c_str() << std::endl;
      ofs << "  Errors: " << errors.c_str() << std::endl;
      break;
    case cmCTestUpdateHandler::e_SVN:
        {
        std::string partialOutput;
        command = updateCommand + " update " + updateOptions +
          " " + extra_update_opts;
        ofs << "* Update repository: " << std::endl;
        ofs << "  Command: " << command.c_str() << std::endl;
        bool res1 = this->CTest->RunCommand(command.c_str(), &partialOutput,
          &errors,
          &retVal, sourceDirectory, 0 /*this->TimeOut*/);
        ofs << "  Output: " << partialOutput.c_str() << std::endl;
        ofs << "  Errors: " << errors.c_str() << std::endl;
        goutput = partialOutput;
        command = updateCommand + " status";
        ofs << "* Status repository: " << std::endl;
        ofs << "  Command: " << command.c_str() << std::endl;
        res = this->CTest->RunCommand(command.c_str(), &partialOutput,
          &errors, &retVal, sourceDirectory, 0 /*this->TimeOut*/);
        ofs << "  Output: " << partialOutput.c_str() << std::endl;
        ofs << "  Errors: " << errors.c_str() << std::endl;
        goutput += partialOutput;
        res = res && res1;
        ofs << "  Total output of update: " << goutput.c_str() << std::endl;
        }
      }
    if ( ofs )
      {
      ofs << "--- Update repository ---" << std::endl;
      ofs << goutput << std::endl;
      }
    }
  if ( !res || retVal )
    {
    updateProducedError = true;
    checkoutErrorMessages += " " + goutput;
    }

  os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    << "<Update mode=\"Client\" Generator=\"ctest-"
    << cmVersion::GetCMakeVersion() << "\">\n"
    << "\t<Site>" << this->CTest->GetCTestConfiguration("Site") << "</Site>\n"
    << "\t<BuildName>" << this->CTest->GetCTestConfiguration("BuildName")
    << "</BuildName>\n"
    << "\t<BuildStamp>" << this->CTest->GetCurrentTag() << "-"
    << this->CTest->GetTestModelString() << "</BuildStamp>" << std::endl;
  os << "\t<StartDateTime>" << start_time << "</StartDateTime>\n"
    << "\t<StartTime>" << start_time_time << "</StartTime>\n"
    << "\t<UpdateCommand>" << this->CTest->MakeXMLSafe(command)
    << "</UpdateCommand>\n"
    << "\t<UpdateType>" << this->CTest->MakeXMLSafe(
      cmCTestUpdateHandlerUpdateToString(updateType))
    << "</UpdateType>\n";

  // Even though it failed, we may have some useful information. Try to
  // continue...
  std::vector<cmStdString> lines;
  cmSystemTools::Split(goutput.c_str(), lines);
  std::vector<cmStdString> errLines;
  cmSystemTools::Split(errors.c_str(), errLines);
  lines.insert(lines.end(), errLines.begin(), errLines.end());

  // CVS style regular expressions
  cmsys::RegularExpression cvs_date_author_regex(
    "^date: +([^;]+); +author: +([^;]+); +state: +[^;]+;");
  cmsys::RegularExpression cvs_revision_regex("^revision +([^ ]*) *$");
  cmsys::RegularExpression cvs_end_of_file_regex(
    "^=========================================="
    "===================================$");
  cmsys::RegularExpression cvs_end_of_comment_regex(
    "^----------------------------$");

  // Subversion style regular expressions
  cmsys::RegularExpression svn_status_line_regex(
    "^ *([0-9]+)  *([0-9]+)  *([^ ]+)  *([^ ][^\t\r\n]*)[ \t\r\n]*$");
  cmsys::RegularExpression svn_latest_revision_regex(
    "(Updated to|At) revision ([0-9]+)\\.");

  cmsys::RegularExpression file_removed_line(
    "cvs update: `?([^']*)'? is no longer in the repository");
  cmsys::RegularExpression file_removed_line2(
    "cvs update: warning: `?([^']*)'? is not \\(any longer\\) pertinent");
  cmsys::RegularExpression file_update_line("([A-Z])  *(.*)");
  std::string current_path = "<no-path>";
  bool first_file = true;

  cmCTestUpdateHandler::AuthorsToUpdatesMap authors_files_map;
  int numUpdated = 0;
  int numModiefied = 0;
  int numConflicting = 0;
  // In subversion, get the latest revision
  if ( updateType == cmCTestUpdateHandler::e_SVN )
    {
    for ( cc= 0; cc < lines.size(); cc ++ )
      {
      const char* line = lines[cc].c_str();
      if ( svn_latest_revision_regex.find(line) )
        {
        svn_latest_revision = atoi(
          svn_latest_revision_regex.match(2).c_str());
        }
      }
    if ( svn_latest_revision <= 0 )
      {
      cmCTestLog(this->CTest, ERROR_MESSAGE,
        "Problem determining the current "
        "revision of the repository from output:" << std::endl
        << goutput.c_str() << std::endl);
      }
    else
      {
      cmCTestLog(this->CTest, HANDLER_OUTPUT,
        "   Current revision of repository is: " << svn_latest_revision
        << std::endl);
      }
    }

  cmCTestLog(this->CTest, HANDLER_OUTPUT,
    "   Gathering version information (each . represents one updated file):"
    << std::endl);
  int file_count = 0;
  std::string removed_line;
  for ( cc= 0; cc < lines.size(); cc ++ )
    {
    const char* line = lines[cc].c_str();
    if ( file_removed_line.find(line) )
      {
      removed_line = "D " + file_removed_line.match(1);
      line = removed_line.c_str();
      }
    else if ( file_removed_line2.find(line) )
      {
      removed_line = "D " + file_removed_line2.match(1);
      line = removed_line.c_str();
      }
    if ( file_update_line.find(line) )
      {
      if ( file_count == 0 )
        {
        cmCTestLog(this->CTest, HANDLER_OUTPUT, "    " << std::flush);
        }
      cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
      std::string upChar = file_update_line.match(1);
      std::string upFile = file_update_line.match(2);
      char mod = upChar[0];
      bool modifiedOrConflict = false;
      if ( mod == 'X' || mod == 'L')
        {
        continue;
        }
      if ( mod != 'M' && mod != 'C' && mod != 'G' )
        {
        count ++;
        modifiedOrConflict = true;
        }
      const char* file = upFile.c_str();
      cmCTestLog(this->CTest, DEBUG, "Line" << cc << ": " << mod << " - "
        << file << std::endl);

      std::string output;
      if ( modifiedOrConflict )
        {
        std::string logcommand;
        switch ( updateType )
          {
        case cmCTestUpdateHandler::e_CVS:
          logcommand = updateCommand + " -z3 log -N \"" + file + "\"";
          break;
        case cmCTestUpdateHandler::e_SVN:
          if ( svn_latest_revision > 0 &&
            svn_latest_revision > svn_current_revision )
            {
            cmOStringStream logCommandStream;
            logCommandStream << updateCommand << " log -r "
              << svn_current_revision << ":" << svn_latest_revision
              << " --xml \"" << file << "\"";
            logcommand = logCommandStream.str();
            }
          else
            {
            logcommand = updateCommand +
              " status  --verbose \"" + file + "\"";
            svn_use_status = 1;
            }
          break;
          }
        cmCTestLog(this->CTest, DEBUG, "Do log: " << logcommand << std::endl);
        cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
          "* Get file update information: " << logcommand.c_str()
          << std::endl);
        ofs << "* Get log information for file: " << file << std::endl;
        ofs << "  Command: " << logcommand.c_str() << std::endl;
        res = this->CTest->RunCommand(logcommand.c_str(), &output, &errors,
          &retVal, sourceDirectory, 0 /*this->TimeOut*/);
        ofs << "  Output: " << output.c_str() << std::endl;
        ofs << "  Errors: " << errors.c_str() << std::endl;
        if ( ofs )
          {
          ofs << output << std::endl;
          }
        }
      if ( res )
        {
        cmCTestLog(this->CTest, DEBUG, output << std::endl);
        std::string::size_type sline = 0;
        std::string srevision1 = "Unknown";
        std::string sdate1     = "Unknown";
        std::string sauthor1   = "Unknown";
        std::string semail1    = "Unknown";
        std::string comment1   = "";
        std::string srevision2 = "Unknown";
        std::string sdate2     = "Unknown";
        std::string sauthor2   = "Unknown";
        std::string comment2   = "";
        std::string semail2    = "Unknown";
        if ( updateType == cmCTestUpdateHandler::e_CVS )
          {
          bool have_first = false;
          bool have_second = false;
          std::vector<cmStdString> ulines;
          cmSystemTools::Split(output.c_str(), ulines);
          for ( kk = 0; kk < ulines.size(); kk ++ )
            {
            const char* clp = ulines[kk].c_str();
            if ( !have_second && !sline && cvs_revision_regex.find(clp) )
              {
              if ( !have_first )
                {
                srevision1 = cvs_revision_regex.match(1);
                }
              else
                {
                srevision2 = cvs_revision_regex.match(1);
                }
              }
            else if ( !have_second && !sline &&
              cvs_date_author_regex.find(clp) )
              {
              sline = kk + 1;
              if ( !have_first )
                {
                sdate1 = cvs_date_author_regex.match(1);
                sauthor1 = cvs_date_author_regex.match(2);
                }
              else
                {
                sdate2 = cvs_date_author_regex.match(1);
                sauthor2 = cvs_date_author_regex.match(2);
                }
              }
            else if ( sline && cvs_end_of_comment_regex.find(clp) ||
              cvs_end_of_file_regex.find(clp))
              {
              if ( !have_first )
                {
                have_first = true;
                }
              else if ( !have_second )
                {
                have_second = true;
                }
              sline = 0;
              }
            else if ( sline )
              {
              if ( !have_first )
                {
                comment1 += clp;
                comment1 += "\n";
                }
              else
                {
                comment2 += clp;
                comment2 += "\n";
                }
              }
            }
          }
        else if ( updateType == cmCTestUpdateHandler::e_SVN )
          {
          if ( svn_use_status )
            {
            cmOStringStream str;
            str << svn_current_revision;
            srevision1 = str.str();
            if (!svn_status_line_regex.find(output))
              {
              cmCTestLog(this->CTest, ERROR_MESSAGE,
                "Bad output from SVN status command: " << output
                << std::endl);
              }
            else if ( svn_status_line_regex.match(4) != file )
              {
              cmCTestLog(this->CTest, ERROR_MESSAGE,
                "Bad output from SVN status command. "
                "The file name returned: \""
                << svn_status_line_regex.match(4)
                << "\" was different than the file specified: \"" << file
                << "\"" << std::endl);
              }
            else
              {
              srevision1 = svn_status_line_regex.match(2);
              int latest_revision = atoi(
                svn_status_line_regex.match(2).c_str());
              if ( svn_current_revision < latest_revision )
                {
                srevision2 = str.str();
                }
              sauthor1 = svn_status_line_regex.match(3);
              }
            }
          else
            {
            cmCTestUpdateHandlerSVNXMLParser parser(this);
            if ( parser.Parse(output.c_str()) )
              {
              int minrev = parser.GetMinRevision();
              int maxrev = parser.GetMaxRevision();
              cmCTestUpdateHandlerSVNXMLParser::
                t_VectorOfCommits::iterator it;
              for ( it = parser.GetCommits()->begin();
                it != parser.GetCommits()->end();
                ++ it )
                {
                if ( it->Revision == maxrev )
                  {
                  cmOStringStream mRevStream;
                  mRevStream << maxrev;
                  srevision1 = mRevStream.str();
                  sauthor1 = it->Author;
                  comment1 = it->Message;
                  sdate1 = it->Date;
                  }
                else if ( it->Revision == minrev )
                  {
                  cmOStringStream mRevStream;
                  mRevStream << minrev;
                  srevision2 = mRevStream.str();
                  sauthor2 = it->Author;
                  comment2 = it->Message;
                  sdate2 = it->Date;
                  }
                }
              }
            }
          }
        if ( mod == 'M' )
          {
          comment1 = "Locally modified file\n";
          sauthor1 = "Local User";
          }
        if ( mod == 'D' )
          {
          comment1 += " - Removed file\n";
          }
        if ( mod == 'C' )
          {
          comment1 = "Conflict while updating\n";
          sauthor1 = "Local User";
          }
        std::string path = cmSystemTools::GetFilenamePath(file);
        std::string fname = cmSystemTools::GetFilenameName(file);
        if ( path != current_path )
          {
          if ( !first_file )
            {
            os << "\t</Directory>" << std::endl;
            }
          else
            {
            first_file = false;
            }
          os << "\t<Directory>\n"
            << "\t\t<Name>" << path << "</Name>" << std::endl;
          }
        if ( mod == 'C' )
          {
          numConflicting ++;
          os << "\t<Conflicting>" << std::endl;
          }
        else if ( mod == 'G' )
          {
          numConflicting ++;
          os << "\t<Conflicting>" << std::endl;
          }
        else if ( mod == 'M' )
          {
          numModiefied ++;
          os << "\t<Modified>" << std::endl;
          }
        else
          {
          numUpdated ++;
          os << "\t<Updated>" << std::endl;
          }
        if ( srevision2 == "Unknown" )
          {
          srevision2 = srevision1;
          }
        cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "File: "
          << path.c_str() << " / " << fname.c_str() << " was updated by "
          << sauthor1.c_str() << " to revision: " << srevision1.c_str()
          << " from revision: " << srevision2.c_str() << std::endl);
        os << "\t\t<File Directory=\"" << cmCTest::MakeXMLSafe(path) << "\">"
          << cmCTest::MakeXMLSafe(fname)
          << "</File>\n"
          << "\t\t<Directory>" << cmCTest::MakeXMLSafe(path)
          << "</Directory>\n"
          << "\t\t<FullName>" << cmCTest::MakeXMLSafe(file) << "</FullName>\n"
          << "\t\t<CheckinDate>" << cmCTest::MakeXMLSafe(sdate1)
          << "</CheckinDate>\n"
          << "\t\t<Author>" << cmCTest::MakeXMLSafe(sauthor1) << "</Author>\n"
          << "\t\t<Email>" << cmCTest::MakeXMLSafe(semail1) << "</Email>\n"
          << "\t\t<Log>" << cmCTest::MakeXMLSafe(comment1) << "</Log>\n"
          << "\t\t<Revision>" << srevision1 << "</Revision>\n"
          << "\t\t<PriorRevision>" << srevision2 << "</PriorRevision>"
          << std::endl;
        if ( srevision2 != srevision1 )
          {
          os
            << "\t\t<Revisions>\n"
            << "\t\t\t<Revision>" << srevision1 << "</Revision>\n"
            << "\t\t\t<PreviousRevision>" << srevision2
            << "</PreviousRevision>\n"
            << "\t\t\t<Author>" << cmCTest::MakeXMLSafe(sauthor1)
            << "</Author>\n"
            << "\t\t\t<Date>" << cmCTest::MakeXMLSafe(sdate1)
            << "</Date>\n"
            << "\t\t\t<Comment>" << cmCTest::MakeXMLSafe(comment1)
            << "</Comment>\n"
            << "\t\t\t<Email>" << cmCTest::MakeXMLSafe(semail1)
            << "</Email>\n"
            << "\t\t</Revisions>\n"
            << "\t\t<Revisions>\n"
            << "\t\t\t<Revision>" << srevision2 << "</Revision>\n"
            << "\t\t\t<PreviousRevision>" << srevision2
            << "</PreviousRevision>\n"
            << "\t\t\t<Author>" << cmCTest::MakeXMLSafe(sauthor2)
            << "</Author>\n"
            << "\t\t\t<Date>" << cmCTest::MakeXMLSafe(sdate2)
            << "</Date>\n"
            << "\t\t\t<Comment>" << cmCTest::MakeXMLSafe(comment2)
            << "</Comment>\n"
            << "\t\t\t<Email>" << cmCTest::MakeXMLSafe(semail2)
            << "</Email>\n"
            << "\t\t</Revisions>" << std::endl;
          }
        if ( mod == 'C' )
          {
          os << "\t</Conflicting>" << std::endl;
          }
        else if ( mod == 'G' )
          {
          os << "\t</Conflicting>" << std::endl;
          }
        else if ( mod == 'M' )
          {
          os << "\t</Modified>" << std::endl;
          }
        else
          {
          os << "\t</Updated>" << std::endl;
          }
        cmCTestUpdateHandler::UpdateFiles *u = &authors_files_map[sauthor1];
        cmCTestUpdateHandler::StringPair p;
        p.first = path;
        p.second = fname;
        u->push_back(p);

        current_path = path;
        }
      file_count ++;
      }
    }
  if ( file_count )
    {
    cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
    }
  if ( numUpdated )
    {
    cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Found " << numUpdated
      << " updated files" << std::endl);
    }
  if ( numModiefied )
    {
    cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Found " << numModiefied
      << " locally modified files"
      << std::endl);
    }
  if ( numConflicting )
    {
    cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Found " << numConflicting
      << " conflicting files"
      << std::endl);
    }
  if ( numModiefied == 0 && numConflicting == 0 && numUpdated == 0 )
    {
    cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Project is up-to-date"
      << std::endl);
    }
  if ( !first_file )
    {
    os << "\t</Directory>" << std::endl;
    }

  cmCTestUpdateHandler::AuthorsToUpdatesMap::iterator it;
  for ( it = authors_files_map.begin();
    it != authors_files_map.end();
    it ++ )
    {
    os << "\t<Author>\n"
      << "\t\t<Name>" << it->first << "</Name>" << std::endl;
    cmCTestUpdateHandler::UpdateFiles *u = &(it->second);
    for ( cc = 0; cc < u->size(); cc ++ )
      {
      os << "\t\t<File Directory=\"" << (*u)[cc].first << "\">"
        << (*u)[cc].second << "</File>" << std::endl;
      }
    os << "\t</Author>" << std::endl;
    }

  cmCTestLog(this->CTest, DEBUG, "End" << std::endl);
  std::string end_time = this->CTest->CurrentTime();
  os << "\t<EndDateTime>" << end_time << "</EndDateTime>\n"
     << "\t<EndTime>" << static_cast<unsigned int>(cmSystemTools::GetTime())
     << "</EndTime>\n"
    << "<ElapsedMinutes>" <<
    static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
    << "</ElapsedMinutes>\n"
    << "\t<UpdateReturnStatus>";
  if ( numModiefied > 0 || numConflicting > 0 )
    {
    os << "Update error: There are modified or conflicting files in the "
      "repository";
    cmCTestLog(this->CTest, ERROR_MESSAGE,
      "   There are modified or conflicting files in the repository"
      << std::endl);
    }
  if ( updateProducedError )
    {
    os << "Update error: ";
    os << this->CTest->MakeXMLSafe(checkoutErrorMessages);
    cmCTestLog(this->CTest, ERROR_MESSAGE, "   Update with command: "
      << command << " failed" << std::endl);
    }
  os << "</UpdateReturnStatus>" << std::endl;
  os << "</Update>" << std::endl;
  if (! res  )
    {
    cmCTestLog(this->CTest, ERROR_MESSAGE,
      "Error(s) when updating the project" << std::endl);
    cmCTestLog(this->CTest, ERROR_MESSAGE, "Output: "
      << goutput << std::endl);
    return -1;
    }
  return count;
}