Transaction Restore Help!

Linas Vepstas linas@linas.org
Mon, 16 Jul 2001 11:50:01 -0500


--QKdGvSO+nmPlgiQ/
Content-Type: multipart/mixed; boundary="7JfCtLOvnd9MIVvH"
Content-Disposition: inline


--7JfCtLOvnd9MIVvH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Sun, Jul 15, 2001 at 04:22:53PM -0400, Michael T. Garrison Stuber was he=
ard to remark:
> >
> > sort of. There is in 1.4, but its fallen into disrepair for 1.6.
> > Some of the engine api's changed.  Also, the repair tool is in perl,
> > and you have to go through a few extra steps to create the perl module
> > to the engine that would allow you use it.  (you need to build from
> > scratch, and run swig.  I don't know, its possible that the Makefiles
> > to automatically run swig have fallen into disrepair as well ...)
>=20
> Well assuming I want to take this on, where do I find "swig"?

www.swig.org

Its a tool for auto-generating perl bindings to C libraries.
(Its actaully more general than that, but that is all we have used it
for).

> found an onminous "last of swig" note in the CVS logs for the src/optiona=
l=20

!  I am not sure why swig was removed. It certainly is a handy dandy
tool for exactly this sort of stuff.  (The alternative is to learn
scheme, and do one's programming in that, but this is too much for some
people.)

I'll attach two swig files, and the log file parser from version=20
gnucash-1.4

--linas



--7JfCtLOvnd9MIVvH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="perl5_swig_annotations.i"
Content-Transfer-Encoding: quoted-printable

/*=20
 * FILE:
 * perl5_swig_annotations.i
 *
 * FUNCTION:
 * clean up various aspects of the gnucash engine interface with=20
 * respect to the SWIG-generated perl bindings. This includes:
 * -- handling time_t values as ints
 * -- converting C ** arrays to perl arrays
 *
 * HISTORY:
 * Created by Linas Vepstas January 1999
 * Copyright (c) 1999 Linas Vepstas=20
 */

#ifdef DOESNT_WORK_DONT_KNOW_WHY
%apply int {time_t }
#endif /* DOESNT_WORK_DONT_KNOW_WHY */

/* Convert the return values from the function to perl values */
/* specifically, create a new perl scalar and store the int value there */
%typemap(perl5, out) time_t {

  $target =3D newSViv ((IV) *($source));
  /*=20
   * An alternate way of writing this code would have been ...
   *    $target =3D sv_newmortal ();
   *    sv_setiv ($target, (IV) $source);
   */
  argvi ++;
  // printf ("Info: converted return time_t secs to %d \n", (int) SvIV($tar=
get));
}

%typemap(perl5, in) time_t   *(time_t temp)  {
  /* Convert function arguments from perl to the C represntation */
  /* in particular, convert perl scalar into integer, then cast to time_t */
  temp =3D (time_t) SvIV($source);
  $target =3D &temp;
  // printf ("Info: time_t input arg is %ld \n", * ($target));
}

/* --------------------------------------------------------- */

#ifdef DOESNT_WORK_DONT_KNOW_WHY
// well, this sort of works ... it does create an array,
// but it doesn't seem to be an array of SplitPtr,
// which is what we want if we are to have=20
//    foreach $split (@splits) {...}


// Creates a new Perl array and places a Split ** into it
%typemap(perl5,out) Split ** {
    AV *myav;
    SV **svs;
    int i =3D 0,len =3D 0;

    /* Figure out how many elements we have */
    while ($source[len]) len++;
    svs =3D (SV **) malloc(len*sizeof(SV *));
    printf ("Info: measured Split array length of len=3D%d\n", len);

    /* fill in array of scalar values */
    for (i =3D 0; i < len ; i++) {
        svs[i] =3D sv_newmortal();
        sv_setref_pv (svs[i], "SplitPtr", $source[i]);
    };

    /* convert array of scalars into perl array */
    myav =3D  av_make(len,svs);
    free(svs);
    // $target =3D myav;
    $target =3D newRV((SV*)myav);
    sv_2mortal($target);
    argvi ++;
}

