|
1
|
#ifdef FOSSIL_ENABLE_JSON |
|
2
|
/* |
|
3
|
** Copyright (c) 2013 D. Richard Hipp |
|
4
|
** |
|
5
|
** This program is free software; you can redistribute it and/or |
|
6
|
** modify it under the terms of the Simplified BSD License (also |
|
7
|
** known as the "2-Clause License" or "FreeBSD License".) |
|
8
|
** |
|
9
|
** This program is distributed in the hope that it will be useful, |
|
10
|
** but without any warranty; without even the implied warranty of |
|
11
|
** merchantability or fitness for a particular purpose. |
|
12
|
** |
|
13
|
** Author contact information: |
|
14
|
** [email protected] |
|
15
|
** http://www.hwaci.com/drh/ |
|
16
|
** |
|
17
|
*/ |
|
18
|
|
|
19
|
#include "config.h" |
|
20
|
#include "json_status.h" |
|
21
|
|
|
22
|
#if INTERFACE |
|
23
|
#include "json_detail.h" |
|
24
|
#endif |
|
25
|
|
|
26
|
/* |
|
27
|
Reminder to check if a column exists: |
|
28
|
|
|
29
|
PRAGMA table_info(table_name) |
|
30
|
|
|
31
|
and search for a row where the 'name' field matches. |
|
32
|
|
|
33
|
That assumes, of course, that table_info()'s output format |
|
34
|
is stable. |
|
35
|
*/ |
|
36
|
|
|
37
|
/* |
|
38
|
** Implementation of the /json/status page. |
|
39
|
** |
|
40
|
*/ |
|
41
|
cson_value * json_page_status(){ |
|
42
|
Stmt q = empty_Stmt; |
|
43
|
cson_object * oPay; |
|
44
|
/*cson_object * files;*/ |
|
45
|
int vid, nErr = 0; |
|
46
|
cson_object * tmpO; |
|
47
|
char * zTmp; |
|
48
|
i64 iMtime; |
|
49
|
cson_array * aFiles; |
|
50
|
|
|
51
|
if(!db_open_local(0)){ |
|
52
|
json_set_err(FSL_JSON_E_DB_NEEDS_CHECKOUT, NULL); |
|
53
|
return NULL; |
|
54
|
} |
|
55
|
oPay = cson_new_object(); |
|
56
|
cson_object_set(oPay, "repository", |
|
57
|
json_new_string(db_repository_filename())); |
|
58
|
cson_object_set(oPay, "localRoot", |
|
59
|
json_new_string(g.zLocalRoot)); |
|
60
|
vid = db_lget_int("checkout", 0); |
|
61
|
if(!vid){ |
|
62
|
json_set_err( FSL_JSON_E_UNKNOWN, "Can this even happen?" ); |
|
63
|
return 0; |
|
64
|
} |
|
65
|
vfile_check_signature(vid, 0); |
|
66
|
/* TODO: dupe show_common_info() state */ |
|
67
|
tmpO = cson_new_object(); |
|
68
|
cson_object_set(oPay, "checkout", cson_object_value(tmpO)); |
|
69
|
|
|
70
|
zTmp = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
|
71
|
cson_object_set(tmpO, "uuid", json_new_string(zTmp) ); |
|
72
|
free(zTmp); |
|
73
|
|
|
74
|
cson_object_set( tmpO, "tags", json_tags_for_checkin_rid(vid, 0) ); |
|
75
|
|
|
76
|
/* FIXME: optimize the datetime/timestamp queries into 1 query. */ |
|
77
|
zTmp = db_text(0, "SELECT datetime(mtime) || " |
|
78
|
"' UTC' FROM event WHERE objid=%d", |
|
79
|
vid); |
|
80
|
cson_object_set(tmpO, "datetime", json_new_string(zTmp)); |
|
81
|
free(zTmp); |
|
82
|
iMtime = db_int64(0, "SELECT CAST(strftime('%%s',mtime) AS INTEGER) " |
|
83
|
"FROM event WHERE objid=%d", vid); |
|
84
|
cson_object_set(tmpO, "timestamp", |
|
85
|
cson_value_new_integer((cson_int_t)iMtime)); |
|
86
|
#if 0 |
|
87
|
/* TODO: add parent artifact info */ |
|
88
|
tmpO = cson_new_object(); |
|
89
|
cson_object_set( oPay, "parent", cson_object_value(tmpO) ); |
|
90
|
cson_object_set( tmpO, "uuid", TODO ); |
|
91
|
cson_object_set( tmpO, "timestamp", TODO ); |
|
92
|
#endif |
|
93
|
|
|
94
|
/* Now get the list of non-pristine files... */ |
|
95
|
aFiles = cson_new_array(); |
|
96
|
cson_object_set( oPay, "files", cson_array_value( aFiles ) ); |
|
97
|
|
|
98
|
db_prepare(&q, |
|
99
|
"SELECT pathname, deleted, chnged, rid, " |
|
100
|
" coalesce(origname!=pathname,0) AS renamed," |
|
101
|
" origname" |
|
102
|
" FROM vfile " |
|
103
|
" WHERE is_selected(id)" |
|
104
|
" AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1" |
|
105
|
); |
|
106
|
while( db_step(&q)==SQLITE_ROW ){ |
|
107
|
cson_array *aStatuses = NULL; |
|
108
|
const char *zPathname = db_column_text(&q,0); |
|
109
|
int isDeleted = db_column_int(&q, 1); |
|
110
|
int isChnged = db_column_int(&q,2); |
|
111
|
int isNew = db_column_int(&q,3)==0; |
|
112
|
int isRenamed = db_column_int(&q,4); |
|
113
|
cson_object * oFile; |
|
114
|
char const * zStatus = "unmodified"; |
|
115
|
char * zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
|
116
|
if( isDeleted ){ |
|
117
|
zStatus = "deleted"; |
|
118
|
}else if( isNew ){ |
|
119
|
zStatus = "new" /* maintenance reminder: MUST come |
|
120
|
BEFORE the isChnged checks. */; |
|
121
|
}else if( !file_isfile_or_link(zFullName) ){ |
|
122
|
if( file_access(zFullName, F_OK)==0 ){ |
|
123
|
zStatus = "notAFile"; |
|
124
|
++nErr; |
|
125
|
}else{ |
|
126
|
zStatus = "missing"; |
|
127
|
++nErr; |
|
128
|
} |
|
129
|
}else if( isChnged ){ |
|
130
|
switch( isChnged ){ |
|
131
|
/* These numbers from checkin.c: status_report() */ |
|
132
|
case 1: |
|
133
|
if( file_contains_merge_marker(zFullName) ){ |
|
134
|
zStatus = "conflict"; |
|
135
|
}else{ |
|
136
|
zStatus = "edited"; |
|
137
|
} |
|
138
|
break; |
|
139
|
case 2: zStatus = "updatedByMerge"; break; |
|
140
|
case 3: zStatus = "addedByMerge"; break; |
|
141
|
case 4: zStatus = "updatedByIntegrate"; break; |
|
142
|
case 5: zStatus = "addedByIntegrate"; break; |
|
143
|
case 6: zStatus = "+exec"; break; |
|
144
|
case 7: zStatus = "+symlink"; break; |
|
145
|
case 8: zStatus = "-exec"; break; |
|
146
|
case 9: zStatus = "unlink"; break; |
|
147
|
} |
|
148
|
} |
|
149
|
oFile = cson_new_object(); |
|
150
|
cson_array_append( aFiles, cson_object_value(oFile) ); |
|
151
|
if( isRenamed ){ |
|
152
|
if( *zStatus!='?' ){ |
|
153
|
aStatuses = cson_new_array(); |
|
154
|
cson_object_set( oFile, "status", cson_array_value( aStatuses ) ); |
|
155
|
cson_array_append(aStatuses, |
|
156
|
cson_value_new_string(zStatus, strlen(zStatus))); |
|
157
|
cson_array_append(aStatuses, cson_value_new_string("renamed", 7)); |
|
158
|
}else{ |
|
159
|
zStatus = "renamed"; |
|
160
|
} |
|
161
|
cson_object_set( oFile, "priorName", |
|
162
|
cson_sqlite3_column_to_value(q.pStmt,5)); |
|
163
|
} |
|
164
|
/* optimization potential: move these keys into cson_strings |
|
165
|
to take advantage of refcounting. */ |
|
166
|
cson_object_set( oFile, "name", json_new_string( zPathname ) ); |
|
167
|
cson_object_set( oFile, "status", aStatuses!=NULL ? |
|
168
|
cson_array_value(aStatuses) : json_new_string( zStatus ) ); |
|
169
|
|
|
170
|
free(zFullName); |
|
171
|
} |
|
172
|
cson_object_set( oPay, "errorCount", json_new_int( nErr ) ); |
|
173
|
db_finalize(&q); |
|
174
|
|
|
175
|
#if 0 |
|
176
|
/* TODO: add "merged with" status. First need (A) to decide on a |
|
177
|
structure and (B) to set up some tests for the multi-merge |
|
178
|
case.*/ |
|
179
|
db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0"); |
|
180
|
while( db_step(&q)==SQLITE_ROW ){ |
|
181
|
const char *zLabel = "MERGED_WITH"; |
|
182
|
switch( db_column_int(&q, 1) ){ |
|
183
|
case -1: zLabel = "CHERRYPICK "; break; |
|
184
|
case -2: zLabel = "BACKOUT "; break; |
|
185
|
case -4: zLabel = "INTEGRATE "; break; |
|
186
|
} |
|
187
|
blob_append(report, zPrefix, nPrefix); |
|
188
|
blob_appendf(report, "%s %s\n", zLabel, db_column_text(&q, 0)); |
|
189
|
} |
|
190
|
db_finalize(&q); |
|
191
|
if( nErr ){ |
|
192
|
fossil_fatal("aborting due to prior errors"); |
|
193
|
} |
|
194
|
#endif |
|
195
|
return cson_object_value( oPay ); |
|
196
|
} |
|
197
|
|
|
198
|
#endif /* FOSSIL_ENABLE_JSON */ |
|
199
|
|