New JNI interface to better support cursor result sets

This commit is contained in:
Jeremy Lakeman 2013-01-22 15:26:40 +10:30
parent 03536301f8
commit 3a93fee8a5
5 changed files with 292 additions and 118 deletions

2
cli.c
View File

@ -99,6 +99,7 @@ int cli_arg(int argc, const char *const *argv, const struct command_line_option
int arglen = strlen(argname); int arglen = strlen(argname);
int i; int i;
const char *word; const char *word;
*dst = defaultvalue;
for(i = 0; (word = o->words[i]); ++i) { for(i = 0; (word = o->words[i]); ++i) {
int wordlen = strlen(word); int wordlen = strlen(word);
/* No need to check that the "<...>" and "[<...>]" are all intact in the command_line_option, /* No need to check that the "<...>" and "[<...>]" are all intact in the command_line_option,
@ -118,7 +119,6 @@ int cli_arg(int argc, const char *const *argv, const struct command_line_option
never happen, but it can because more than one version of a command line option may exist, one never happen, but it can because more than one version of a command line option may exist, one
with a given argument and another without, and allowing a default value means we can have a with a given argument and another without, and allowing a default value means we can have a
single function handle both in a fairly simple manner. */ single function handle both in a fairly simple manner. */
*dst = defaultvalue;
return 1; return 1;
} }

View File

@ -61,8 +61,9 @@ int commandline_usage(int argc, const char *const *argv, const struct command_li
JNIEnv *jni_env = NULL; JNIEnv *jni_env = NULL;
int jni_exception = 0; int jni_exception = 0;
jobject outv_list = NULL; jobject jniResults = NULL;
jmethodID listAddMethodId = NULL; jclass IJniResults = NULL;
jmethodID startResultSet, setColumnName, putString, putBlob, putLong, putDouble, totalRowCount;
char *outv_buffer = NULL; char *outv_buffer = NULL;
char *outv_current = NULL; char *outv_current = NULL;
@ -84,98 +85,128 @@ static int outv_growbuf(size_t needed)
return 0; return 0;
} }
static int outv_end_field() static int put_blob(jbyte *value, jsize length){
{ jbyteArray arr = NULL;
size_t length = outv_current - outv_buffer; if (value && length>0){
outv_current = outv_buffer; arr = (*jni_env)->NewByteArray(jni_env, length);
jbyteArray arr = (*jni_env)->NewByteArray(jni_env, length); if (arr == NULL || (*jni_env)->ExceptionOccurred(jni_env)) {
if (arr == NULL) {
jni_exception = 1; jni_exception = 1;
return WHY("Exception thrown from NewByteArray()"); return WHY("Exception thrown from NewByteArray()");
} }
(*jni_env)->SetByteArrayRegion(jni_env, arr, 0, length, (jbyte*)outv_buffer); (*jni_env)->SetByteArrayRegion(jni_env, arr, 0, length, value);
if ((*jni_env)->ExceptionOccurred(jni_env)) { if ((*jni_env)->ExceptionOccurred(jni_env)) {
jni_exception = 1; jni_exception = 1;
return WHY("Exception thrown from SetByteArrayRegion()"); return WHYF("Exception thrown from SetByteArrayRegion()");
} }
(*jni_env)->CallBooleanMethod(jni_env, outv_list, listAddMethodId, arr); }
(*jni_env)->CallVoidMethod(jni_env, jniResults, putBlob, arr);
if ((*jni_env)->ExceptionOccurred(jni_env)) { if ((*jni_env)->ExceptionOccurred(jni_env)) {
jni_exception = 1; jni_exception = 1;
return WHY("Exception thrown from CallBooleanMethod()"); return WHY("Exception thrown from CallVoidMethod()");
} }
if (arr)
(*jni_env)->DeleteLocalRef(jni_env, arr); (*jni_env)->DeleteLocalRef(jni_env, arr);
return 0; return 0;
} }
static int outv_end_field()
{
jsize length = outv_current - outv_buffer;
outv_current = outv_buffer;
return put_blob((jbyte *)outv_buffer, length);
}
int Throw(JNIEnv *env, const char *class, const char *msg){
jclass exceptionClass = NULL;
if ((exceptionClass = (*env)->FindClass(env, class)) == NULL)
return -1; // exception
(*env)->ThrowNew(env, exceptionClass, msg);
return -1;
}
/* JNI entry point to command line. See org.servalproject.servald.ServalD class for the Java side. /* JNI entry point to command line. See org.servalproject.servald.ServalD class for the Java side.
JNI method descriptor: "(Ljava/util/List;[Ljava/lang/String;)I" JNI method descriptor: "(Ljava/util/List;[Ljava/lang/String;)I"
*/ */
JNIEXPORT jint JNICALL Java_org_servalproject_servald_ServalD_rawCommand(JNIEnv *env, jobject this, jobject outv, jobjectArray args) JNIEXPORT jint JNICALL Java_org_servalproject_servald_ServalD_rawCommand(JNIEnv *env, jobject this, jobject outv, jobjectArray args)
{ {
jclass stringClass = NULL; if (!IJniResults){
jclass listClass = NULL; IJniResults = (*env)->FindClass(env, "org/servalproject/servald/IJniResults");
unsigned char status = 0; // to match what the shell gets: 0..255 if (IJniResults==NULL)
// Enforce non re-entrancy. return Throw(env, "java/lang/IllegalStateException", "Unable to locate class org.servalproject.servald.IJniResults");
if (jni_env) { startResultSet = (*env)->GetMethodID(env, IJniResults, "startResultSet", "(I)V");
jclass exceptionClass = NULL; if (startResultSet==NULL)
if ((exceptionClass = (*env)->FindClass(env, "java/lang/IllegalStateException")) == NULL) return Throw(env, "java/lang/IllegalStateException", "Unable to locate method startResultSet");
return -1; // exception setColumnName = (*env)->GetMethodID(env, IJniResults, "setColumnName", "(ILjava/lang/String;)V");
(*env)->ThrowNew(env, exceptionClass, "re-entrancy not supported"); if (setColumnName==NULL)
return -1; return Throw(env, "java/lang/IllegalStateException", "Unable to locate method setColumnName");
putString = (*env)->GetMethodID(env, IJniResults, "putString", "(Ljava/lang/String;)V");
if (putString==NULL)
return Throw(env, "java/lang/IllegalStateException", "Unable to locate method putString");
putBlob = (*env)->GetMethodID(env, IJniResults, "putBlob", "([B)V");
if (putBlob==NULL)
return Throw(env, "java/lang/IllegalStateException", "Unable to locate method putBlob");
putLong = (*env)->GetMethodID(env, IJniResults, "putLong", "(J)V");
if (putLong==NULL)
return Throw(env, "java/lang/IllegalStateException", "Unable to locate method putLong");
putDouble = (*env)->GetMethodID(env, IJniResults, "putDouble", "(D)V");
if (putDouble==NULL)
return Throw(env, "java/lang/IllegalStateException", "Unable to locate method putDouble");
totalRowCount = (*env)->GetMethodID(env, IJniResults, "totalRowCount", "(I)V");
if (totalRowCount==NULL)
return Throw(env, "java/lang/IllegalStateException", "Unable to locate method totalRowCount");
} }
// Get some handles to some classes and methods that we use later on. unsigned char status = 0; // to match what the shell gets: 0..255
if ((stringClass = (*env)->FindClass(env, "java/lang/String")) == NULL)
return -1; // exception if (jni_env)
if ((listClass = (*env)->FindClass(env, "java/util/List")) == NULL) return Throw(env, "java/lang/IllegalStateException", "re-entrancy not supported");
return -1; // exception
if ((listAddMethodId = (*env)->GetMethodID(env, listClass, "add", "(Ljava/lang/Object;)Z")) == NULL)
return -1; // exception
// Construct argv, argc from this method's arguments. // Construct argv, argc from this method's arguments.
jsize len = (*env)->GetArrayLength(env, args); jsize len = (*env)->GetArrayLength(env, args);
const char **argv = malloc(sizeof(char*) * (len + 1)); const char **argv = alloca(sizeof(char*) * (len + 1));
if (argv == NULL) { if (argv == NULL)
jclass exceptionClass = NULL; return Throw(env, "java/lang/OutOfMemoryError", "alloca returned NULL");
if ((exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError")) == NULL)
return -1; // exception argv[len]=NULL;
(*env)->ThrowNew(env, exceptionClass, "malloc returned NULL");
return -1;
}
jsize i; jsize i;
for (i = 0; i <= len; ++i)
argv[i] = NULL;
int argc = len; int argc = len;
// From now on, in case of an exception we have to free some resources before // From now on, in case of an exception we have to free some resources before
// returning. // returning.
jni_exception = 0; jni_exception = 0;
for (i = 0; !jni_exception && i != len; ++i) { const char *empty="";
for (i = 0; i < len; ++i) {
const jstring arg = (jstring)(*env)->GetObjectArrayElement(env, args, i); const jstring arg = (jstring)(*env)->GetObjectArrayElement(env, args, i);
if (arg == NULL) if ((*env)->ExceptionOccurred(env))
jni_exception = 1; jni_exception = 1;
else {
argv[i] = empty;
if (arg != NULL && !jni_exception){
const char *str = (*env)->GetStringUTFChars(env, arg, NULL); const char *str = (*env)->GetStringUTFChars(env, arg, NULL);
if (str == NULL) if (str == NULL){
jni_exception = 1; jni_exception = 1;
else }else
argv[i] = str; argv[i] = str;
} }
} }
if (!jni_exception) { if (!jni_exception) {
// Set up the output buffer. // Set up the output buffer.
outv_list = outv; jniResults = outv;
outv_current = outv_buffer; outv_current = outv_buffer;
// Execute the command. // Execute the command.
jni_env = env; jni_env = env;
status = parseCommandLine(NULL, argc, argv); status = parseCommandLine(NULL, argc, argv);
jni_env = NULL; jni_env = NULL;
} }
// Release argv Java string buffers. // Release argv Java string buffers.
for (i = 0; i != len; ++i) { for (i = 0; i < len; ++i) {
if (argv[i]) { if (argv[i] && argv[i]!=empty) {
const jstring arg = (jstring)(*env)->GetObjectArrayElement(env, args, i); const jstring arg = (jstring)(*env)->GetObjectArrayElement(env, args, i);
(*env)->ReleaseStringUTFChars(env, arg, argv[i]); (*env)->ReleaseStringUTFChars(env, arg, argv[i]);
} }
} }
free(argv);
// Deal with Java exceptions: NewStringUTF out of memory in outv_end_field(). // Deal with Java exceptions: NewStringUTF out of memory in outv_end_field().
if (jni_exception || (outv_current != outv_buffer && outv_end_field() == -1)) if (jni_exception || (outv_current != outv_buffer && outv_end_field() == -1))
return -1; return -1;
@ -231,7 +262,6 @@ int cli_putchar(char c)
*outv_current++ = c; *outv_current++ = c;
return (unsigned char) c; return (unsigned char) c;
} }
else
#endif #endif
return putchar(c); return putchar(c);
} }
@ -257,7 +287,6 @@ int cli_write(const unsigned char *buf, size_t len)
outv_current += len; outv_current += len;
return 0; return 0;
} }
else
#endif #endif
return fwrite(buf, len, 1, stdout); return fwrite(buf, len, 1, stdout);
} }
@ -309,6 +338,117 @@ int cli_printf(const char *fmt, ...)
return ret; return ret;
} }
void cli_columns(int columns, const char *names[]){
#ifdef HAVE_JNI_H
if (jni_env) {
(*jni_env)->CallVoidMethod(jni_env, jniResults, startResultSet, columns);
if ((*jni_env)->ExceptionOccurred(jni_env)) {
jni_exception = 1;
WHY("Exception thrown from CallVoidMethod()");
return;
}
int i;
for (i=0;i<columns;i++){
jstring str = (jstring)(*jni_env)->NewStringUTF(jni_env, names[i]);
if (str == NULL) {
jni_exception = 1;
WHY("Exception thrown from NewStringUTF()");
return;
}
(*jni_env)->CallVoidMethod(jni_env, jniResults, setColumnName, i, str);
(*jni_env)->DeleteLocalRef(jni_env, str);
}
return;
}
#endif
cli_printf("%d",columns);
cli_delim("\n");
int i;
for (i=0;i<columns;i++){
cli_puts(names[i]);
if (i+1==columns)
cli_delim("\n");
else
cli_delim(":");
}
}
void cli_field_name(const char *name, const char *delim){
#ifdef HAVE_JNI_H
if (jni_env) {
jstring str = (jstring)(*jni_env)->NewStringUTF(jni_env, name);
if (str == NULL) {
jni_exception = 1;
WHY("Exception thrown from NewStringUTF()");
return;
}
(*jni_env)->CallVoidMethod(jni_env, jniResults, setColumnName, -1, str);
(*jni_env)->DeleteLocalRef(jni_env, str);
return;
}
#endif
cli_puts(name);
cli_delim(delim);
}
void cli_put_long(int64_t value, const char *delim){
#ifdef HAVE_JNI_H
if (jni_env) {
(*jni_env)->CallVoidMethod(jni_env, jniResults, putLong, value);
return;
}
#endif
cli_printf("%lld",value);
cli_delim(delim);
}
void cli_put_string(const char *value, const char *delim){
#ifdef HAVE_JNI_H
if (jni_env) {
jstring str = NULL;
if (value){
str = (jstring)(*jni_env)->NewStringUTF(jni_env, value);
if (str == NULL) {
jni_exception = 1;
WHY("Exception thrown from NewStringUTF()");
return;
}
}
(*jni_env)->CallVoidMethod(jni_env, jniResults, putString, str);
(*jni_env)->DeleteLocalRef(jni_env, str);
return;
}
#endif
if (value)
cli_puts(value);
cli_delim(delim);
}
void cli_put_hexvalue(const unsigned char *value, int length, const char *delim){
#ifdef HAVE_JNI_H
if (jni_env) {
put_blob((jbyte*)value, length);
return;
}
#endif
if (value)
cli_puts(alloca_tohex(value, length));
cli_delim(delim);
}
void cli_row_count(int rows){
#ifdef HAVE_JNI_H
if (jni_env) {
(*jni_env)->CallVoidMethod(jni_env, jniResults, totalRowCount, rows);
if ((*jni_env)->ExceptionOccurred(jni_env)) {
jni_exception = 1;
WHY("Exception thrown from CallVoidMethod()");
return;
}
}
#endif
}
/* Delimit the current output field. This closes the current field, so that the next cli_ output /* Delimit the current output field. This closes the current field, so that the next cli_ output
function will start appending to a new field. Returns 0 on success, -1 on error. If not in a function will start appending to a new field. Returns 0 on success, -1 on error. If not in a
JNI call, then this simply writes a newline to standard output (or the value of the JNI call, then this simply writes a newline to standard output (or the value of the
@ -1397,7 +1537,7 @@ int app_rhizome_list(int argc, const char *const *argv, const struct command_lin
return -1; return -1;
if (rhizome_opendb() == -1) if (rhizome_opendb() == -1)
return -1; return -1;
return rhizome_list_manifests(service, name, sender_sid, recipient_sid, atoi(offset), atoi(limit)); return rhizome_list_manifests(service, name, sender_sid, recipient_sid, atoi(offset), atoi(limit), 0);
} }
int app_keyring_create(int argc, const char *const *argv, const struct command_line_option *o, void *context) int app_keyring_create(int argc, const char *const *argv, const struct command_line_option *o, void *context)

View File

@ -310,7 +310,7 @@ long long rhizome_bar_version(unsigned char *bar);
unsigned long long rhizome_bar_bidprefix_ll(unsigned char *bar); unsigned long long rhizome_bar_bidprefix_ll(unsigned char *bar);
int rhizome_list_manifests(const char *service, const char *name, int rhizome_list_manifests(const char *service, const char *name,
const char *sender_sid, const char *recipient_sid, const char *sender_sid, const char *recipient_sid,
int limit, int offset); int limit, int offset, char count_rows);
int rhizome_retrieve_manifest(const char *manifestid, rhizome_manifest *m); int rhizome_retrieve_manifest(const char *manifestid, rhizome_manifest *m);
#define RHIZOME_DONTVERIFY 0 #define RHIZOME_DONTVERIFY 0

View File

@ -930,11 +930,11 @@ rollback:
int rhizome_list_manifests(const char *service, const char *name, int rhizome_list_manifests(const char *service, const char *name,
const char *sender_sid, const char *recipient_sid, const char *sender_sid, const char *recipient_sid,
int limit, int offset) int limit, int offset, char count_rows)
{ {
IN(); IN();
strbuf b = strbuf_alloca(1024); strbuf b = strbuf_alloca(1024);
strbuf_sprintf(b, "SELECT id, manifest, version, inserttime, author FROM manifests WHERE 1=1"); strbuf_sprintf(b, "SELECT id, manifest, version, inserttime, author, rowid FROM manifests WHERE 1=1");
if (service && *service) if (service && *service)
strbuf_sprintf(b, " AND service = ?1"); strbuf_sprintf(b, " AND service = ?1");
@ -947,8 +947,6 @@ int rhizome_list_manifests(const char *service, const char *name,
strbuf_sprintf(b, " ORDER BY inserttime DESC"); strbuf_sprintf(b, " ORDER BY inserttime DESC");
if (limit)
strbuf_sprintf(b, " LIMIT %u", limit);
if (offset) if (offset)
strbuf_sprintf(b, " OFFSET %u", offset); strbuf_sprintf(b, " OFFSET %u", offset);
@ -977,24 +975,30 @@ int rhizome_list_manifests(const char *service, const char *name,
} }
ret=0; ret=0;
size_t rows = 0; size_t rows = 0;
cli_puts("12"); cli_delim("\n"); // number of columns
cli_puts("service"); cli_delim(":"); const char *names[]={
cli_puts("id"); cli_delim(":"); "_id",
cli_puts("version"); cli_delim(":"); "service",
cli_puts("date"); cli_delim(":"); "id",
cli_puts(".inserttime"); cli_delim(":"); "version",
cli_puts(".author"); cli_delim(":"); "date",
cli_puts(".fromhere"); cli_delim(":"); ".inserttime",
cli_puts("filesize"); cli_delim(":"); ".author",
cli_puts("filehash"); cli_delim(":"); ".fromhere",
cli_puts("sender"); cli_delim(":"); "filesize",
cli_puts("recipient"); cli_delim(":"); "filehash",
cli_puts("name"); cli_delim("\n"); // should be last, because name may contain ':' "sender",
"recipient",
"name"
};
cli_columns(13,names);
while (sqlite_step_retry(&retry, statement) == SQLITE_ROW) { while (sqlite_step_retry(&retry, statement) == SQLITE_ROW) {
++rows; ++rows;
if (!( sqlite3_column_count(statement) == 5 if (limit>0 && rows>limit)
break;
if (!( sqlite3_column_count(statement) == 6
&& sqlite3_column_type(statement, 0) == SQLITE_TEXT && sqlite3_column_type(statement, 0) == SQLITE_TEXT
&& sqlite3_column_type(statement, 1) == SQLITE_BLOB && sqlite3_column_type(statement, 1) == SQLITE_BLOB
&& sqlite3_column_type(statement, 2) == SQLITE_INTEGER && sqlite3_column_type(statement, 2) == SQLITE_INTEGER
@ -1017,6 +1021,8 @@ int rhizome_list_manifests(const char *service, const char *name,
long long q_version = sqlite3_column_int64(statement, 2); long long q_version = sqlite3_column_int64(statement, 2);
long long q_inserttime = sqlite3_column_int64(statement, 3); long long q_inserttime = sqlite3_column_int64(statement, 3);
const char *q_author = (const char *) sqlite3_column_text(statement, 4); const char *q_author = (const char *) sqlite3_column_text(statement, 4);
long long rowid = sqlite3_column_int64(statement, 5);
if (rhizome_read_manifest_file(m, manifestblob, manifestblobsize) == -1) { if (rhizome_read_manifest_file(m, manifestblob, manifestblobsize) == -1) {
WARNF("MANIFESTS row id=%s has invalid manifest blob -- skipped", q_manifestid); WARNF("MANIFESTS row id=%s has invalid manifest blob -- skipped", q_manifestid);
} else { } else {
@ -1024,6 +1030,7 @@ int rhizome_list_manifests(const char *service, const char *name,
if (blob_version != q_version) if (blob_version != q_version)
WARNF("MANIFESTS row id=%s version=%lld does not match manifest blob.version=%lld", q_manifestid, q_version, blob_version); WARNF("MANIFESTS row id=%s version=%lld does not match manifest blob.version=%lld", q_manifestid, q_version, blob_version);
int match = 1; int match = 1;
const char *blob_service = rhizome_manifest_get(m, "service", NULL, 0); const char *blob_service = rhizome_manifest_get(m, "service", NULL, 0);
if (service[0] && !(blob_service && strcasecmp(service, blob_service) == 0)) if (service[0] && !(blob_service && strcasecmp(service, blob_service) == 0))
match = 0; match = 0;
@ -1037,43 +1044,64 @@ int rhizome_list_manifests(const char *service, const char *name,
if (!(blob_recipient && strcasecmp(recipient_sid, blob_recipient) == 0)) if (!(blob_recipient && strcasecmp(recipient_sid, blob_recipient) == 0))
match = 0; match = 0;
} }
if (match) { if (match) {
const char *blob_name = rhizome_manifest_get(m, "name", NULL, 0); const char *blob_name = rhizome_manifest_get(m, "name", NULL, 0);
long long blob_date = rhizome_manifest_get_ll(m, "date"); long long blob_date = rhizome_manifest_get_ll(m, "date");
const char *blob_filehash = rhizome_manifest_get(m, "filehash", NULL, 0); const char *blob_filehash = rhizome_manifest_get(m, "filehash", NULL, 0);
long long blob_filesize = rhizome_manifest_get_ll(m, "filesize"); long long blob_filesize = rhizome_manifest_get_ll(m, "filesize");
int from_here = 0; int from_here = 0;
unsigned char senderSid[SID_SIZE];
unsigned char recipientSid[SID_SIZE];
if (blob_sender)
stowSid(senderSid, 0, blob_sender);
if (blob_recipient)
stowSid(recipientSid, 0, blob_recipient);
if (q_author) { if (q_author) {
if (config.debug.rhizome) DEBUGF("q_author=%s", alloca_str_toprint(q_author)); if (config.debug.rhizome) DEBUGF("q_author=%s", alloca_str_toprint(q_author));
unsigned char authorSid[SID_SIZE]; stowSid(m->author, 0, q_author);
stowSid(authorSid, 0, q_author);
int cn = 0, in = 0, kp = 0; int cn = 0, in = 0, kp = 0;
from_here = keyring_find_sid(keyring, &cn, &in, &kp, authorSid); from_here = keyring_find_sid(keyring, &cn, &in, &kp, m->author);
} }
if (!from_here && blob_sender) { if (!from_here && blob_sender) {
if (config.debug.rhizome) DEBUGF("blob_sender=%s", alloca_str_toprint(blob_sender)); if (config.debug.rhizome) DEBUGF("blob_sender=%s", alloca_str_toprint(blob_sender));
unsigned char senderSid[SID_SIZE];
stowSid(senderSid, 0, blob_sender);
int cn = 0, in = 0, kp = 0; int cn = 0, in = 0, kp = 0;
from_here = keyring_find_sid(keyring, &cn, &in, &kp, senderSid); from_here = keyring_find_sid(keyring, &cn, &in, &kp, senderSid);
} }
if (config.debug.rhizome) DEBUGF("manifest payload size = %lld", blob_filesize); if (config.debug.rhizome) DEBUGF("manifest payload size = %lld", blob_filesize);
cli_puts(blob_service ? blob_service : ""); cli_delim(":");
cli_puts(q_manifestid); cli_delim(":"); cli_put_long(rowid, ":");
cli_printf("%lld", blob_version); cli_delim(":"); cli_put_string(blob_service, ":");
cli_printf("%lld", blob_date); cli_delim(":"); cli_put_hexvalue(m->cryptoSignPublic, RHIZOME_MANIFEST_ID_BYTES, ":");
cli_printf("%lld", q_inserttime); cli_delim(":"); cli_put_long(blob_version, ":");
cli_puts(q_author ? q_author : ""); cli_delim(":"); cli_put_long(blob_date, ":");
cli_printf("%d", from_here); cli_delim(":"); cli_put_long(q_inserttime, ":");
cli_printf("%lld", blob_filesize); cli_delim(":"); cli_put_hexvalue(q_author?m->author:NULL, SID_SIZE, ":");
cli_puts(blob_filehash ? blob_filehash : ""); cli_delim(":"); cli_put_long(from_here, ":");
cli_puts(blob_sender ? blob_sender : ""); cli_delim(":"); cli_put_long(m->fileLength, ":");
cli_puts(blob_recipient ? blob_recipient : ""); cli_delim(":");
cli_puts(blob_name ? blob_name : ""); cli_delim("\n"); unsigned char filehash[SHA512_DIGEST_LENGTH];
if (m->fileLength)
fromhex(filehash, blob_filehash, SHA512_DIGEST_LENGTH);
cli_put_hexvalue(m->fileLength?filehash:NULL, SHA512_DIGEST_LENGTH, ":");
cli_put_hexvalue(blob_sender?senderSid:NULL, SID_SIZE, ":");
cli_put_hexvalue(blob_recipient?recipientSid:NULL, SID_SIZE, ":");
cli_put_string(blob_name, "\n");
} }
} }
if (m) rhizome_manifest_free(m); if (m) rhizome_manifest_free(m);
} }
if (ret==0 && count_rows){
while (sqlite_step_retry(&retry, statement) == SQLITE_ROW)
++rows;
}
cli_row_count(rows);
cleanup: cleanup:
sqlite3_finalize(statement); sqlite3_finalize(statement);
RETURN(ret); RETURN(ret);

View File

@ -630,6 +630,12 @@ int cli_putchar(char c);
int cli_puts(const char *str); int cli_puts(const char *str);
int cli_printf(const char *fmt, ...); int cli_printf(const char *fmt, ...);
int cli_delim(const char *opt); int cli_delim(const char *opt);
void cli_columns(int columns, const char *names[]);
void cli_row_count(int rows);
void cli_field_name(const char *name, const char *delim);
void cli_put_long(int64_t value, const char *delim);
void cli_put_string(const char *value, const char *delim);
void cli_put_hexvalue(const unsigned char *value, int length, const char *delim);
int overlay_mdp_getmyaddr(int index,unsigned char *sid); int overlay_mdp_getmyaddr(int index,unsigned char *sid);
int overlay_mdp_bind(unsigned char *localaddr,int port); int overlay_mdp_bind(unsigned char *localaddr,int port);