summaryrefslogtreecommitdiff
path: root/methods/debdelta.cc
blob: 42d91a6dcd4d9d5915d59bead52c8d6603d916d1 (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
// Includes									/*{{{*/
#include <apt-pkg/acquire-item.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/mmap.h>
#include <apt-pkg/error.h>
#include <apt-pkg/acquire-method.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/hashes.h>
#include <apt-pkg/init.h>

#include <sys/stat.h>
#include <sys/uio.h>
#include <unistd.h>
#include <utime.h>
#include <stdio.h>
#include <errno.h>
#include <zlib.h>
#include <apti18n.h>

/*}}}*/
/** \brief DebdeltaMethod - TODO: say something about debdelta here!
 * */
class DebdeltaMethod : public pkgAcqMethod {
   bool Debug;
   string DebdeltaFile;
   string FromFile;
   string ToFile;
protected:
   // the main(i.e. most important) method of the debdelta method.
   virtual bool Fetch(FetchItem *Itm);
public:
   DebdeltaMethod() : pkgAcqMethod("1.1", SingleInstance | SendConfig) {};
   void MakeToFile();
};

bool DebdeltaMethod::Fetch(FetchItem *Itm)						/*{{{*/
{
   /// Testing only...
   //FetchResult ResTest;
   //ResTest.Filename = "/home/ishan/devel/apt/testrepo/binary/gcc-4.6-base_4.6.0-7_amd64.deb";
   //URIDone(ResTest);
   //return true; 
   ///
   Debug = _config->FindB("Debug::pkgAcquire::Debdelta", false);
   FromFile = Itm->DestFile;
   URI U(Itm->Uri);
   DebdeltaFile = U.Path;
   
   if (flExtension(FromFile) != "deb" || !FileExists(FromFile))
      FromFile = "/";
   if (!FileExists(DebdeltaFile))
      return _error->Error("[Debdelta] Could not find a debdelta file.");
   MakeToFile();
   if (FileExists(ToFile))
      return _error->Error("[Debdelta] New .deb already exists.");
   
   pid_t Process = ExecFork();      
   if (Process == 0)
   {
      const char* Args[8] = {0};
      Args[0] = "/usr/bin/debpatch";
      if (!FileExists(Args[0]))
	 return _error->Error("[Debdelta] Could not find debpatch.");
      Args[1] = "-A";
      Args[2] = DebdeltaFile.c_str();
      Args[3] = FromFile.c_str();
      Args[4] = ToFile.c_str();
      Args[5] = "1>&2";
      Args[6] = "2>/dev/null";
      if (Debug == true)
      {
	 std::cerr << "[Debdelta] Command:" << std::endl;
	 std::cerr << Args[0] << " " << Args[1] << " " << Args[2] << " " << Args[3] << " "
                   << Args[4] << std::endl;
      }
      std::cerr << "[Debdelta] Patching " << ToFile << "...\r";
      execv(Args[0], (char **)Args);
   }
   if (ExecWait(Process, "debpatch", false))
   {
      if (!FileExists(ToFile))
	 return _error->Error("[Debdelta] Failed to patch %s", ToFile.c_str());
      // move the .deb to Dir::Cache::Archives
      string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(ToFile);
      Rename(ToFile, FinalFile);
      ToFile = FinalFile;
      FetchResult Res;
      Res.Filename = ToFile;
      if (Queue != 0)
	 URIDone(Res);
      else
	 std::cout << "Filename: " << Res.Filename << std::endl;
      return true;
   }
   return false;
}


void DebdeltaMethod::MakeToFile()
{
   string DebdeltaName = flNotDir(DebdeltaFile);
   int NewBegin = DebdeltaName.find("_", 0);
   string PkgName = DebdeltaName.substr(0, NewBegin); 
   NewBegin = DebdeltaName.find("_", NewBegin + 1);
   int NewEnd = DebdeltaName.find("_", NewBegin + 1);
   string NewVersion = DebdeltaName.substr(NewBegin + 1, NewEnd - NewBegin - 1);
   string Arch = DebdeltaName.substr(NewEnd + 1, DebdeltaName.find(".", NewEnd + 1) - NewEnd - 1);
   ToFile = _config->FindDir("Dir::Cache::Archives") + "partial/" 
      + PkgName + "_" + NewVersion + "_" + Arch + ".deb";
}

/*}}}*/
/** \brief Wrapper class for testing debdelta */					/*{{{*/
class TestDebdeltaMethod : public DebdeltaMethod {
public:
   /** \brief Run debdelta in debug test mode
    *
    *  This method can be used to run the debdelta method outside
    *  of the "normal" acquire environment for easier testing.
    *
    *  \param base basename of all files involved in this debdelta test
    */
   bool Run(char const *DebdeltaFile, char const *FromFile)
   {
      if (pkgInitConfig(*_config) == false ||
          pkgInitSystem(*_config,_system) == false)
      {
	 std::cerr << "[Debdelta] E: Could not initialize the system/configuration." << std::endl;
	 _error->DumpErrors();
	 return 100;
      }
      _config->CndSet("Debug::pkgAcquire::Debdetla", "true");
      FetchItem *Test = new FetchItem;
      Test->DestFile = FromFile;
      Test->Uri = "debdelta://" + string(DebdeltaFile);
      Test->FailIgnore = false;
      Test->IndexFile = false;
      Test->Next = 0;
     
      return Fetch(Test);  
   }
};

/*}}}*/
/** \brief Starter for the debdelta method (or its test method)			{{{
 *
 *  Used without parameters is the normal behavior for methods for
 *  the APT acquire system. While this works great for the acquire system
 *  it is very hard to test the method and therefore the method also
 *  accepts one parameter which will switch it directly to debug test mode:
 */
int main(int argc, char *argv[])
{
   if (argc <= 1)
   {
      DebdeltaMethod Mth;
      return Mth.Run();
   }
   else
   {
      TestDebdeltaMethod Mth;
      bool result = Mth.Run(argv[1], argv[2]);
      _error->DumpErrors();
      return result;
   }
}
/*}}}*/