1#include "Perforce.h"
2#include "log.h"
3#include <string.h>
4#include <cstdio>
5#include <stdlib.h>
6#include <sstream>
7#include <sys/types.h>
8#include <unistd.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/wait.h>
12#include <cstdio>
13
14using namespace std;
15
16extern char** environ;
17
18int
19Perforce::RunCommand(const string& cmd, string* result, bool printOnFailure)
20{
21    int err;
22    int outPipe[2];
23    int errPipe[2];
24    pid_t pid;
25
26    log_printf("Perforce::RunCommand: %s\n", cmd.c_str());
27
28    err = pipe(outPipe);
29    err |= pipe(errPipe);
30    if (err == -1) {
31        printf("couldn't create pipe. exiting.\n");
32        exit(1);
33        return -1;
34    }
35
36    pid = fork();
37    if (pid == -1) {
38        printf("couldn't fork. eixiting\n");
39        exit(1);
40        return -1;
41    }
42    else if (pid == 0) {
43        char const* args[] = {
44            "/bin/sh",
45            "-c",
46            cmd.c_str(),
47            NULL
48        };
49        close(outPipe[0]);
50        close(errPipe[0]);
51        dup2(outPipe[1], 1);
52        dup2(errPipe[1], 2);
53        execve(args[0], (char* const*)args, environ);
54        // done
55    }
56
57    close(outPipe[1]);
58    close(errPipe[1]);
59
60    result->clear();
61
62    char buf[1024];
63
64    // stdout
65    while (true) {
66        size_t amt = read(outPipe[0], buf, sizeof(buf));
67        result->append(buf, amt);
68        if (amt <= 0) {
69            break;
70        }
71    }
72
73    // stderr -- the messages are short so it ought to just fit in the buffer
74    string error;
75    while (true) {
76        size_t amt = read(errPipe[0], buf, sizeof(buf));
77        error.append(buf, amt);
78        if (amt <= 0) {
79            break;
80        }
81    }
82
83    close(outPipe[0]);
84    close(errPipe[0]);
85
86    waitpid(pid, &err, 0);
87    if (WIFEXITED(err)) {
88        err = WEXITSTATUS(err);
89    } else {
90        err = -1;
91    }
92    if (err != 0 && printOnFailure) {
93        write(2, error.c_str(), error.length());
94    }
95    return err;
96}
97
98int
99Perforce::GetResourceFileNames(const string& version, const string& base,
100                                const vector<string>& apps, vector<string>* results,
101                                bool printOnFailure)
102{
103    int err;
104    string text;
105    stringstream cmd;
106
107    cmd << "p4 files";
108
109    const size_t I = apps.size();
110    for (size_t i=0; i<I; i++) {
111        cmd << " \"" << base << '/' << apps[i] << "/res/values/strings.xml@" << version << '"';
112    }
113
114    err = RunCommand(cmd.str(), &text, printOnFailure);
115
116    const char* str = text.c_str();
117    while (*str) {
118        const char* lineend = strchr(str, '\n');
119        if (lineend == str) {
120            str++;
121            continue;
122        }
123        if (lineend-str > 1023) {
124            fprintf(stderr, "line too long!\n");
125            return 1;
126        }
127
128        string s(str, lineend-str);
129
130        char filename[1024];
131        char edit[1024];
132        int count = sscanf(str, "%[^#]#%*d - %s change %*d %*[^\n]\n", filename, edit);
133
134        if (count == 2 && 0 != strcmp("delete", edit)) {
135            results->push_back(string(filename));
136        }
137
138        str = lineend + 1;
139    }
140
141    return err;
142}
143
144int
145Perforce::GetFile(const string& file, const string& version, string* result,
146        bool printOnFailure)
147{
148    stringstream cmd;
149    cmd << "p4 print -q \"" << file << '@' << version << '"';
150    return RunCommand(cmd.str(), result, printOnFailure);
151}
152
153string
154Perforce::GetCurrentChange(bool printOnFailure)
155{
156    int err;
157    string text;
158
159    err = RunCommand("p4 changes -m 1 \\#have", &text, printOnFailure);
160    if (err != 0) {
161        return "";
162    }
163
164    long long n;
165    int count = sscanf(text.c_str(), "Change %lld on", &n);
166    if (count != 1) {
167        return "";
168    }
169
170    char result[100];
171    sprintf(result, "%lld", n);
172
173    return string(result);
174}
175
176static int
177do_files(const string& op, const vector<string>& files, bool printOnFailure)
178{
179    string text;
180    stringstream cmd;
181
182    cmd << "p4 " << op;
183
184    const size_t I = files.size();
185    for (size_t i=0; i<I; i++) {
186        cmd << " \"" << files[i] << "\"";
187    }
188
189    return Perforce::RunCommand(cmd.str(), &text, printOnFailure);
190}
191
192int
193Perforce::EditFiles(const vector<string>& files, bool printOnFailure)
194{
195    return do_files("edit", files, printOnFailure);
196}
197
198int
199Perforce::AddFiles(const vector<string>& files, bool printOnFailure)
200{
201    return do_files("add", files, printOnFailure);
202}
203
204int
205Perforce::DeleteFiles(const vector<string>& files, bool printOnFailure)
206{
207    return do_files("delete", files, printOnFailure);
208}
209
210string
211Perforce::Where(const string& depotPath, bool printOnFailure)
212{
213    int err;
214    string text;
215    string cmd = "p4 where ";
216    cmd += depotPath;
217
218    err = RunCommand(cmd, &text, printOnFailure);
219    if (err != 0) {
220        return "";
221    }
222
223    size_t index = text.find(' ');
224    if (index == text.npos) {
225        return "";
226    }
227    index = text.find(' ', index+1)+1;
228    if (index == text.npos) {
229        return "";
230    }
231
232    return text.substr(index, text.length()-index-1);
233}
234
235