|
GnuCash 2.4.99
|
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 *************************************/
1.7.4