#endif /* DOESNT_WORK_DONT_KNOW_WHY */

--7JfCtLOvnd9MIVvH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="gnucash-swig-defines.c"

#define sv_undef PL_sv_undef
#define sv_yes PL_sv_yes
#define na PL_na

--7JfCtLOvnd9MIVvH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="helperfuncs.c"

#include "helperfuncs.h"

FILE *
get_fileptr_stdin() {
  return stdin;
}

FILE *
get_fileptr_stdout() {
  return stdout;
}

FILE *
get_fileptr_stderr() {
  return stderr;
}

--7JfCtLOvnd9MIVvH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="helperfuncs.h"

#ifndef __HELPERFUNCS_H__
#define __HELPERFUNCS_H__

#include <stdio.h>

FILE *get_fileptr_stdin();
FILE *get_fileptr_stdout();
FILE *get_fileptr_stderr();

#endif

--7JfCtLOvnd9MIVvH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="Makefile.am"


docdir = ${GNC_DOC_INSTALL_DIR}
doc_DATA = \
  gnucash.engine_wrap.doc

perlsharedir = ${GNC_SHAREDIR}/perl
perlshare_DATA = gnucash.pm

perllibdir = ${GNC_LIBDIR}/perl

perllib_LTLIBRARIES = libgncswig.la

libgncswig_la_SOURCES = \
  helperfuncs.c \
  gnucash-engine-perl5_wrap.c

libgncswig_la_LDFLAGS = -version-info 1:1:1

noinst_HEADERS = \
  helperfuncs.h

EXTRA_DIST = \
  .cvsignore \
  examples/scan-acct.pl \
  perl5_swig_annotations.i \
  gnucash-swig-defines.c

CFLAGS = @CFLAGS@ ${GNOME_CFLAGS} -Dbool=char

INCLUDES = \
  -I@PERLINCL@/CORE \
  -I${top_srcdir}/src/engine

libgncswig_la_LIBADD = \
  @GLIB_LIBS@ \
  ${top_srcdir}/src/engine/AccInfo.lo \
  ${top_srcdir}/src/engine/Account.lo \
  ${top_srcdir}/src/engine/DateUtils.lo \
  ${top_srcdir}/src/engine/FileIO.lo \
  ${top_srcdir}/src/engine/GNCId.lo \
  ${top_srcdir}/src/engine/Group.lo \
  ${top_srcdir}/src/engine/Query.lo \
  ${top_srcdir}/src/engine/Queue.lo \
  ${top_srcdir}/src/engine/Scrub.lo \
  ${top_srcdir}/src/engine/Session.lo \
  ${top_srcdir}/src/engine/TransLog.lo \
  ${top_srcdir}/src/engine/Transaction.lo \
  ${top_srcdir}/src/engine/Backend.lo \
  ${top_srcdir}/src/engine/date.lo \
  ${top_srcdir}/src/engine/util.lo \
  ${top_srcdir}/src/engine/guid.lo \
  ${top_srcdir}/src/engine/md5.lo

SWIG_INPUT_HDRS := \
  ${top_srcdir}/src/engine/AccInfo.h \
  ${top_srcdir}/src/engine/Account.h \
  ${top_srcdir}/src/engine/DateUtils.h \
  ${top_srcdir}/src/engine/FileIO.h \
  ${top_srcdir}/src/engine/GNCId.h \
  ${top_srcdir}/src/engine/Group.h \
  ${top_srcdir}/src/engine/Query.h \
  ${top_srcdir}/src/engine/Queue.h \
  ${top_srcdir}/src/engine/Scrub.h \
  ${top_srcdir}/src/engine/Session.h \
  ${top_srcdir}/src/engine/TransLog.h \
  ${top_srcdir}/src/engine/Transaction.h \
  ${top_srcdir}/src/engine/date.h

## This is kinda ugly, but swig insists that the .so file be named the
## same as the .pm file, and automake won't tolerate libraries that
## aren't named lib*.so...
install-data-local: libgncswig.la
	cd ${DESTDIR}${perllibdir} && rm -f gnucash.so
	if [ -f ${DESTDIR}${perllibdir}/libgncswig.so ]; then \
		cd ${DESTDIR}${perllibdir} && ln -s libgncswig.so gnucash.so; \
	elif [ -f ${DESTDIR}${perllibdir}/libgncswig.so.1.1 ]; then \
		cd ${DESTDIR}${perllibdir} && \
                ln -s libgncswig.so.1.1 gnucash.so; \
	else \
		echo "Can't find libgncswig shared library!" \
		exit 1; \
	fi

uninstall-local:
	rm -f ${DESTDIR}${perllibdir}/gnucash.so

gnucash.engine.i: ${SWIG_INPUT_HDRS} perl5_swig_annotations.i
	@echo Making $@.
	@echo "%module gnucash" > $@
	@echo "%include perl5_swig_annotations.i" >> $@
	@echo "%{" >> $@
	@for file in ${SWIG_INPUT_HDRS}; \
        do \
          echo "#include <`basename $$file`>" >> $@; \
	done
	@echo "%}" >> $@
	@for file in ${SWIG_INPUT_HDRS}; \
        do \
          echo "%include `basename $$file`" >> $@; \
	done

CLEANFILES += gnucash.engine.i

gnucash.engine_wrap.doc gnucash-engine-perl5_wrap_int.c: gnucash.engine.i
	${SWIG} -perl5 -I.. -I${top_srcdir}/src/engine -o $@ gnucash.engine.i
CLEANFILES += gnucash.engine_wrap.doc gnucash-engine-perl5_wrap_int.c

gnucash-engine-perl5_wrap.c: gnucash-swig-defines.c \
                             gnucash-engine-perl5_wrap_int.c
	cat gnucash-swig-defines.c gnucash-engine-perl5_wrap_int.c > $@
CLEANFILES += gnucash-engine-perl5_wrap.c

CLEANFILES += gnucash.pm gnucash.so

# We have to do this because otherwise automake insists on putting
# these files into the dist tarfile.  If there's a a better way,
# by all means, let us know...
dist-hook:
	rm -f ${distdir}/gnucash-engine-perl5_wrap.c

--7JfCtLOvnd9MIVvH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="gnc-restore.pl"
Content-Transfer-Encoding: quoted-printable

#! /usr/bin/perl
#
# FUNCTION:
# restore gnucash transactions from a gnucash log file.
#
# Usage: cat <logfile> | gnc-restore.pl <gnucash-filename>
#
# Warning! this script probably does the wrong thing,=20
# and has never been tested!!
# It will probably destroy your data!  Use at your own risk!
#

# hack alert -- fix the paths
# set the path below to where your gnucash.pm is located
use lib '/usr/local/gnucash-1.4/lib/gnucash/perl';
use lib '/usr/local/gnucash-1.4/share/gnucash/perl';
use gnucash;

 =20
die "Usage: cat <logfile> | $0 <gnucash-filename>" if $#ARGV < 0;
 =20
# open the file
print "Opening file $ARGV[0]\n";
$sess =3D gnucash::xaccMallocSession ();
$grp =3D gnucash::xaccSessionBeginFile ($sess,$ARGV[0]);

die "failed to read file $ARGV[0], maybe its locked? " if (! $grp);

$got_data =3D 0;
$nsplit =3D 0;

# --------------------------------------------------
while (<STDIN>) {

  # start of transaction
  if (/^=3D=3D=3D=3D=3D START/) {=20
     $nsplit =3D 0;
     next;=20
  }

  # end of transaction
  if (/^=3D=3D=3D=3D=3D END/) {=20
     if ($got_data =3D=3D 1) {
        gnucash::xaccTransCommitEdit ($trans);
     }
     $got_data =3D 0;
     next;=20
  }
 =20
  # ignore 'begin' lines
  if (/^B/) { next; }
  if (/^D/) {=20
    print "WARNING: deletes not handled, you will have to manually delete\n=
";
    next;=20
  }

  # ignore any line that's not a 'commit'
  if (!/^C/) { next; }
 =20
  chop;

  # get journal entry
  ($mod, $id, $time_now, $date_entered, $date_posted,
   $account, $num, $description, $memo, $action,=20
   $reconciled, $amount, $price, $date_reconciled)
    =3D split (/	/);

  # parse amount & price=20
  # gnucash-1.4.x : float point;  gnucash-1.5.x: fractional ratio
  ($anum, $adeno) =3D split (/\//, $amount);
  if (0 !=3D $adeno) {
     $amount =3D $anum / $adeno;
  }
   =20
  ($pnum, $pdeno) =3D split (/\//, $price);
  if (0 !=3D $pdeno) {
     $price =3D $pnum / $pdeno;
     # value, not price in gnucash-1.5.x
     if (0 !=3D $amount) {
        $price =3D $price/$amount;
     }
  }

  $dyear =3D int($date_posted/10000000000);
  $dmonth =3D int($date_posted/100000000) - 100*$dyear;
  $dday =3D int($date_posted/1000000) - 10000*$dyear - 100*$dmonth;
 =20
  $dpost =3D $dmonth . "/" . $dday . "/" . $dyear;

  # do a 'commit'
  if ($mod =3D=3D C) {=20
     print "restoring '$account' $dpost '$description' for $amount at price=
 $price\n";
    =20
     if ($got_data =3D=3D 0) {
        $trans =3D gnucash::xaccMallocTransaction();
        gnucash::xaccTransBeginEdit( $trans, 1);
        $got_data =3D 1;
     }

     gnucash::xaccTransSetDescription( $trans, $description);
     gnucash::xaccTransSetDateStr ($trans, $dpost);
     gnucash::xaccTransSetNum ($trans, $num);

     if ($nsplit =3D=3D 0) {
        $split =3D gnucash::xaccTransGetSplit ($trans, $nsplit);
     } else {
        $split =3D gnucash::xaccMallocSplit();
	gnucash::xaccTransAppendSplit($trans, $split);
     }
     gnucash::xaccSplitSetAction ($split, $action);
     gnucash::xaccSplitSetMemo ($split, $memo);
     gnucash::xaccSplitSetReconcile ($split, $reconciled);

     # hack alert -- fixme: the reconcile date is not set ...
     # need to convert date_reconciled to 'seconds' ...=20
     # gnucash::xaccSplitSetDateReconciled ($split, $date_reconciled);
     gnucash::xaccSplitSetSharePriceAndAmount($split, $price, $amount);

     $acct =3D gnucash::xaccGetAccountFromName ($grp,$account);
     gnucash::xaccAccountBeginEdit ($acct, 1);
     gnucash::xaccAccountInsertSplit ($acct, $split);
     gnucash::xaccAccountCommitEdit ($acct);
 =20
     $nsplit ++;
  }

}

gnucash::xaccSessionSave ($sess);
gnucash::xaccSessionEnd ($sess);



--7JfCtLOvnd9MIVvH--

--QKdGvSO+nmPlgiQ/
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD4DBQE7Uxs5ZKmaggEEWTMRAuE/AJiATM1sJNQk0PUPP+WJOVpJhhQ0AJwPCGQd
lhJzw6dJ4V4ZwwqbbeMr+w==
=yjUb
-----END PGP SIGNATURE-----

--QKdGvSO+nmPlgiQ/--