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 **   drh@hwaci.com
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 */
json_page_status()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   /* TODO: dupe show_common_info() state */
66   tmpO = cson_new_object();
67   cson_object_set(oPay, "checkout", cson_object_value(tmpO));
68 
69   zTmp = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
70   cson_object_set(tmpO, "uuid", json_new_string(zTmp) );
71   free(zTmp);
72 
73   cson_object_set( tmpO, "tags", json_tags_for_checkin_rid(vid, 0) );
74 
75   /* FIXME: optimize the datetime/timestamp queries into 1 query. */
76   zTmp = db_text(0, "SELECT datetime(mtime) || "
77                  "' UTC' FROM event WHERE objid=%d",
78                  vid);
79   cson_object_set(tmpO, "datetime", json_new_string(zTmp));
80   free(zTmp);
81   iMtime = db_int64(0, "SELECT CAST(strftime('%%s',mtime) AS INTEGER) "
82                     "FROM event WHERE objid=%d", vid);
83   cson_object_set(tmpO, "timestamp",
84                   cson_value_new_integer((cson_int_t)iMtime));
85 #if 0
86     /* TODO: add parent artifact info */
87   tmpO = cson_new_object();
88   cson_object_set( oPay, "parent", cson_object_value(tmpO) );
89   cson_object_set( tmpO, "uuid", TODO );
90   cson_object_set( tmpO, "timestamp", TODO );
91 #endif
92 
93   /* Now get the list of non-pristine files... */
94   aFiles = cson_new_array();
95   cson_object_set( oPay, "files", cson_array_value( aFiles ) );
96 
97   db_prepare(&q,
98     "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)"
99     "  FROM vfile "
100     " WHERE is_selected(id)"
101     "   AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1"
102   );
103   while( db_step(&q)==SQLITE_ROW ){
104     const char *zPathname = db_column_text(&q,0);
105     int isDeleted = db_column_int(&q, 1);
106     int isChnged = db_column_int(&q,2);
107     int isNew = db_column_int(&q,3)==0;
108     int isRenamed = db_column_int(&q,4);
109     cson_object * oFile;
110     char const * zStatus = "???";
111     char * zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
112     if( isDeleted ){
113       zStatus = "deleted";
114     }else if( isNew ){
115       zStatus = "new" /* maintenance reminder: MUST come
116                          BEFORE the isChnged checks. */;
117     }else if( isRenamed ){
118       zStatus = "renamed";
119     }else if( !file_isfile_or_link(zFullName) ){
120       if( file_access(zFullName, F_OK)==0 ){
121         zStatus = "notAFile";
122         ++nErr;
123       }else{
124         zStatus = "missing";
125         ++nErr;
126       }
127     }else if( 2==isChnged ){
128       zStatus = "updatedByMerge";
129     }else if( 3==isChnged ){
130       zStatus = "addedByMerge";
131     }else if( 4==isChnged ){
132       zStatus = "updatedByIntegrate";
133     }else if( 5==isChnged ){
134       zStatus = "addedByIntegrate";
135     }else if( 1==isChnged ){
136       if( file_contains_merge_marker(zFullName) ){
137         zStatus = "conflict";
138       }else{
139         zStatus = "edited";
140       }
141     }
142 
143     oFile = cson_new_object();
144     cson_array_append( aFiles, cson_object_value(oFile) );
145     /* optimization potential: move these keys into cson_strings
146        to take advantage of refcounting. */
147     cson_object_set( oFile, "name", json_new_string( zPathname ) );
148     cson_object_set( oFile, "status", json_new_string( zStatus ) );
149 
150     free(zFullName);
151   }
152   cson_object_set( oPay, "errorCount", json_new_int( nErr ) );
153   db_finalize(&q);
154 
155 #if 0
156   /* TODO: add "merged with" status.  First need (A) to decide on a
157      structure and (B) to set up some tests for the multi-merge
158      case.*/
159   db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0");
160   while( db_step(&q)==SQLITE_ROW ){
161     const char *zLabel = "MERGED_WITH";
162     switch( db_column_int(&q, 1) ){
163       case -1:  zLabel = "CHERRYPICK ";  break;
164       case -2:  zLabel = "BACKOUT    ";  break;
165       case -4:  zLabel = "INTEGRATE  ";  break;
166     }
167     blob_append(report, zPrefix, nPrefix);
168     blob_appendf(report, "%s %s\n", zLabel, db_column_text(&q, 0));
169   }
170   db_finalize(&q);
171   if( nErr ){
172     fossil_fatal("aborting due to prior errors");
173   }
174 #endif
175   return cson_object_value( oPay );
176 }
177 
178 #endif /* FOSSIL_ENABLE_JSON */
179