GnuCash 2.4.99
TransLog.c
00001 /********************************************************************\
00002  * TransLog.c -- the transaction logger                             *
00003  * Copyright (C) 1998 Linas Vepstas                                 *
00004  *                                                                  *
00005  * This program is free software; you can redistribute it and/or    *
00006  * modify it under the terms of the GNU General Public License as   *
00007  * published by the Free Software Foundation; either version 2 of   *
00008  * the License, or (at your option) any later version.              *
00009  *                                                                  *
00010  * This program is distributed in the hope that it will be useful,  *
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00013  * GNU General Public License for more details.                     *
00014  *                                                                  *
00015  * You should have received a copy of the GNU General Public License*
00016  * along with this program; if not, contact:                        *
00017  *                                                                  *
00018  * Free Software Foundation           Voice:  +1-617-542-5942       *
00019  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00020  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00021  *                                                                  *
00022 \********************************************************************/
00023 
00024 #include "config.h"
00025 
00026 #include <errno.h>
00027 #include <glib.h>
00028 #include <glib/gstdio.h>
00029 #include <string.h>
00030 
00031 #include "Account.h"
00032 #include "Transaction.h"
00033 #include "TransactionP.h"
00034 #include "TransLog.h"
00035 #include "qof.h"
00036 #ifdef _MSC_VER
00037 # define g_fopen fopen
00038 #endif
00039 
00040 /*
00041  * Some design philosphy that I think would be good to keep in mind:
00042  * (0) Simplicity and foolproofness are the over-riding design points.
00043  *     This is supposed to be a fail-safe safety net.   We don't want
00044  *     our safety net to fail because of some whiz-bang shenanigans.
00045  *
00046  * (1) Try to keep the code simple.  Want to make it simple and obvious
00047  *     that we are recording everything that we need to record.
00048  *
00049  * (2) Keep the printed format human readable, for the same reasons.
00050  * (2.a) Keep the format, simple, flat, more or less unstructured,
00051  *       record oriented.  This will help parsing by perl scripts.
00052  *       No, using a perl script to analyze a file that's supposed to
00053  *       be human readable is not a contradication in terms -- that's
00054  *       exactly the point.
00055  * (2.b) Use tabs as a human friendly field separator; its also a
00056  *       character that does not (should not) appear naturally anywhere
00057  *       in the data, as it serves no formatting purpose in the current
00058  *       GUI design.  (hack alert -- this is not currently tested for
00059  *       or enforced, so this is a very unsafe assumption. Maybe
00060  *       urlencoding should be used.)
00061  * (2.c) Don't print redundant information in a single record. This
00062  *       would just confuse any potential user of this file.
00063  * (2.d) Saving space, being compact is not a priority, I don't think.
00064  *
00065  * (3) There are no compatibility requirements from release to release.
00066  *     Sounds OK to me to change the format of the output when needed.
00067  *
00068  * (-) print transaction start and end delimiters
00069  * (-) print a unique transaction id as a handy label for anyone
00070  *     who actually examines these logs.
00071  *     The C address pointer to the transaction struct should be fine,
00072  *     as it is simple and unique until the transaction is deleted ...
00073  *     and we log deletions, so that's OK.  Just note that the id
00074  *     for a deleted transaction might be recycled.
00075  * (-) print the current timestamp, so that if it is known that a bug
00076  *     occurred at a certain time, it can be located.
00077  * (-) hack alert -- something better than just the account name
00078  *     is needed for identifying the account.
00079  */
00080 /* ------------------------------------------------------------------ */
00081 
00082 
00083 static int gen_logs = 1;
00084 static FILE * trans_log = NULL; 
00085 static char * trans_log_name = NULL; 
00086 static char * log_base_name = NULL;
00087 
00088 /********************************************************************\
00089 \********************************************************************/
00090 
00091 void xaccLogDisable (void)
00092 {
00093     gen_logs = 0;
00094 }
00095 void xaccLogEnable  (void)
00096 {
00097     gen_logs = 1;
00098 }
00099 
00100 /********************************************************************\
00101 \********************************************************************/
00102 
00103 void
00104 xaccReopenLog (void)
00105 {
00106     if (trans_log)
00107     {
00108         xaccCloseLog();
00109         xaccOpenLog();
00110     }
00111 }
00112 
00113 
00114 void
00115 xaccLogSetBaseName (const char *basepath)
00116 {
00117     if (!basepath) return;
00118 
00119     g_free (log_base_name);
00120     log_base_name = g_strdup (basepath);
00121 
00122     if (trans_log)
00123     {
00124         xaccCloseLog();
00125         xaccOpenLog();
00126     }
00127 }
00128 
00129 
00130 /*
00131  * See if the provided file name is that of the current log file.
00132  * Since the filename is generated with a time-stamp we can ignore the
00133  * directory path and avoid problems with worrying about any ".."
00134  * components in the path.
00135  */
00136 gboolean
00137 xaccFileIsCurrentLog (const gchar *name)
00138 {
00139     gchar *base;
00140     gint result;
00141 
00142     if (!name || !trans_log_name)
00143         return FALSE;
00144 
00145     base = g_path_get_basename(name);
00146     result = (strcmp(base, trans_log_name) == 0);
00147     g_free(base);
00148     return result;
00149 }
00150 
00151 /********************************************************************\
00152 \********************************************************************/
00153 
00154 void
00155 xaccOpenLog (void)
00156 {
00157     char * filename;
00158     char * timestamp;
00159 
00160     if (!gen_logs) return;
00161     if (trans_log) return;
00162 
00163     if (!log_base_name) log_base_name = g_strdup ("translog");
00164 
00165     /* tag each filename with a timestamp */
00166     timestamp = xaccDateUtilGetStampNow ();
00167 
00168     filename = g_strconcat (log_base_name, ".", timestamp, ".log", NULL);
00169 
00170     trans_log = g_fopen (filename, "a");
00171     if (!trans_log)
00172     {
00173         int norr = errno;
00174         printf ("Error: xaccOpenLog(): cannot open journal \n"
00175                 "\t %d %s\n", norr, g_strerror (norr) ? g_strerror (norr) : "");
00176 
00177         g_free (filename);
00178         g_free (timestamp);
00179         return;
00180     }
00181 
00182     /* Save the log file name */
00183     if (trans_log_name)
00184         g_free (trans_log_name);
00185     trans_log_name = g_path_get_basename(filename);
00186 
00187     g_free (filename);
00188     g_free (timestamp);
00189 
00190     /*  Note: this must match src/import-export/log-replay/gnc-log-replay.c */
00191     fprintf (trans_log, "mod\ttrans_guid\tsplit_guid\ttime_now\t"
00192              "date_entered\tdate_posted\t"
00193              "acc_guid\tacc_name\tnum\tdescription\t"
00194              "notes\tmemo\taction\treconciled\t"
00195              "amount\tvalue\tdate_reconciled\n");
00196     fprintf (trans_log, "-----------------\n");
00197 }
00198 
00199 /********************************************************************\
00200 \********************************************************************/
00201 
00202 void
00203 xaccCloseLog (void)
00204 {
00205     if (!trans_log) return;
00206     fflush (trans_log);
00207     fclose (trans_log);
00208     trans_log = NULL;
00209 }
00210 
00211 /********************************************************************\
00212 \********************************************************************/
00213 
00214 void
00215 xaccTransWriteLog (Transaction *trans, char flag)
00216 {
00217     GList *node;
00218     char trans_guid_str[GUID_ENCODING_LENGTH + 1];
00219     char split_guid_str[GUID_ENCODING_LENGTH + 1];
00220     const char *trans_notes;
00221     char dnow[100], dent[100], dpost[100], drecn[100];
00222     Timespec ts;
00223 
00224     if (!gen_logs) return;
00225     if (!trans_log) return;
00226 
00227     timespecFromTime_t(&ts, time(NULL));
00228     gnc_timespec_to_iso8601_buff (ts, dnow);
00229 
00230     timespecFromTime_t(&ts, trans->date_entered.tv_sec);
00231     gnc_timespec_to_iso8601_buff (ts, dent);
00232 
00233     timespecFromTime_t(&ts, trans->date_posted.tv_sec);
00234     gnc_timespec_to_iso8601_buff (ts, dpost);
00235 
00236     guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
00237     trans_notes = xaccTransGetNotes(trans);
00238     fprintf (trans_log, "===== START\n");
00239 
00240     for (node = trans->splits; node; node = node->next)
00241     {
00242         Split *split = node->data;
00243         const char * accname = "";
00244         char acc_guid_str[GUID_ENCODING_LENGTH + 1];
00245         gnc_numeric amt, val;
00246 
00247         if (xaccSplitGetAccount(split))
00248         {
00249             accname = xaccAccountGetName (xaccSplitGetAccount(split));
00250             guid_to_string_buff(xaccAccountGetGUID(xaccSplitGetAccount(split)),
00251                                 acc_guid_str);
00252         }
00253         else
00254         {
00255             acc_guid_str[0] = '\0';
00256         }
00257 
00258         timespecFromTime_t(&ts, split->date_reconciled.tv_sec);
00259         gnc_timespec_to_iso8601_buff (ts, drecn);
00260 
00261         guid_to_string_buff (xaccSplitGetGUID(split), split_guid_str);
00262         amt = xaccSplitGetAmount (split);
00263         val = xaccSplitGetValue (split);
00264 
00265         /* use tab-separated fields */
00266         fprintf (trans_log,
00267                  "%c\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
00268                  "%s\t%s\t%s\t%s\t%c\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%s\n",
00269                  flag,
00270                  trans_guid_str, split_guid_str,  /* trans+split make up unique id */
00271                  /* Note that the next three strings always exist,
00272                                 * so we don't need to test them. */
00273                  dnow,
00274                  dent,
00275                  dpost,
00276                  acc_guid_str,
00277                  accname ? accname : "",
00278                  trans->num ? trans->num : "",
00279                  trans->description ? trans->description : "",
00280                  trans_notes ? trans_notes : "",
00281                  split->memo ? split->memo : "",
00282                  split->action ? split->action : "",
00283                  split->reconciled,
00284                  gnc_numeric_num(amt),
00285                  gnc_numeric_denom(amt),
00286                  gnc_numeric_num(val),
00287                  gnc_numeric_denom(val),
00288                  /* The next string always exists. No need to test it. */
00289                  drecn);
00290     }
00291 
00292     fprintf (trans_log, "===== END\n");
00293 
00294     /* get data out to the disk */
00295     fflush (trans_log);
00296 }
00297 
00298 /************************ END OF ************************************\
00299 \************************* FILE *************************************/
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines