From 6b9ccc4ca0a5cb35c6cc76a8d5247ac914561d64 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 15 May 2015 09:53:31 +0200 Subject: fixing misc. minor bugs, towards getting testcases to work --- src/pq/db_pq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/pq/db_pq.c') diff --git a/src/pq/db_pq.c b/src/pq/db_pq.c index 72a9370e..42ce3fa7 100644 --- a/src/pq/db_pq.c +++ b/src/pq/db_pq.c @@ -60,6 +60,7 @@ TALER_PQ_exec_prepared (PGconn *db_conn, break; case TALER_PQ_QF_RSA_PUBLIC_KEY: case TALER_PQ_QF_RSA_SIGNATURE: + case TALER_PQ_QF_TIME_ABSOLUTE: len++; break; default: @@ -67,6 +68,7 @@ TALER_PQ_exec_prepared (PGconn *db_conn, GNUNET_assert (0); break; } + i++; } /* new scope to allow stack allocation without alloca */ @@ -190,6 +192,7 @@ TALER_PQ_exec_prepared (PGconn *db_conn, GNUNET_assert (0); break; } + i++; } GNUNET_assert (off == len); res = PQexecPrepared (db_conn, @@ -200,7 +203,7 @@ TALER_PQ_exec_prepared (PGconn *db_conn, param_formats, 1); for (off = 0; off < soff; off++) - GNUNET_free (scratch[soff]); + GNUNET_free (scratch[off]); return res; } } -- cgit v1.2.3 From 6c774a1f032e2e09ab5e22a58a1979acc2c3430b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 15 May 2015 14:16:10 +0200 Subject: completing test-case implementation: --- src/pq/db_pq.c | 4 ++-- src/pq/pq_helper.c | 2 +- src/pq/test_pq.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 6 deletions(-) (limited to 'src/pq/db_pq.c') diff --git a/src/pq/db_pq.c b/src/pq/db_pq.c index 42ce3fa7..220ce9f9 100644 --- a/src/pq/db_pq.c +++ b/src/pq/db_pq.c @@ -113,7 +113,7 @@ TALER_PQ_exec_prepared (PGconn *db_conn, param_formats[off] = 1; off++; param_values[off] = (void *) amount->currency; - param_lengths[off] = strlen (amount->currency) + 1; + param_lengths[off] = strlen (amount->currency); param_formats[off] = 1; off++; } @@ -136,7 +136,7 @@ TALER_PQ_exec_prepared (PGconn *db_conn, param_formats[off] = 1; off++; param_values[off] = (void *) amount->currency; - param_lengths[off] = strlen (amount->currency) + 1; + param_lengths[off] = strlen (amount->currency); param_formats[off] = 1; off++; } diff --git a/src/pq/pq_helper.c b/src/pq/pq_helper.c index 183bd43f..5baab5a1 100644 --- a/src/pq/pq_helper.c +++ b/src/pq/pq_helper.c @@ -138,7 +138,7 @@ TALER_PQ_RESULT_SPEC_AMOUNT_NBO (const char *name, struct TALER_AmountNBO *amount) { struct TALER_PQ_ResultSpec res = - {TALER_PQ_RF_AMOUNT_NBO, (void *) (&amount), sizeof (amount), (name), NULL }; + {TALER_PQ_RF_AMOUNT_NBO, (void *) (&amount), sizeof (*amount), (name), NULL }; return res; } diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c index 1672cc54..dfae8612 100644 --- a/src/pq/test_pq.c +++ b/src/pq/test_pq.c @@ -118,6 +118,12 @@ run_queries (PGconn *conn) sig = GNUNET_CRYPTO_rsa_sign (priv, msg, sizeof (msg)); + TALER_string_to_amount ("EUR:5.5", + &hamount); + TALER_amount_hton (&namount, + &hamount); + TALER_string_to_amount ("EUR:4.4", + &hamount); { struct TALER_PQ_QueryParam params_insert[] = { TALER_PQ_QUERY_PARAM_RSA_PUBLIC_KEY (pub), @@ -143,19 +149,68 @@ run_queries (PGconn *conn) TALER_PQ_RESULT_SPEC_END }; - + fprintf (stderr, + "Inserting\n"); result = TALER_PQ_exec_prepared (conn, "test_insert", params_insert); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database failure: %s\n", + PQresultErrorMessage (result)); + PQclear (result); + GNUNET_CRYPTO_rsa_signature_free (sig); + GNUNET_CRYPTO_rsa_private_key_free (priv); + GNUNET_CRYPTO_rsa_public_key_free (pub); + return 1; + } + PQclear (result); + fprintf (stderr, + "Selecting\n"); result = TALER_PQ_exec_prepared (conn, "test_select", params_select); + if (1 != + PQntuples (result)) + { + GNUNET_break (0); + PQclear (result); + GNUNET_CRYPTO_rsa_signature_free (sig); + GNUNET_CRYPTO_rsa_private_key_free (priv); + GNUNET_CRYPTO_rsa_public_key_free (pub); + return 1; + } ret = TALER_PQ_extract_result (result, results_select, 0); - // FIXME: cmp results! - + GNUNET_break (GNUNET_YES == ret); + fprintf (stderr, + "Verifying\n"); + GNUNET_break (abs_time.abs_value_us == abs_time2.abs_value_us); + GNUNET_break (forever.abs_value_us == forever2.abs_value_us); + GNUNET_break (0 == + memcmp (&hc, + &hc2, + sizeof (struct GNUNET_HashCode))); + GNUNET_break (0 == + TALER_amount_cmp (&hamount, + &hamount2)); + TALER_string_to_amount ("EUR:5.5", + &hamount); + TALER_amount_ntoh (&hamount2, + &namount2); + GNUNET_break (0 == + TALER_amount_cmp (&hamount, + &hamount2)); + GNUNET_break (0 == + GNUNET_CRYPTO_rsa_signature_cmp (sig, + sig2)); + GNUNET_break (0 == + GNUNET_CRYPTO_rsa_public_key_cmp (pub, + pub2)); + TALER_PQ_cleanup_result (results_select); PQclear (result); } -- cgit v1.2.3 From 955054bf25222be9c3942f695e74c8195627405c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 15 May 2015 14:36:08 +0200 Subject: misc bugfixes --- src/include/taler_pq_lib.h | 2 +- src/pq/db_pq.c | 33 +++++++++++++++++++++------------ src/pq/pq_helper.c | 12 ++++++------ src/pq/test_pq.c | 1 + 4 files changed, 29 insertions(+), 19 deletions(-) (limited to 'src/pq/db_pq.c') diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h index 148385c8..6570cb95 100644 --- a/src/include/taler_pq_lib.h +++ b/src/include/taler_pq_lib.h @@ -297,7 +297,7 @@ struct TALER_PQ_ResultSpec * @param dst point to where to store the result, type fits expected result size * @return array entry for the result specification to use */ -#define TALER_PQ_RESULT_SPEC(name, dst) { TALER_PQ_RF_VARSIZE_BLOB, (void *) dst, sizeof (*(dst)), name, NULL } +#define TALER_PQ_RESULT_SPEC(name, dst) { TALER_PQ_RF_FIXED_BLOB, (void *) (dst), sizeof (*(dst)), name, NULL } /** diff --git a/src/pq/db_pq.c b/src/pq/db_pq.c index 220ce9f9..3b39645a 100644 --- a/src/pq/db_pq.c +++ b/src/pq/db_pq.c @@ -225,25 +225,34 @@ TALER_PQ_cleanup_result (struct TALER_PQ_ResultSpec *rs) switch (rs[i].format) { case TALER_PQ_RF_VARSIZE_BLOB: - if (NULL != rs[i].dst) { - GNUNET_free (rs[i].dst); - rs[i].dst = NULL; - *rs[i].result_size = 0; + void **dst = rs[i].dst; + if (NULL != *dst) + { + GNUNET_free (*dst); + *dst = NULL; + *rs[i].result_size = 0; + } + break; } - break; case TALER_PQ_RF_RSA_PUBLIC_KEY: - if (NULL != rs[i].dst) { - GNUNET_CRYPTO_rsa_public_key_free (rs[i].dst); - rs[i].dst = NULL; + void **dst = rs[i].dst; + if (NULL != *dst) + { + GNUNET_CRYPTO_rsa_public_key_free (*dst); + *dst = NULL; + } + break; } - break; case TALER_PQ_RF_RSA_SIGNATURE: - if (NULL != rs[i].dst) { - GNUNET_CRYPTO_rsa_signature_free (rs[i].dst); - rs[i].dst = NULL; + void **dst = rs[i].dst; + if (NULL != *dst) + { + GNUNET_CRYPTO_rsa_signature_free (*dst); + *dst = NULL; + } } break; default: diff --git a/src/pq/pq_helper.c b/src/pq/pq_helper.c index 5baab5a1..9cbdc54b 100644 --- a/src/pq/pq_helper.c +++ b/src/pq/pq_helper.c @@ -121,7 +121,7 @@ TALER_PQ_RESULT_SPEC_VAR (const char *name, size_t *sptr) { struct TALER_PQ_ResultSpec res = - { TALER_PQ_RF_VARSIZE_BLOB, (void *) (dst), 0, (name), sptr }; + { TALER_PQ_RF_VARSIZE_BLOB, (void *) (dst), 0, name, sptr }; return res; } @@ -138,7 +138,7 @@ TALER_PQ_RESULT_SPEC_AMOUNT_NBO (const char *name, struct TALER_AmountNBO *amount) { struct TALER_PQ_ResultSpec res = - {TALER_PQ_RF_AMOUNT_NBO, (void *) (&amount), sizeof (*amount), (name), NULL }; + {TALER_PQ_RF_AMOUNT_NBO, (void *) amount, sizeof (*amount), name, NULL }; return res; } @@ -155,7 +155,7 @@ TALER_PQ_RESULT_SPEC_AMOUNT (const char *name, struct TALER_Amount *amount) { struct TALER_PQ_ResultSpec res = - {TALER_PQ_RF_AMOUNT, (void *) (&amount), sizeof (*amount), (name), NULL }; + {TALER_PQ_RF_AMOUNT, (void *) amount, sizeof (*amount), name, NULL }; return res; } @@ -172,7 +172,7 @@ TALER_PQ_RESULT_SPEC_RSA_PUBLIC_KEY (const char *name, struct GNUNET_CRYPTO_rsa_PublicKey **rsa) { struct TALER_PQ_ResultSpec res = - {TALER_PQ_RF_RSA_PUBLIC_KEY, (void *) &(rsa), 0, (name), NULL }; + {TALER_PQ_RF_RSA_PUBLIC_KEY, (void *) rsa, 0, name, NULL }; return res; } @@ -189,7 +189,7 @@ TALER_PQ_RESULT_SPEC_RSA_SIGNATURE (const char *name, struct GNUNET_CRYPTO_rsa_Signature **sig) { struct TALER_PQ_ResultSpec res = - {TALER_PQ_RF_RSA_SIGNATURE, (void *) &(sig), 0, (name), NULL }; + {TALER_PQ_RF_RSA_SIGNATURE, (void *) sig, 0, (name), NULL }; return res; } @@ -206,7 +206,7 @@ TALER_PQ_RESULT_SPEC_ABSOLUTE_TIME (const char *name, struct GNUNET_TIME_Absolute *at) { struct TALER_PQ_ResultSpec res = - {TALER_PQ_RF_TIME_ABSOLUTE, (void *) (&at), sizeof (at), (name), NULL }; + {TALER_PQ_RF_TIME_ABSOLUTE, (void *) at, sizeof (*at), (name), NULL }; return res; } diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c index dfae8612..7146281e 100644 --- a/src/pq/test_pq.c +++ b/src/pq/test_pq.c @@ -124,6 +124,7 @@ run_queries (PGconn *conn) &hamount); TALER_string_to_amount ("EUR:4.4", &hamount); + /* FIXME: test TALER_PQ_RESULT_SPEC_VAR */ { struct TALER_PQ_QueryParam params_insert[] = { TALER_PQ_QUERY_PARAM_RSA_PUBLIC_KEY (pub), -- cgit v1.2.3 From 85f198ef32012b02d72a7e4af2924f69b6625f73 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 15 May 2015 14:46:03 +0200 Subject: getting test to pass --- src/include/taler_pq_lib.h | 2 +- src/pq/db_pq.c | 4 ---- src/pq/pq_helper.c | 4 ++-- src/pq/test_pq.c | 11 ++--------- 4 files changed, 5 insertions(+), 16 deletions(-) (limited to 'src/pq/db_pq.c') diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h index 6570cb95..980ca29b 100644 --- a/src/include/taler_pq_lib.h +++ b/src/include/taler_pq_lib.h @@ -179,7 +179,7 @@ TALER_PQ_QUERY_PARAM_RSA_SIGNATURE(const struct GNUNET_CRYPTO_rsa_Signature *x); * @param x pointer to the query parameter to pass */ struct TALER_PQ_QueryParam -TALER_PQ_QUERY_PARAM_ABSOLUTE_TIME(struct GNUNET_TIME_Absolute x); +TALER_PQ_QUERY_PARAM_ABSOLUTE_TIME(const struct GNUNET_TIME_Absolute *x); /** diff --git a/src/pq/db_pq.c b/src/pq/db_pq.c index 3b39645a..bb18c56a 100644 --- a/src/pq/db_pq.c +++ b/src/pq/db_pq.c @@ -178,8 +178,6 @@ TALER_PQ_exec_prepared (PGconn *db_conn, at_nbo = GNUNET_new (struct GNUNET_TIME_AbsoluteNBO); scratch[soff++] = at_nbo; - /* FIXME: this does not work for 'forever' as PQ uses 63-bit integers; - should check and handle! (Need testcase!) */ *at_nbo = GNUNET_TIME_absolute_hton (*at_hbo); param_values[off] = (void *) at_nbo; param_lengths[off] = sizeof (struct GNUNET_TIME_AbsoluteNBO); @@ -537,8 +535,6 @@ TALER_PQ_extract_result (PGresult *result, PQgetvalue (result, row, fnum); - /* FIXME: this does not work for 'forever' as PQ uses 63-bit integers; - should check and handle! (Need testcase!) */ *dst = GNUNET_TIME_absolute_ntoh (*res); break; } diff --git a/src/pq/pq_helper.c b/src/pq/pq_helper.c index 9cbdc54b..98c02de7 100644 --- a/src/pq/pq_helper.c +++ b/src/pq/pq_helper.c @@ -99,10 +99,10 @@ TALER_PQ_QUERY_PARAM_RSA_SIGNATURE (const struct GNUNET_CRYPTO_rsa_Signature *x) * @return array entry for the query parameters to use */ struct TALER_PQ_QueryParam -TALER_PQ_QUERY_PARAM_ABSOLUTE_TIME (struct GNUNET_TIME_Absolute x) +TALER_PQ_QUERY_PARAM_ABSOLUTE_TIME (const struct GNUNET_TIME_Absolute *x) { struct TALER_PQ_QueryParam res = - { TALER_PQ_QF_TIME_ABSOLUTE, &(x), sizeof (x) }; + { TALER_PQ_QF_TIME_ABSOLUTE, x, sizeof (*x) }; return res; } diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c index 7146281e..d3417111 100644 --- a/src/pq/test_pq.c +++ b/src/pq/test_pq.c @@ -129,8 +129,8 @@ run_queries (PGconn *conn) struct TALER_PQ_QueryParam params_insert[] = { TALER_PQ_QUERY_PARAM_RSA_PUBLIC_KEY (pub), TALER_PQ_QUERY_PARAM_RSA_SIGNATURE (sig), - TALER_PQ_QUERY_PARAM_ABSOLUTE_TIME (abs_time), - TALER_PQ_QUERY_PARAM_ABSOLUTE_TIME (forever), + TALER_PQ_QUERY_PARAM_ABSOLUTE_TIME (&abs_time), + TALER_PQ_QUERY_PARAM_ABSOLUTE_TIME (&forever), TALER_PQ_QUERY_PARAM_PTR (&hc), TALER_PQ_QUERY_PARAM_AMOUNT (&hamount), TALER_PQ_QUERY_PARAM_AMOUNT_NBO (&namount), @@ -150,8 +150,6 @@ run_queries (PGconn *conn) TALER_PQ_RESULT_SPEC_END }; - fprintf (stderr, - "Inserting\n"); result = TALER_PQ_exec_prepared (conn, "test_insert", params_insert); @@ -168,8 +166,6 @@ run_queries (PGconn *conn) } PQclear (result); - fprintf (stderr, - "Selecting\n"); result = TALER_PQ_exec_prepared (conn, "test_select", params_select); @@ -187,8 +183,6 @@ run_queries (PGconn *conn) results_select, 0); GNUNET_break (GNUNET_YES == ret); - fprintf (stderr, - "Verifying\n"); GNUNET_break (abs_time.abs_value_us == abs_time2.abs_value_us); GNUNET_break (forever.abs_value_us == forever2.abs_value_us); GNUNET_break (0 == @@ -233,7 +227,6 @@ main(int argc, PGresult *result; int ret; - // FIXME: pass valid connect string for tests... GNUNET_log_setup ("test-pq", "WARNING", NULL); -- cgit v1.2.3 From 21da34a5011bfb9445b658febdb40c90c34b4170 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 16 May 2015 18:26:34 +0200 Subject: -fix doxygen errors --- src/include/taler_crypto_lib.h | 8 ++++---- src/include/taler_mintdb_plugin.h | 4 ++-- src/include/taler_pq_lib.h | 2 +- src/mintdb/plugin_mintdb_postgres.c | 6 +++--- src/pq/db_pq.c | 2 +- src/util/crypto.c | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src/pq/db_pq.c') diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index fce27ce5..f2c73faa 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -419,7 +419,7 @@ struct TALER_RefreshLinkEncrypted * private key and the coin's public key. * * @param secret_enc encrypted link secret - * @param transfer_priv transfer private key + * @param trans_priv transfer private key * @param coin_pub coin public key * @param[out] secret set to the shared secret * @return #GNUNET_OK on success, #GNUNET_SYSERR on error @@ -437,7 +437,7 @@ TALER_link_decrypt_secret (const struct TALER_EncryptedLinkSecretP *secret_enc, * public key and the coin's private key. * * @param secret_enc encrypted link secret - * @param transfer_pub transfer public key + * @param trans_pub transfer public key * @param coin_priv coin private key * @param[out] secret set to the shared secret * @return #GNUNET_OK on success, #GNUNET_SYSERR on error @@ -455,8 +455,8 @@ TALER_link_decrypt_secret2 (const struct TALER_EncryptedLinkSecretP *secret_enc, * * @param secret link secret to encrypt * @param coin_pub coin public key - * @param transfer_priv[out] set to transfer private key - * @param transfer_pub[out] set to transfer public key + * @param[out] trans_priv set to transfer private key + * @param[out] trans_pub set to transfer public key * @param[out] secret_enc set to the encryptd @a secret * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 0c9b21eb..1a4a6c60 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -1085,7 +1085,7 @@ struct TALER_MINTDB_Plugin /** * Obtain shared secret and transfer public key from the public key of * the coin. This information and the link information returned by - * #TALER_db_get_link() enable the owner of an old coin to determine + * @e get_link_data_list() enable the owner of an old coin to determine * the private keys of the new coins after the melt. * * @@ -1113,7 +1113,7 @@ struct TALER_MINTDB_Plugin * @param sesssion database connection * @param lock lock operation * @return #GNUNET_YES if known, - * #GNUENT_NO if not, + * #GNUNET_NO if not, * #GNUNET_SYSERR on internal error */ int diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h index 980ca29b..d02dc55a 100644 --- a/src/include/taler_pq_lib.h +++ b/src/include/taler_pq_lib.h @@ -394,7 +394,7 @@ TALER_PQ_exec_prepared (PGconn *db_conn, * is returned. * * @param result result to process - * @param[in|out] rs result specification to extract for + * @param[in,out] rs result specification to extract for * @param row row from the result to extract * @return * #GNUNET_YES if all results could be extracted diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 8bf3302d..e5eb7cce 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -848,7 +848,7 @@ postgres_commit (void *cls, * Insert a denomination key * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion connection to use + * @param session connection to use * @param dki the denomination key information * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure */ @@ -2099,14 +2099,14 @@ postgres_get_refresh_commit_links (void *cls, * Get all of the information from the given melt commit operation. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection to use + * @param session database connection to use * @param session_hash hash to identify refresh session * @return NULL if the @a session_hash does not correspond to any known melt * operation */ static struct TALER_MINTDB_MeltCommitment * postgres_get_melt_commitment (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash) { // FIXME: needs to be implemented! diff --git a/src/pq/db_pq.c b/src/pq/db_pq.c index bb18c56a..a718c805 100644 --- a/src/pq/db_pq.c +++ b/src/pq/db_pq.c @@ -266,7 +266,7 @@ TALER_PQ_cleanup_result (struct TALER_PQ_ResultSpec *rs) * is returned. * * @param result result to process - * @param[in|out] rs result specification to extract for + * @param[in,out] rs result specification to extract for * @param row row from the result to extract * @return * #GNUNET_YES if all results could be extracted diff --git a/src/util/crypto.c b/src/util/crypto.c index 966f010e..8cd08af6 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -371,7 +371,7 @@ TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info) * private key and the coin's public key. * * @param secret_enc encrypted link secret - * @param transfer_priv transfer private key + * @param trans_priv transfer private key * @param coin_pub coin public key * @param[out] secret set to the shared secret * @return #GNUNET_OK on success, #GNUNET_SYSERR on error @@ -410,7 +410,7 @@ TALER_link_decrypt_secret (const struct TALER_EncryptedLinkSecretP *secret_enc, * public key and the coin's private key. * * @param secret_enc encrypted link secret - * @param transfer_pub transfer public key + * @param trans_pub transfer public key * @param coin_priv coin private key * @param[out] secret set to the shared secret * @return #GNUNET_OK on success, #GNUNET_SYSERR on error @@ -449,8 +449,8 @@ TALER_link_decrypt_secret2 (const struct TALER_EncryptedLinkSecretP *secret_enc, * * @param secret link secret to encrypt * @param coin_pub coin public key - * @param transfer_priv[out] set to transfer private key - * @param transfer_pub[out] set to transfer public key + * @param[out] trans_priv set to transfer private key + * @param[out] trans_pub set to transfer public key * @param[out] secret_enc set to the encryptd @a secret * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ -- cgit v1.2.3