diff --git a/AUTHORS b/AUTHORS index aea8e69..14b8b3c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,19 +1,20 @@ Original version by: Dave Smith Dave Smith Current maintainer: Carl Byington With contributions by: Joseph Nahmias -- bounces Joseph Nahmias Arne Ahrend Nigel Horne Chris Halls Stevens Miller Brad Hards Alexander Grau Antonio Palama Sean Loaring James Woodcock + Joachim Metz diff --git a/ChangeLog b/ChangeLog index 041d865..2bce645 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,553 +1,560 @@ +LibPST 0.6.9 (2008-05-11) +=============================== + * Patch from Joachim Metz for 64 bit + compile + * Signed/unsigned cleanup from 'CFLAGS=-Wextra ./configure' + * Reindent vbuf.c to make it readable. + LibPST 0.6.8 (2008-03-05) =============================== * Initial version of pst2dii to convert to Summation dii load file format. * Changes for Fedora packaging (#434727) LibPST 0.6.7 (2008-02-16) =============================== * Work around bogus 7c.b5 blocks in some messages that have been read. They appear to have attachments, but of some unknown format. Before the message was read, it did not have any attachments. * Use autoscan to cleanup our autoconf system. * Use autoconf to detect when we need to use our XGetopt files and other header files. * More fields, including BCC. * Fix missing LE32_CPU byte swapping for FILETIME types. LibPST 0.6.6 (2008-01-31) =============================== * More code cleanup, removing unnecessary null terminations on binary buffers. All pst file reads now go thru one function. Logging all pst reads to detect cases where we read the same data multiple times - discovers node sizes are actually 512 bytes. * Switch from cvs to mercurial source control. LibPST 0.6.5 (2008-01-22) =============================== * More code cleanup, removing obsolete code. All the boolean flags of type 0xb have length 4, so these are all 32 bits in the file. Libpst treats them all as 16 bits, but at least we are consistent. * More fields decoded - for example, see We should be able to use that data for much more complete decoding. * Move the rpm group to Applications/Productivity consistent with Evolution. LibPST 0.6.4 (2008-01-19) =============================== * More fixes for Outlook 2003 64 bit parsing. We observed cases of compressed RTF bodies (type 0x1009) with zero length. * Document type 0x0101 descriptor blocks and process them. * Fix large file support - we need to include config.h before any standard headers. * Merge following changes from svn snapshot from Alioth: * Add new fields to appointment for recurring events (SourceForge #304198) * Map IPM.Task items to PST_TYPE_TASK. * Applied patch to remove compiler warnings, thanks! (SourceForge #304314) * Fix crash with unknown reference type * Fix more memory issues detected by valgrind * lspst - add usage mesage and option parsing using getopt (SourceForge #304199) * Fix crash caused by invalid free calls * Fix crash when email subject is empty * Fix memory and information leak in hex debug dump LibPST 0.6.3 (2008-01-13) =============================== * More type consistency issues found by splint. LibPST 0.6.2 (2008-01-12) =============================== * More fixes for Outlook 2003 64 bit parsing. * All buffer sizes changed to size_t, all file offsets changed to off_t, all function names start with pst_, many other type consistency issues found by splint. Many changes to #llx in debug printing for 64 bit items. All id values are now uint64_t. LibPST 0.6.1 (2008-01-06) =============================== * Outlook 2003 64 bit parsing. Some documentation from Alexander Grau and patches from Sean Loaring . * fix from Antonio Palama for email items that happen to have item->contact non null, and were being processed as contacts. * Add large file support so we can read .pst files larger than 2gb. * Change lspst to be similar to readpst, properly using recursion to walk the tree, and testing item types. Add a man page for lspst. LibPST 0.5.12 (2007-10-02) =============================== * security fix from Brad Hards for buffer overruns in liv-zemple decoding for corrupted or malicious pst files. LibPST 0.5.11 (2007-08-24) =============================== * fix from Stevens Miller for unitialized variable. LibPST 0.5.10 (2007-08-20) =============================== * fix yet more valgrind errors - finally have a clean memory check. * restructure readpst.c for proper recursive tree walk. * buffer overrun test was backwards, introduced at 0.5.6 * fix broken email attachments, introduced at 0.5.6 LibPST 0.5.9 (2007-08-12) =============================== * fix more valgrind errors. LibPST 0.5.8 (2007-08-10) =============================== * fix more valgrind errors. lzfu_decompress needs to return the actual buffer size, since the lz header overestimates the size. This caused base64_encode to encode undefined bytes into the email attachment. LibPST 0.5.7 (2007-08-09) =============================== * fix valgrind errors, using uninitialized data. * improve debug logging and readpstlog for indented listings. * cleanup documentation. LibPST 0.5.6 (2007-07-15) =============================== * Fix to allow very small pst files with only one node in the tree. We were mixing signed/unsigned types in comparisons. * More progress decoding the basic structure 7c blocks. Many four byte values may be ID2 indices with data outside the buffer. * Start using doxygen to generate internal documentation. LibPST 0.5.5 (2007-07-10) =============================== * merge the following changes from Joe Nahmias version: * Lots of memory fixes. Thanks to Nigel Horne for his assistance tracking these down! * Fixed creation of vCards from contacts, thanks to Nigel Horne for his help with this! * fix for MIME multipart/alternative attachments. * added -c options to readpst manpage. * use 8.3 attachment filename if long filename isn't available. * new -b option to skip rtf-body.rtf attachments. * fix format of From header lines in mbox files. * Add more appointment fields, thanks to Chris Halls for tracking them down! LibPST 0.5.4 (2006-02-25) =============================== * patches from Arne, adding MH mode, remove leading zeros from the generated numbered filenames starting with one rather than zero. Miscellaneous code cleanup. * document the "7c" descriptor block format. LibPST 0.5.3 (2006-02-20) =============================== * switch to gnu autoconf/automake. This breaks the MS VC++ projects since the source code is now in the src subdirectory. * documentation switched to xml, building man pages and html from the master xml copy. * include rpm .spec file for building src and binary rpms. LibPST 0.5.2 (2006-02-18) =============================== * Added pst2ldif to convert the contacts to ldif format for import into ldap databases. * Major changes to libpst.c to properly use the node depth values from the b-tree nodes. We also use the item count values in the nodes rather than trying to guess how many items are active. * Cleanup whitespace - using tabs for every four columns. LibPST 0.5.1 (17 November 2004) =============================== Well, alot has happened since the last release of libpst. Release / Management: * The project has forked! The new maintainer is Joseph Nahmias. * We have changed hosting sites, thanks to sourceforge for hosting to this point. From this point forward we will be using alioth.debian.org. * The project is now using SubVersioN for source control. You can get the latest code by running: svn co svn://svn.debian.org/svn/libpst/trunk . * See for more information. Code Changes: * Added lspst program to list items in a PST. Still incomplete. * Added vim folding markers to readpst.c * avoid the pseudo-prologue that MS prepends to the email headers * fix build on msvc, since it doesn't have sys/param.h * Re-vamped Makefile: * Only define CFLAGS in Makefileif missing * fixed {un,}install targets in Makefile * Fixed up build process in Makefile * Added mozilla conversion script from David Binard * Fixed bogus creation of readpst.log on every invocation * escaped dashes and apostrophe in manpages * Updated TODO * added manpages from debian pkg * fix escaped-string length count to consider '\n', thanks to Paul Bakker . * ensure there's a blank line between header and body patch from (SourceForge #890745). * Apply accumulated endian-related patches * Removed unused files, upstream's debian/ dir -- Joe Nahmias LibPST v0.5 =========== It is with GREAT relief that I bring you version 0.5 of the LibPST tools! Through great difficulties, this tool has survived and expanded to become even better. The changes are as follows: * RTF support. We can now decompress RTF bodies in emails, and are saved as attachments * Better support in reading the indexes. Fixed many bugs with them * Improved reliability. "Now we are getting somewhere!" * Improved compiling. Hopefully we won't be hitting too many compile errors now. * vCard handling. Contacts are now exported as vCard entries. * vEvent handling. Support has begun on exporting Calendar entries as events * Support for Journal entries has also begun If you have any problems with this release, don't hesitate to contact me. These changes come to you, as always, free under the GPL license!! What a wonderful thing it is. It does mean that you can write your own program off of this library and distribute it also for free. However, anyone with commercial interests for developing applications they will be charging for are encouraged to get in touch with me, as I am sure we can come to some arrangement. Dave Smith LibPST v0.4.3 ============= Bug fix release. No extra functionality Dave Smith LibPST v0.4.2 ============= The debug system has had an overhaul. The debug messages are no longer printed to the screen when they are enabled. They are dumped to a binary file. There is another utility called "readlog" that I have written to handle these log files. It should make it easier to selectively view bits of a log file. It also shows the position that the log message was printed from. There is a new switch in readpst. It is -d. It enables the user to specify the log file which the binary log is written to. If the switch isn't used, the default file of "readpst.log" is used. The code is now Visual C++ compatible. It has compiled on Visual C++ .net Standard edition, and produces the readpst.exe file. Use the project file included in this distribution. There have been minor improvements elsewhere too. LibPST v0.4.1 ============= Fixed a couple more bugs. Is it me or do bugs just insert themselves in random, hard to find places! Cured a few problems with regard to emails with multiple embeded items. They are not fully re-created using Mime-types, but are accessible with the -S switch (which saves everything as seperate items) Fixed a problem reading the first index. Back sliders are now detected. (ie when the value following the current one is smaller, not bigger!) Added some error messages when we try and read outside of the PST file, this was causing a few problems before, cause the return value wasn't always checked, so it was possible to be reading random data, and trying to make sense of it! Anyway, if you find any problems, don't hesitate to mail me Dave Smith LibPST v0.4 =========== Fixed a nasty bug that occasionally corrupted attachments. Another bug with regard to reading of indexes (also occasional). Another output method has been added which is called "Seperate". It is activated with the -S switch. It operates in the following manor: |--Inbox-->000000 | 000001 | 000002 |--Sentmail-->0000000 | 0000001 | 0000002 All the emails are stored in seperate files counting from 0 upwards, in a folder named as the PST folder. When an email has an attachment, it is saved as a seperate file. The filename for the attachment is made up of 2 parts, the first is the email number to which it belongs, the second is its filename. The should now be runnable on big-endian machines, if the define.h file is first modified. The #define LITTLE_ENDIAN must be commented out, and the #define BIG_ENDIAN must be uncommented. More verbose error messages have been added. Apparently people got confused when the program stopped for no visible reason. This has now been resolved. Thanks for the continued support of all people involved. Dave Smith Libpst v0.3.4 ============= Several more fixes. An Infinite loop and incorrect interpreting of item index attributes. Work has started on making the code executable on big endian CPUs. At present it should work with Linux on these CPUs, but I would appreciate it if you could provide feedback with regard to it's performance. I am also working with some other people at make it operate on Solaris. A whole load more items are now recognized by the Item records. With more items in Emails and Folders. I haven't got to the Contacts yet. Anyway, this is what I would call a minor feature enhancment and bugfix release. Dave Smith LibPST v0.3.3 ============= Fixed several items. Mainly memory leaks. Loads of them! oops.. I have added a new program, mainly of debugging, which when passed an ID value and a pst file, will extract and decrypt that ID from the pst file. I don't see it being a huge attraction, or of much use to most people, but it is another example of writing an application to use the libpst interface. Another fix was in the reading of the item index. This has hopefully now been corrected. The result of this bug was that not all the emails in a folder were converted. Hopefully you should have more luck now. Dave Smith LibPST v0.3.2 ============= Quick bugfix release. There was a bug in the decryption of the basic encryption that outlook uses. One byte, 0x6c, was incorrectly decrypted to 0x6c instead of 0xcd. This release fixes this bug. Sorry... LibPST v0.3.1 ============= Minor improvements. Fixed bug when linking multiple blocks together, so now the linking blocks are not "encrypted" when trying to read them. LibPST v0.3 =========== A lot of bug fixing has been done for this release. Testing has been done on the creation of the files by readpst. Better handling of large binaries being extracted from the PST file has been implemented. Quite a few reports have come in about not being able to compile on Darwin. This could be down to using macros with variable parameter lists. This has now been changed to use C functions with variable parameters. I hope this fixes a lot of problems. Added support for recreating the folder structure into normal directories. For Instance: Personal Folders |-Inbox | |-Jokes | |-Meetings |-Send Items each folder containing an mbox file with the correct emails for that folder. Dave Smith LibPST v0.3 beta1 ================= Again, a shed load of enhancements. More work has been done on the mime creation. A bug has been fixed that was letting part of the attachments that were created disappear. A major enhancement is that "compressible encryption" support has been added. This was an incredibly simple method to use. It is basically a ceasar cipher. It has been noted by several users already that the PST password that Outlook uses, serves *no purpose*. It is not used to encrypt the PST, it is mearly stored there. This means that the readpst application is able to convert PST files without knowing the password. Microsoft have some explaning to do! Output files are now not overwritten if they already exist. This means that if you have two folders in your PST file named "fred", the first one encountered will be named "fred" and the second one will be named "fred00000001". As you can see, there is enough room there for many duplicate names! Output filenames are now restricted. Any "/" or "\" characters in the name are replaced with "_". If you find that there are any other characters that need to be changed, could you please make me aware! Thanks to Berry Wizard for help with supporting the encryption. Thanks to Auke Kok, Carolus Walraven and Yogesh Kumar Guatam for providing debugging information and testing. Dave Smith LibPST v0.2 beta1 ================= Hello once more... Attachments are now re-created in mime format. The method is very crude and could be prone to over generalisation. Please test this version, and if attachments are not recreated correctly, please send me the email (complete message source) of the original and converted. Cheers. I hope this will work for everyone who uses this program, but reality can be very different! Let us see how it goes... Dave Smith LibPST v0.2 alpha1 =========== Hello! Some improvements. The internal code has been changed so that attachments are now processed and loaded into the structures. The readpst program is not finished yet. It needs to convert these binary structs into mime data. At present it just saves them to the current directory, overwriting any previous files with the attachment name. Improvements over previous version: * KMail output is supported - if the "-k" flag is specified, all the directory hierarchy is created using the KMail standard * Lots of bugs and memory leaks fixed Usage: ReadPST v0.2alpha1 implementing LibPST v0.2alpha1 Usage: ./readpst [OPTIONS] {PST FILENAME} OPTIONS: -h - Help. This screen -k - KMail. Output in kmail format -o - Output Dir. Directory to write files to. CWD is changed *after* opening pst file -V - Version. Display program version If you want to view lots of debug output, modify a line in "define.h" from "//#define DEBUG_ALL" to "#define DEBUG_ALL". It would then be advisable to pipe all output to a log file: ./readpst -o out pst_file &> logfile Dave Smith LibPST v0.1 =========== Hi Folks! This has been a long, hard slog, but I now feel that I have got somewhere useful. The included program "main" is able to read an Outlook PST file and dump the emails into mbox files, separating each folder into a different mbox file. All the mbox files are stored in the current directory and no attempt is yet made to organise these files into a directory hierarchy. This would not be too difficult to achieve though. Email attachments are not yet handled, neither are Contacts. There is no pretty interface yet, but you can convert a PST file in the following manner ./main {path to PST file} This is very much a work in progress, but I thought I should release this code so that people can lose their conception that outlook files will never be converted to Linux. I am intending that the code I am writing will be developed into greater applications to provide USEFUL tools for accessing and converting PST files into a variety of formats. One point I feel I should make is that Outlook, by default, creates "Compressible Encryption" PST files. I have not, as yet, attempted to write any decryption routines, so you will not be able to convert these files. However, if you create a new PST file and choose not to make an encrypted one, you can copy all your emails into this new one and then convert the unencrypted one. I hope you enjoy, Dave Smith diff --git a/NEWS b/NEWS index 64a5710..5445c65 100644 --- a/NEWS +++ b/NEWS @@ -1,19 +1,20 @@ +0.6.9 2008-05-11 Patch from Joachim Metz for 64 bit compile. 0.6.8 2008-03-05 Initial version of pst2dii to convert to Summation dii load file format. 0.6.7 2008-02-16 Ignore unknown attachments on some read messages; autoconf cleanup. 0.6.6 2008-01-31 Code cleanup, switch from cvs to mercurial source control. 0.6.5 2008-01-22 Code cleanup, rpm group Applications/Productivity. 0.6.4 2008-01-19 More fixes for 64 bit format, merge changes from svn Alioth. 0.6.3 2008-01-13 More type consistency issues found by splint. 0.6.2 2008-01-12 More fixes for 64 bit format, consistent types size_t, off_t, etc. 0.6.1 2008-01-06 Outlook 2003 64 bit format and fix for bogus contacts. 0.5.12 2007-10-02 security fix for possible buffer overruns in liv-zemple decoding 0.5.11 2007-08-24 fix for unitialized variable 0.5.10 2007-08-20 fix yet more valgrind errors, restructure readpst recursive walk, backwards overrun test 0.5.9 2007-08-12 fix more valgrind errors, pst2ldif wrote undefined data 0.5.8 2007-08-10 lzfu_decompress/base64_encode encoded random data into attachment 0.5.7 2007-08-09 fix valgrind errors, using uninitialized data 0.5.6 2007-07-15 handle small pst files, better decoding of 7c blocks 0.5.5 2007-07-10 merge changes from Joe Nahmias version 0.5.4 2006-02-25 add MH mode, generated filenames with no leading zeros 0.5.3 2006-02-20 switch to gnu autoconf/automake 0.5.2 2006-02-18 add pst2ldif, fix btree processing in libpst.c diff --git a/configure.in b/configure.in index 9c49ad6..0d26bce 100644 --- a/configure.in +++ b/configure.in @@ -1,88 +1,88 @@ AC_PREREQ(2.59) -AC_INIT(libpst,0.6.8,carl@five-ten-sg.com) +AC_INIT(libpst,0.6.9,carl@five-ten-sg.com) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE($PACKAGE_NAME,$PACKAGE_VERSION) # Checks for programs. my_build_dii=yes AC_PATH_PROG(CONVERT, convert) if test "x$CONVERT" = "x" ; then AC_MSG_WARN([convert not found. pst2dii disabled]) my_build_dii=no fi AM_CONDITIONAL(BUILD_DII, test $my_build_dii = yes) # Checks for programs. AC_PROG_CXX AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_RANLIB AC_SYS_LARGEFILE # Checks for header files. AC_CHECK_HEADER([unistd.h], AM_CONDITIONAL(NEED_XGETOPT, [test yes = no ]), AM_CONDITIONAL(NEED_XGETOPT, [test yes = yes]) ) AC_HEADER_DIRENT AC_HEADER_STDC AC_CHECK_HEADERS([fcntl.h limits.h malloc.h netinet/in.h stdint.h stdlib.h string.h sys/param.h wchar.h]) if test "$my_build_dii" = "yes"; then AC_CHECK_HEADERS([gd.h]) fi # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_C_CONST AC_TYPE_OFF_T AC_TYPE_SIZE_T AC_STRUCT_TM # Checks for library functions. AC_FUNC_LSTAT AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK AC_FUNC_MALLOC AC_FUNC_MKTIME AC_FUNC_REALLOC AC_FUNC_STRFTIME AC_FUNC_VPRINTF AC_CHECK_FUNCS([memchr memmove memset strcasecmp strchr strdup strerror strpbrk strrchr strstr strtol]) # The following lines adds the --enable-pst-debug option to configure: # # Give the user the choice to enter one of these: # --enable-pst-debug # --enable-pst-debug=yes # --enable-pst-debug=no # AC_MSG_CHECKING([whether we are forcing debug dump file creation]) AC_ARG_ENABLE(pst-debug, AC_HELP_STRING([--enable-pst-debug], [force debug dump file creation]), [if test "${enable_pst_debug}" = "no" ; then AC_MSG_RESULT([no]) else AC_DEFINE(DEBUG_ALL, 1, Define to 1 to force debug dump file creation) AC_MSG_RESULT([yes]) fi], # Default value for configure AC_MSG_RESULT([no]) ) AC_OUTPUT( \ Makefile \ libpst.spec \ html/Makefile \ info/Makefile \ man/Makefile \ src/Makefile \ src/version.h \ src/pst2dii.cpp \ xml/Makefile \ xml/libpst \ ) diff --git a/libpst.spec.in b/libpst.spec.in index 4e8c45d..5293a63 100644 --- a/libpst.spec.in +++ b/libpst.spec.in @@ -1,59 +1,62 @@ Summary: Utilities to convert Outlook .pst files to other formats Name: @PACKAGE@ Version: @VERSION@ Release: 1%{?dist} License: GPLv2+ Group: Applications/Productivity Source: http://www.five-ten-sg.com/%{name}/packages/%{name}-%{version}.tar.gz BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) URL: http://www.five-ten-sg.com/%{name}/ Requires: ImageMagick BuildRequires: ImageMagick freetype-devel gd-devel libjpeg-devel zlib-devel %description The Libpst utilities include readpst which can convert email messages to both mbox and MH mailbox formats, pst2ldif which can convert the contacts to .ldif format for import into ldap databases, and pst2dii which can convert email messages to the DII load file format used by Summation. %prep %setup -q %build %configure make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %{_bindir}/* %{_mandir}/man1/* %{_mandir}/man5/* %docdir %{_datadir}/doc/%{name}-%{version} %{_datadir}/doc/%{name}-%{version} %changelog +* Sun May 11 2008 Carl Byington - 0.6.9 +- Patch from Joachim Metz for 64 bit compile. + * Wed Mar 05 2008 Carl Byington - 0.6.8 - Initial version of pst2dii to convert to Summation dii load file format - changes for Fedora packaging guidelines (#434727) * Tue Jul 10 2007 Carl Byington - 0.5.5 - merge changes from Joe Nahmias version * Sun Feb 19 2006 Carl Byington - 0.5.3 - initial spec file using autoconf and http://www.fedora.us/docs/rpm-packaging-guidelines.html diff --git a/src/debug.c b/src/debug.c index db50d88..2e8220f 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,439 +1,439 @@ #include "define.h" #include #include #include #include #include #include #ifdef _WIN32 # define vsnprintf _vsnprintf #endif struct pst_debug_item { int type; char * function; unsigned int line; char * file; char * text; struct pst_debug_item *next; } *item_head=NULL, *item_tail=NULL, *item_ptr=NULL, *info_ptr=NULL, *temp_list=NULL; struct pst_debug_func { char * name; struct pst_debug_func *next; } *func_head=NULL, *func_ptr=NULL; void pst_debug_write_msg(struct pst_debug_item *item, char *fmt, va_list *ap, int size); void pst_debug_write_hex(struct pst_debug_item *item, unsigned char *buf, size_t size, int col); void * xmalloc(size_t size); // the largest text size we will store in memory. Otherwise we // will do a debug_write, then create a new record, and write the // text body directly to the file #define MAX_MESSAGE_SIZE 4096 void pst_debug(char *fmt, ...) { va_list ap; va_start(ap,fmt); vfprintf(stderr, fmt, ap); va_end(ap); } #define NUM_COL 30 void pst_debug_hexdumper(FILE *out, unsigned char *buf, size_t size, int col, int delta) { - int off = 0, toff; + size_t off = 0, toff; int count = 0; if (!out) return; // no file if (col == -1) col = NUM_COL; fprintf(out, "\n"); while (off < size) { fprintf(out, "%06X\t:", off+delta); toff = off; while (count < col && off < size) { fprintf(out, "%02hhx ", buf[off]); off++; count++; } off = toff; while (count < col) { // only happens at end of block to pad the text over to the text column fprintf(out, " "); count++; } count = 0; fprintf(out, ":"); while (count < col && off < size) { fprintf(out, "%c", isgraph(buf[off])?buf[off]:'.'); off++; count ++; } fprintf(out, "\n"); count=0; } fprintf(out, "\n"); } FILE *debug_fp = NULL; unsigned int max_items=DEBUG_MAX_ITEMS, curr_items=0; void pst_debug_init(char* fname) { unsigned char version = DEBUG_VERSION; item_head = item_tail = NULL; curr_items = 0; if (debug_fp) pst_debug_close(); if (!fname) return; if ((debug_fp = fopen(fname, "wb")) == NULL) { fprintf(stderr, "Opening of file %s failed\n", fname); exit(1); } fwrite(&version, 1, sizeof(char), debug_fp); } // function must be called before pst_debug_msg. It sets up the // structure for the function that follows void pst_debug_msg_info(int line, char* file, int type) { char *x; if (!debug_fp) return; // no file info_ptr = (struct pst_debug_item*) xmalloc(sizeof(struct pst_debug_item)); info_ptr->type = type; info_ptr->line = line; x = (func_head==NULL?"No Function":func_head->name); info_ptr->function = (char*) xmalloc(strlen(x)+1); strcpy(info_ptr->function, x); info_ptr->file = (char*) xmalloc(strlen(file)+1); strcpy(info_ptr->file, file); //put the current record on a temp linked list info_ptr->next = temp_list; temp_list = info_ptr; } void pst_debug_msg_text(char* fmt, ...) { va_list ap; int f, g; char x[2]; struct pst_debug_item *temp; if (!debug_fp) return; // no file va_start(ap, fmt); // get the record off of the temp_list info_ptr = temp_list; if (info_ptr) temp_list = info_ptr->next; else { fprintf(stderr, "NULL info_ptr. ERROR!!\n"); exit(-2); } // according to glibc 2.1, this should return the req. number of bytes for // the string #ifdef _WIN32 // vsnprintf trick doesn't work. must use function called _vscprintf // cannot find much documentation about this on internet or anywhere. // I assume it isn't a standard function, but only in VisualC++ f = _vscprintf(fmt, ap); #else f = vsnprintf(x, 1, fmt, ap); #endif va_end(ap); // must be called after vsnprintf() if (f > 0 && f < MAX_MESSAGE_SIZE) { info_ptr->text = (char*) xmalloc(f+1); va_start(ap, fmt); if ((g = vsnprintf(info_ptr->text, f, fmt, ap)) == -1) { fprintf(stderr, "_debug_msg: Dieing! vsnprintf returned -1 for format \"%s\"\n", fmt); exit(-2); } va_end(ap); info_ptr->text[g] = '\0'; if (f != g) { fprintf(stderr, "_debug_msg: f != g\n"); } } else if (f > 0) { // it is over the max_message_size then f += strlen(info_ptr->file)+strlen(info_ptr->function); temp = info_ptr; pst_debug_write(); // dump the current messages info_ptr = temp; va_start(ap, fmt); pst_debug_write_msg(info_ptr, fmt, &ap, f); va_end(ap); free(info_ptr->function); free(info_ptr->file); free(info_ptr); info_ptr = NULL; return; } else { fprintf(stderr, "_debug_msg: error getting requested size of debug message\n"); info_ptr->text = "ERROR Saving\n"; } if (!item_head) item_head = info_ptr; info_ptr->next = NULL; if (item_tail) item_tail->next = info_ptr; item_tail = info_ptr; if (++curr_items == max_items) { // here we will jump off and save the contents pst_debug_write(); info_ptr = NULL; } } void pst_debug_hexdump(unsigned char *x, size_t y, int cols, int delta) { struct pst_debug_item *temp; if (!debug_fp) return; // no file info_ptr = temp_list; if (info_ptr) temp_list = info_ptr->next; temp = info_ptr; pst_debug_write(); info_ptr = temp; pst_debug_write_hex(info_ptr, x, y, cols); free(info_ptr->function); free(info_ptr->file); free(info_ptr); info_ptr = NULL; } void pst_debug_func(char *function) { func_ptr = xmalloc (sizeof(struct pst_debug_func)); func_ptr->name = xmalloc(strlen(function)+1); strcpy(func_ptr->name, function); func_ptr->next = func_head; func_head = func_ptr; } void pst_debug_func_ret() { //remove the head item func_ptr = func_head; if (func_head) { func_head = func_head->next; free(func_ptr->name); free(func_ptr); } else { DIE(("function list is empty!\n")); } } void pst_debug_close(void) { pst_debug_write(); while (func_head) { func_ptr = func_head; func_head = func_head->next; free(func_ptr->name); free(func_ptr); } if (debug_fp) fclose(debug_fp); debug_fp = NULL; } void pst_debug_write() { size_t size, ptr, funcname, filename, text, end; char *buf = NULL, rec_type; if (!debug_fp) return; // no file off_t index_pos = ftell(debug_fp); off_t file_pos = index_pos; // add 2. One for the pointer to the next index, // one for the count of this index int index_size = ((curr_items+2) * sizeof(off_t)); off_t *index; int index_ptr = 0; struct pst_debug_file_rec_m mfile_rec; struct pst_debug_file_rec_l lfile_rec; if (curr_items == 0) return; // no items to write. index = (off_t*)xmalloc(index_size); memset(index, 0, index_size); // valgrind, avoid writing uninitialized data file_pos += index_size; // write the index first, we will re-write it later, but // we want to allocate the space fwrite(index, index_size, 1, debug_fp); index[index_ptr++] = curr_items; item_ptr = item_head; while (item_ptr) { file_pos = ftell(debug_fp); index[index_ptr++] = file_pos; size = strlen(item_ptr->function) + strlen(item_ptr->file) + strlen(item_ptr->text) + 3; //for the three \0s if (buf) free(buf); buf = xmalloc(size+1); ptr = 0; funcname=ptr; ptr += sprintf(&(buf[ptr]), "%s", item_ptr->function)+1; filename=ptr; ptr += sprintf(&(buf[ptr]), "%s", item_ptr->file)+1; text=ptr; ptr += sprintf(&(buf[ptr]), "%s", item_ptr->text)+1; end=ptr; if (end > USHRT_MAX) { // bigger than can be stored in a short rec_type = 'L'; fwrite(&rec_type, 1, sizeof(char), debug_fp); lfile_rec.type = item_ptr->type; lfile_rec.line = item_ptr->line; lfile_rec.funcname = funcname; lfile_rec.filename = filename; lfile_rec.text = text; lfile_rec.end = end; fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp); } else { rec_type = 'M'; fwrite(&rec_type, 1, sizeof(char), debug_fp); mfile_rec.type = item_ptr->type; mfile_rec.line = item_ptr->line; mfile_rec.funcname = funcname; mfile_rec.filename = filename; mfile_rec.text = text; mfile_rec.end = end; fwrite(&mfile_rec, sizeof(mfile_rec), 1, debug_fp); } fwrite(buf, 1, ptr, debug_fp); if (buf) free(buf); buf = NULL; item_head = item_ptr->next; free(item_ptr->function); free(item_ptr->file); free(item_ptr->text); free(item_ptr); item_ptr = item_head; } curr_items = 0; index[index_ptr] = ftell(debug_fp); // we should now have a complete index fseek(debug_fp, index_pos, SEEK_SET); fwrite(index, index_size, 1, debug_fp); fseek(debug_fp, 0, SEEK_END); item_ptr = item_head = item_tail = NULL; free(index); if (buf) free(buf); } void pst_debug_write_msg(struct pst_debug_item *item, char *fmt, va_list *ap, int size) { struct pst_debug_file_rec_l lfile_rec; struct pst_debug_file_rec_m mfile_rec; unsigned char rec_type; int index_size = 3 * sizeof(off_t); off_t index[3]; off_t index_pos, file_pos; char zero='\0'; unsigned int end; if (!debug_fp) return; // no file index[0] = 1; //only one item in this index index_pos = ftell(debug_fp); fwrite(index, index_size, 1, debug_fp); index[1] = ftell(debug_fp); if (size > USHRT_MAX) { // bigger than can be stored in a short rec_type = 'L'; fwrite(&rec_type, 1, sizeof(char), debug_fp); lfile_rec.type = item->type; lfile_rec.line = item->line; lfile_rec.funcname = 0; lfile_rec.filename = strlen(item->function)+1; lfile_rec.text = lfile_rec.filename+strlen(item->file)+1; fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp); } else { rec_type = 'M'; fwrite(&rec_type, 1, sizeof(char), debug_fp); mfile_rec.type = item->type; mfile_rec.line = item->line; mfile_rec.funcname = 0; mfile_rec.filename = strlen(item->function)+1; mfile_rec.text = mfile_rec.filename+strlen(item->file)+1; fwrite(&mfile_rec, sizeof(mfile_rec), 1, debug_fp); } file_pos = ftell(debug_fp); fwrite(item->function, strlen(item->function)+1, 1, debug_fp); fwrite(item->file, strlen(item->file)+1, 1, debug_fp); vfprintf(debug_fp, fmt, *ap); fwrite(&zero, 1, 1, debug_fp); end = ftell(debug_fp)-file_pos; index[2] = ftell(debug_fp); fseek(debug_fp, index_pos, SEEK_SET); fwrite(index, index_size, 1, debug_fp); if (size > USHRT_MAX) { fwrite(&rec_type, 1, sizeof(char), debug_fp); lfile_rec.end = end; fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp); } else { fwrite(&rec_type, 1, sizeof(char), debug_fp); mfile_rec.end = end; fwrite(&mfile_rec, sizeof(mfile_rec), 1, debug_fp); } fseek(debug_fp, 0, SEEK_END); } void pst_debug_write_hex(struct pst_debug_item *item, unsigned char *buf, size_t size, int col) { struct pst_debug_file_rec_l lfile_rec; unsigned char rec_type; int index_size = 3 * sizeof(off_t); off_t index_pos, file_pos, index[3]; char zero='\0'; if (!debug_fp) return; // no file index[0] = 1; // only one item in this index run index[1] = 0; // valgrind, avoid writing uninitialized data index[2] = 0; // "" index_pos = ftell(debug_fp); fwrite(index, index_size, 1, debug_fp); index[1] = ftell(debug_fp); // always use the long rec_type = 'L'; fwrite(&rec_type, 1, sizeof(char), debug_fp); lfile_rec.funcname = 0; lfile_rec.filename = strlen(item->function)+1; lfile_rec.text = lfile_rec.filename+strlen(item->file)+1; lfile_rec.end = 0; // valgrind, avoid writing uninitialized data lfile_rec.line = item->line; lfile_rec.type = item->type; fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp); file_pos = ftell(debug_fp); fwrite(item->function, strlen(item->function)+1, 1, debug_fp); fwrite(item->file, strlen(item->file)+1, 1, debug_fp); pst_debug_hexdumper(debug_fp, buf, size, col, 0); fwrite(&zero, 1, 1, debug_fp); lfile_rec.end = ftell(debug_fp) - file_pos; index[2] = ftell(debug_fp); fseek(debug_fp, index_pos, SEEK_SET); fwrite(index, index_size, 1, debug_fp); fwrite(&rec_type, 1, sizeof(char), debug_fp); fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp); fseek(debug_fp, 0, SEEK_END); } void *xmalloc(size_t size) { void *mem = malloc(size); if (!mem) { fprintf(stderr, "xMalloc: Out Of memory [req: %ld]\n", (long)size); exit(1); } return mem; } diff --git a/src/readpst.c b/src/readpst.c index 4161aaf..9160ff0 100644 --- a/src/readpst.c +++ b/src/readpst.c @@ -1,1327 +1,1327 @@ /*** * readpst.c * Part of the LibPST project * Written by David Smith * dave.s@earthcorp.com */ #include "define.h" #include "libstrfunc.h" //#include "vbuf.h" #include "libpst.h" #include "common.h" #include "timeconv.h" #include "lzfu.h" #define OUTPUT_TEMPLATE "%s" #define OUTPUT_KMAIL_DIR_TEMPLATE ".%s.directory" #define KMAIL_INDEX ".%s.index" #define SEP_MAIL_FILE_TEMPLATE "%i" /* "%09i" */ // max size of the c_time char*. It will store the date of the email #define C_TIME_SIZE 500 #define PERM_DIRS 0777 // macro used for creating directories #ifndef WIN32 #define D_MKDIR(x) mkdir(x, PERM_DIRS) #else #define D_MKDIR(x) mkdir(x) #endif struct file_ll { char *name; char *dname; FILE * output; int32_t stored_count; int32_t email_count; int32_t skip_count; int32_t type; }; void process(pst_item *outeritem, pst_desc_ll *d_ptr); void write_email_body(FILE *f, char *body); char* removeCR (char *c); int usage(); int version(); char* mk_kmail_dir(char*); int close_kmail_dir(); char* mk_recurse_dir(char*); int close_recurse_dir(); char* mk_seperate_dir(char *dir); int close_seperate_dir(); int mk_seperate_file(struct file_ll *f); char* my_stristr(char *haystack, char *needle); void check_filename(char *fname); char* skip_header_prologue(char *headers); void write_separate_attachment(char f_name[], pst_item_attach* current_attach, int attach_num, pst_file* pst); void write_inline_attachment(FILE* f_output, pst_item_attach* current_attach, char boundary[], pst_file* pst); void write_normal_email(FILE* f_output, char f_name[], pst_item* item, int mode, int mode_MH, pst_file* pst, int save_rtf); void write_vcard(FILE* f_output, pst_item_contact* contact, char comment[]); void write_appointment(FILE* f_output, pst_item_appointment* appointment, pst_item_email* email, FILETIME* create_date, FILETIME* modify_date); void create_enter_dir(struct file_ll* f, pst_item *item); void close_enter_dir(struct file_ll *f); char* prog_name; char* output_dir = "."; char* kmail_chdir = NULL; // Normal mode just creates mbox format files in the current directory. Each file is named // the same as the folder's name that it represents #define MODE_NORMAL 0 // KMail mode creates a directory structure suitable for being used directly // by the KMail application #define MODE_KMAIL 1 // recurse mode creates a directory structure like the PST file. Each directory // contains only one file which stores the emails in mbox format. #define MODE_RECURSE 2 // seperate mode is similar directory structure to RECURSE. The emails are stored in // seperate files, numbering from 1 upward. Attachments belonging to the emails are // saved as email_no-filename (e.g. 1-samplefile.doc or 000001-Attachment2.zip) #define MODE_SEPERATE 3 // Decrypt the whole file (even the parts that aren't encrypted) and ralph it to stdout #define MODE_DECSPEW 4 // Output Normal just prints the standard information about what is going on #define OUTPUT_NORMAL 0 // Output Quiet is provided so that only errors are printed #define OUTPUT_QUIET 1 // default mime-type for attachments that have a null mime-type #define MIME_TYPE_DEFAULT "application/octet-stream" // output mode for contacts #define CMODE_VCARD 0 #define CMODE_LIST 1 // output settings for RTF bodies // filename for the attachment #define RTF_ATTACH_NAME "rtf-body.rtf" // mime type for the attachment #define RTF_ATTACH_TYPE "application/rtf" // global settings int mode = MODE_NORMAL; int mode_MH = 0; int output_mode = OUTPUT_NORMAL; int contact_mode = CMODE_VCARD; int overwrite = 0; int save_rtf_body = 1; pst_file pstfile; void process(pst_item *outeritem, pst_desc_ll *d_ptr) { struct file_ll ff; pst_item *item = NULL; DEBUG_ENT("process"); memset(&ff, 0, sizeof(ff)); create_enter_dir(&ff, outeritem); while (d_ptr) { DEBUG_MAIN(("main: New item record\n")); if (!d_ptr->desc) { DEBUG_WARN(("main: ERROR ?? item's desc record is NULL\n")); ff.skip_count++; } else { DEBUG_MAIN(("main: Desc Email ID %#x [d_ptr->id = %#x]\n", d_ptr->desc->id, d_ptr->id)); item = pst_parse_item(&pstfile, d_ptr); DEBUG_MAIN(("main: About to process item\n")); if (item && item->email && item->email->subject && item->email->subject->subj) { DEBUG_EMAIL(("item->email->subject = %p\n", item->email->subject)); DEBUG_EMAIL(("item->email->subject->subj = %p\n", item->email->subject->subj)); } if (item) { if (item->message_store) { // there should only be one message_store, and we have already done it DIE(("main: A second message_store has been found. Sorry, this must be an error.\n")); } if (item->folder && d_ptr->child && strcasecmp(item->file_as, "Deleted Items")) { //if this is a non-empty folder other than deleted items, we want to recurse into it if (output_mode != OUTPUT_QUIET) printf("Processing Folder \"%s\"\n", item->file_as); process(item, d_ptr->child); } else if (item->contact && (item->type == PST_TYPE_CONTACT)) { // deal with a contact // write them to the file, one per line in this format // Desc Name \n if (mode == MODE_SEPERATE) mk_seperate_file(&ff); ff.email_count++; DEBUG_MAIN(("main: Processing Contact\n")); if (ff.type != PST_TYPE_CONTACT) { DEBUG_MAIN(("main: I have a contact, but the folder isn't a contacts folder. Processing anyway\n")); } if (contact_mode == CMODE_VCARD) write_vcard(ff.output, item->contact, item->comment); else fprintf(ff.output, "%s <%s>\n", item->contact->fullname, item->contact->address1); } else if (item->email && (item->type == PST_TYPE_NOTE || item->type == PST_TYPE_REPORT)) { if (mode == MODE_SEPERATE) mk_seperate_file(&ff); ff.email_count++; DEBUG_MAIN(("main: Processing Email\n")); if ((ff.type != PST_TYPE_NOTE) && (ff.type != PST_TYPE_REPORT)) { DEBUG_MAIN(("main: I have an email, but the folder isn't an email folder. Processing anyway\n")); } write_normal_email(ff.output, ff.name, item, mode, mode_MH, &pstfile, save_rtf_body); } else if (item->journal && (item->type == PST_TYPE_JOURNAL)) { // deal with journal items if (mode == MODE_SEPERATE) mk_seperate_file(&ff); ff.email_count++; DEBUG_MAIN(("main: Processing Journal Entry\n")); if (ff.type != PST_TYPE_JOURNAL) { DEBUG_MAIN(("main: I have a journal entry, but the folder isn't a journal folder. Processing anyway\n")); } fprintf(ff.output, "BEGIN:VJOURNAL\n"); if (item->email && item->email->subject && item->email->subject->subj) fprintf(ff.output, "SUMMARY:%s\n", pst_rfc2426_escape(item->email->subject->subj)); if (item->email && item->email->body) fprintf(ff.output, "DESCRIPTION:%s\n", pst_rfc2426_escape(item->email->body)); if (item->journal->start) fprintf(ff.output, "DTSTART;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(item->journal->start)); fprintf(ff.output, "END:VJOURNAL\n\n"); } else if (item->appointment && (item->type == PST_TYPE_APPOINTMENT)) { // deal with Calendar appointments if (mode == MODE_SEPERATE) mk_seperate_file(&ff); ff.email_count++; DEBUG_MAIN(("main: Processing Appointment Entry\n")); if (ff.type != PST_TYPE_APPOINTMENT) { DEBUG_MAIN(("main: I have an appointment, but folder isn't specified as an appointment type. Processing...\n")); } write_appointment(ff.output, item->appointment, item->email, item->create_date, item->modify_date); } else { // these all seem to be things that MS agrees are not included in the item count //ff.skip_count++; DEBUG_MAIN(("main: Unknown item type %i (%s) name (%s)\n", item->type, item->ascii_type, item->file_as)); } pst_freeItem(item); } else { ff.skip_count++; DEBUG_MAIN(("main: A NULL item was seen\n")); } d_ptr = d_ptr->next; } } close_enter_dir(&ff); DEBUG_RET(); } int main(int argc, char** argv) { pst_item *item = NULL; pst_desc_ll *d_ptr; char * fname = NULL; char *d_log = NULL; int c,x; char *temp = NULL; //temporary char pointer prog_name = argv[0]; // command-line option handling while ((c = getopt(argc, argv, "bCc:d:hko:qrSMVw"))!= -1) { switch (c) { case 'b': save_rtf_body = 0; break; case 'C': mode = MODE_DECSPEW; break; case 'c': if (optarg && optarg[0]=='v') contact_mode=CMODE_VCARD; else if (optarg && optarg[0]=='l') contact_mode=CMODE_LIST; else { usage(); exit(0); } break; case 'd': d_log = optarg; break; case 'h': usage(); exit(0); break; case 'V': version(); exit(0); break; case 'k': mode = MODE_KMAIL; break; case 'M': mode = MODE_SEPERATE; mode_MH = 1; break; case 'o': output_dir = optarg; break; case 'q': output_mode = OUTPUT_QUIET; break; case 'r': mode = MODE_RECURSE; break; case 'S': mode = MODE_SEPERATE; break; case 'w': overwrite = 1; break; default: usage(); exit(1); break; } } if (argc > optind) { fname = argv[optind]; } else { usage(); exit(2); } #ifdef DEBUG_ALL // force a log file if (!d_log) d_log = "readpst.log"; #endif // defined DEBUG_ALL DEBUG_INIT(d_log); DEBUG_REGISTER_CLOSE(); DEBUG_ENT("main"); if (mode == MODE_DECSPEW) { - FILE *fp; - char buf[1024]; - int l=0; + FILE *fp; + char buf[1024]; + size_t l = 0; if (NULL == (fp = fopen(fname, "rb"))) { fprintf(stderr, "Couldn't open file %s\n", fname ); DEBUG_RET(); return 1; } while (0 != ( l = fread( buf, 1, 1024, fp))) { if (0 != pst_decrypt( buf, l, PST_COMP_ENCRYPT)) fprintf(stderr, "pst_decrypt() failed (I'll try to continue)\n"); if (l != fwrite( buf, 1, l, stdout)) { fprintf(stderr, "Couldn't output to stdout?\n"); DEBUG_RET(); return 1; } } DEBUG_RET(); return 0; } if (output_mode != OUTPUT_QUIET) printf("Opening PST file and indexes...\n"); RET_DERROR(pst_open(&pstfile, fname), 1, ("Error opening File\n")); RET_DERROR(pst_load_index(&pstfile), 2, ("Index Error\n")); pst_load_extended_attributes(&pstfile); if (chdir(output_dir)) { x = errno; pst_close(&pstfile); DEBUG_RET(); DIE(("main: Cannot change to output dir %s: %s\n", output_dir, strerror(x))); } if (output_mode != OUTPUT_QUIET) printf("About to start processing first record...\n"); d_ptr = pstfile.d_head; // first record is main record item = pst_parse_item(&pstfile, d_ptr); if (!item || !item->message_store) { DEBUG_RET(); DIE(("main: Could not get root record\n")); } // default the file_as to the same as the main filename if it doesn't exist if (!item->file_as) { if (!(temp = strrchr(fname, '/'))) if (!(temp = strrchr(fname, '\\'))) temp = fname; else temp++; // get past the "\\" else temp++; // get past the "/" item->file_as = (char*)xmalloc(strlen(temp)+1); strcpy(item->file_as, temp); DEBUG_MAIN(("file_as was blank, so am using %s\n", item->file_as)); } DEBUG_MAIN(("main: Root Folder Name: %s\n", item->file_as)); d_ptr = pst_getTopOfFolders(&pstfile, item); if (!d_ptr) { DEBUG_RET(); DIE(("Top of folders record not found. Cannot continue\n")); } process(item, d_ptr->child); // do the children of TOPF pst_freeItem(item); pst_close(&pstfile); DEBUG_RET(); return 0; } void write_email_body(FILE *f, char *body) { char *n = body; // DEBUG_MAIN(("write_email_body(): \"%s\"\n", body)); DEBUG_ENT("write_email_body"); while (n) { if (strncmp(body, "From ", 5) == 0) fprintf(f, ">"); if ((n = strchr(body, '\n'))) { n++; fwrite(body, n-body, 1, f); //write just a line body = n; } } fwrite(body, strlen(body), 1, f); DEBUG_RET(); } char *removeCR (char *c) { // converts /r/n to /n char *a, *b; DEBUG_ENT("removeCR"); a = b = c; while (*a != '\0') { *b = *a; if (*a != '\r') b++; a++; } *b = '\0'; DEBUG_RET(); return c; } int usage() { DEBUG_ENT("usage"); version(); printf("Usage: %s [OPTIONS] {PST FILENAME}\n", prog_name); printf("OPTIONS:\n"); printf("\t-C\t- Decrypt the entire file and output on stdout (not typically useful)\n"); printf("\t-M\t- MH. Write emails in the MH format\n"); printf("\t-S\t- Seperate. Write emails in the seperate format\n"); printf("\t-V\t- Version. Display program version\n"); printf("\t-b\t- Don't save RTF-Body attachments\n"); printf("\t-c[v|l]\t- Set the Contact output mode. -cv = VCard, -cl = EMail list\n"); printf("\t-d \t- Debug to file. This is a binary log. Use readpstlog to print it\n"); printf("\t-h\t- Help. This screen\n"); printf("\t-k\t- KMail. Output in kmail format\n"); printf("\t-o \t- Output directory to write files to. CWD is changed *after* opening pst file\n"); printf("\t-q\t- Quiet. Only print error messages\n"); printf("\t-r\t- Recursive. Output in a recursive format\n"); printf("\t-w\t- Overwrite any output mbox files\n"); DEBUG_RET(); return 0; } int version() { DEBUG_ENT("version"); printf("ReadPST / LibPST v%s\n", VERSION); #if BYTE_ORDER == BIG_ENDIAN printf("Big Endian implementation being used.\n"); #elif BYTE_ORDER == LITTLE_ENDIAN printf("Little Endian implementation being used.\n"); #else # error "Byte order not supported by this library" #endif #ifdef __GNUC__ printf("GCC %d.%d : %s %s\n", __GNUC__, __GNUC_MINOR__, __DATE__, __TIME__); #endif DEBUG_RET(); return 0; } char *mk_kmail_dir(char *fname) { //change to that directory //make a directory based on OUTPUT_KMAIL_DIR_TEMPLATE //allocate space for OUTPUT_TEMPLATE and form a char* with fname //return that value char *dir, *out_name, *index; int x; DEBUG_ENT("mk_kmail_dir"); if (kmail_chdir && chdir(kmail_chdir)) { x = errno; DIE(("mk_kmail_dir: Cannot change to directory %s: %s\n", kmail_chdir, strerror(x))); } dir = malloc(strlen(fname)+strlen(OUTPUT_KMAIL_DIR_TEMPLATE)+1); sprintf(dir, OUTPUT_KMAIL_DIR_TEMPLATE, fname); check_filename(dir); if (D_MKDIR(dir)) { //error occured if (errno != EEXIST) { x = errno; DIE(("mk_kmail_dir: Cannot create directory %s: %s\n", dir, strerror(x))); } } kmail_chdir = realloc(kmail_chdir, strlen(dir)+1); strcpy(kmail_chdir, dir); free (dir); //we should remove any existing indexes created by KMail, cause they might be different now index = malloc(strlen(fname)+strlen(KMAIL_INDEX)+1); sprintf(index, KMAIL_INDEX, fname); unlink(index); free(index); out_name = malloc(strlen(fname)+strlen(OUTPUT_TEMPLATE)+1); sprintf(out_name, OUTPUT_TEMPLATE, fname); DEBUG_RET(); return out_name; } int close_kmail_dir() { // change .. int x; DEBUG_ENT("close_kmail_dir"); if (kmail_chdir) { //only free kmail_chdir if not NULL. do not change directory free(kmail_chdir); kmail_chdir = NULL; } else { if (chdir("..")) { x = errno; DIE(("close_kmail_dir: Cannot move up dir (..): %s\n", strerror(x))); } } DEBUG_RET(); return 0; } // this will create a directory by that name, then make an mbox file inside // that dir. any subsequent dirs will be created by name, and they will // contain mbox files char *mk_recurse_dir(char *dir) { int x; char *out_name; DEBUG_ENT("mk_recurse_dir"); check_filename(dir); if (D_MKDIR (dir)) { if (errno != EEXIST) { // not an error because it exists x = errno; DIE(("mk_recurse_dir: Cannot create directory %s: %s\n", dir, strerror(x))); } } if (chdir (dir)) { x = errno; DIE(("mk_recurse_dir: Cannot change to directory %s: %s\n", dir, strerror(x))); } out_name = malloc(strlen("mbox")+1); strcpy(out_name, "mbox"); DEBUG_RET(); return out_name; } int close_recurse_dir() { int x; DEBUG_ENT("close_recurse_dir"); if (chdir("..")) { x = errno; DIE(("close_recurse_dir: Cannot go up dir (..): %s\n", strerror(x))); } DEBUG_RET(); return 0; } char *mk_seperate_dir(char *dir) { size_t dirsize = strlen(dir) + 10; char dir_name[dirsize]; int x = 0, y = 0; DEBUG_ENT("mk_seperate_dir"); do { if (y == 0) snprintf(dir_name, dirsize, "%s", dir); else snprintf(dir_name, dirsize, "%s" SEP_MAIL_FILE_TEMPLATE, dir, y); // enough for 9 digits allocated above check_filename(dir_name); DEBUG_MAIN(("about to try creating %s\n", dir_name)); if (D_MKDIR(dir_name)) { if (errno != EEXIST) { // if there is an error, and it doesn't already exist x = errno; DIE(("mk_seperate_dir: Cannot create directory %s: %s\n", dir, strerror(x))); } } else { break; } y++; } while (overwrite == 0); if (chdir(dir_name)) { x = errno; DIE(("mk_recurse_dir: Cannot change to directory %s: %s\n", dir, strerror(x))); } if (overwrite) { // we should probably delete all files from this directory #if !defined(WIN32) && !defined(__CYGWIN__) DIR * sdir = NULL; struct dirent *dirent = NULL; struct stat filestat; if (!(sdir = opendir("./"))) { WARN(("mk_seperate_dir: Cannot open dir \"%s\" for deletion of old contents\n", "./")); } else { while ((dirent = readdir(sdir))) { if (lstat(dirent->d_name, &filestat) != -1) if (S_ISREG(filestat.st_mode)) { if (unlink(dirent->d_name)) { y = errno; DIE(("mk_seperate_dir: unlink returned error on file %s: %s\n", dirent->d_name, strerror(y))); } } } } #endif } // we don't return a filename here cause it isn't necessary. DEBUG_RET(); return NULL; } int close_seperate_dir() { int x; DEBUG_ENT("close_seperate_dir"); if (chdir("..")) { x = errno; DIE(("close_seperate_dir: Cannot go up dir (..): %s\n", strerror(x))); } DEBUG_RET(); return 0; } int mk_seperate_file(struct file_ll *f) { const int name_offset = 1; DEBUG_ENT("mk_seperate_file"); DEBUG_MAIN(("opening next file to save email\n")); if (f->email_count > 999999999) { // bigger than nine 9's DIE(("mk_seperate_file: The number of emails in this folder has become too high to handle")); } sprintf(f->name, SEP_MAIL_FILE_TEMPLATE, f->email_count + name_offset); if (f->output) fclose(f->output); f->output = NULL; check_filename(f->name); if (!(f->output = fopen(f->name, "w"))) { DIE(("mk_seperate_file: Cannot open file to save email \"%s\"\n", f->name)); } DEBUG_RET(); return 0; } char *my_stristr(char *haystack, char *needle) { // my_stristr varies from strstr in that its searches are case-insensitive char *x=haystack, *y=needle, *z = NULL; DEBUG_ENT("my_stristr"); if (!haystack || !needle) { DEBUG_RET(); return NULL; } while (*y != '\0' && *x != '\0') { if (tolower(*y) == tolower(*x)) { // move y on one y++; if (!z) { z = x; // store first position in haystack where a match is made } } else { y = needle; // reset y to the beginning of the needle z = NULL; // reset the haystack storage point } x++; // advance the search in the haystack } DEBUG_RET(); return z; } void check_filename(char *fname) { char *t = fname; DEBUG_ENT("check_filename"); if (!t) { DEBUG_RET(); return; } while ((t = strpbrk(t, "/\\:"))) { // while there are characters in the second string that we don't want *t = '_'; //replace them with an underscore } DEBUG_RET(); } // The sole purpose of this function is to bypass the pseudo-header prologue // that Microsoft Outlook inserts at the beginning of the internet email // headers for emails stored in their "Personal Folders" files. char *skip_header_prologue(char *headers) { const char *bad = "Microsoft Mail Internet Headers"; if ( strncmp(headers, bad, strlen(bad)) == 0 ) { // Found the offensive header prologue char *pc = strchr(headers, '\n'); return pc + 1; } return headers; } void write_separate_attachment(char f_name[], pst_item_attach* current_attach, int attach_num, pst_file* pst) { FILE *fp = NULL; int x = 0; char *temp = NULL; // If there is a long filename (filename2) use that, otherwise // use the 8.3 filename (filename1) char *attach_filename = (current_attach->filename2) ? current_attach->filename2 : current_attach->filename1; DEBUG_ENT("write_separate_attachment"); check_filename(f_name); if (!attach_filename) { // generate our own (dummy) filename for the attachement temp = xmalloc(strlen(f_name)+15); sprintf(temp, "%s-attach%i", f_name, attach_num); } else { // have an attachment name, make sure it's unique temp = xmalloc(strlen(f_name)+strlen(attach_filename)+15); do { if (fp) fclose(fp); if (x == 0) sprintf(temp, "%s-%s", f_name, attach_filename); else sprintf(temp, "%s-%s-%i", f_name, attach_filename, x); } while ((fp = fopen(temp, "r")) && ++x < 99999999); if (x > 99999999) { DIE(("error finding attachment name. exhausted possibilities to %s\n", temp)); } } DEBUG_EMAIL(("Saving attachment to %s\n", temp)); if (!(fp = fopen(temp, "w"))) { WARN(("write_separate_attachment: Cannot open attachment save file \"%s\"\n", temp)); } else { if (current_attach->data) fwrite(current_attach->data, 1, current_attach->size, fp); else { (void)pst_attach_to_file(pst, current_attach, fp); } fclose(fp); } if (temp) free(temp); DEBUG_RET(); } void write_inline_attachment(FILE* f_output, pst_item_attach* current_attach, char boundary[], pst_file* pst) { char *enc = NULL; // base64 encoded attachment DEBUG_ENT("write_inline_attachment"); DEBUG_EMAIL(("Attachment Size is %i\n", current_attach->size)); DEBUG_EMAIL(("Attachment Pointer is %p\n", current_attach->data)); if (current_attach->data) { enc = base64_encode (current_attach->data, current_attach->size); if (!enc) { DEBUG_EMAIL(("ERROR base64_encode returned NULL. Must have failed\n")); DEBUG_RET(); return; } } if (boundary) { char *attach_filename; fprintf(f_output, "\n--%s\n", boundary); if (!current_attach->mimetype) { fprintf(f_output, "Content-type: %s\n", MIME_TYPE_DEFAULT); } else { fprintf(f_output, "Content-type: %s\n", current_attach->mimetype); } fprintf(f_output, "Content-transfer-encoding: base64\n"); // If there is a long filename (filename2) use that, otherwise // use the 8.3 filename (filename1) if (current_attach->filename2) { attach_filename = current_attach->filename2; } else { attach_filename = current_attach->filename1; } if (!attach_filename) { fprintf(f_output, "Content-Disposition: inline\n\n"); } else { fprintf(f_output, "Content-Disposition: attachment; filename=\"%s\"\n\n", attach_filename); } } if (current_attach->data) { fwrite(enc, 1, strlen(enc), f_output); DEBUG_EMAIL(("Attachment Size after encoding is %i\n", strlen(enc))); free(enc); // caught by valgrind } else { (void)pst_attach_to_file_base64(pst, current_attach, f_output); } fprintf(f_output, "\n\n"); DEBUG_RET(); } void write_normal_email(FILE* f_output, char f_name[], pst_item* item, int mode, int mode_MH, pst_file* pst, int save_rtf) { char *boundary = NULL; // the boundary marker between multipart sections int boundary_created = 0; // we have not (yet) created a new boundary char *temp = NULL; int attach_num, base64_body = 0; time_t em_time; char *c_time; pst_item_attach* current_attach; DEBUG_ENT("write_normal_email"); // convert the sent date if it exists, or set it to a fixed date if (item->email->sent_date) { em_time = fileTimeToUnixTime(item->email->sent_date, 0); c_time = ctime(&em_time); if (c_time) c_time[strlen(c_time)-1] = '\0'; //remove end \n else c_time = "Fri Dec 28 12:06:21 2001"; } else c_time= "Fri Dec 28 12:06:21 2001"; // we will always look at the header to discover some stuff if (item->email->header ) { char *b1, *b2; // see if there is a boundary variable there // this search MUST be made case insensitive (DONE). // Also, we should check to find out if we are looking // at the boundary associated with content-type, and that // the content type really is multipart removeCR(item->email->header); if ((b2 = my_stristr(item->email->header, "boundary="))) { int len; b2 += strlen("boundary="); // move boundary to first char of marker if (*b2 == '"') { b2++; b1 = strchr(b2, '"'); // find terminating quote } else { b1 = b2; while (isgraph(*b1)) // find first char that isn't part of boundary b1++; } len = b1 - b2; boundary = malloc(len+1); //malloc that length strncpy(boundary, b2, len); // copy boundary to another variable boundary[len] = '\0'; b1 = b2 = boundary; while (*b2 != '\0') { // remove any CRs and Tabs if (*b2 != '\n' && *b2 != '\r' && *b2 != '\t') { *b1 = *b2; b1++; } b2++; } *b1 = '\0'; DEBUG_EMAIL(("Found boundary of - %s\n", boundary)); } else { DEBUG_EMAIL(("boundary not found in header\n")); } // also possible to set 7bit encoding detection here. if ((b2 = my_stristr(item->email->header, "Content-Transfer-Encoding:"))) { if ((b2 = strchr(b2, ':'))) { b2++; // skip to the : at the end of the string while (*b2 == ' ' || *b2 == '\t') b2++; if (pst_strincmp(b2, "base64", 6)==0) { DEBUG_EMAIL(("body is base64 encoded\n")); base64_body = 1; } } else { DEBUG_WARN(("found a ':' during the my_stristr, but not after that..\n")); } } } if (!boundary && (item->attach || (item->email->body && item->email->htmlbody) || item->email->rtf_compressed || item->email->encrypted_body || item->email->encrypted_htmlbody)) { // we need to create a boundary here. DEBUG_EMAIL(("must create own boundary. oh dear.\n")); boundary = malloc(50 * sizeof(char)); // allow 50 chars for boundary boundary[0] = '\0'; sprintf(boundary, "--boundary-LibPST-iamunique-%i_-_-", rand()); DEBUG_EMAIL(("created boundary is %s\n", boundary)); boundary_created = 1; } DEBUG_EMAIL(("About to print Header\n")); if (item && item->email && item->email->subject && item->email->subject->subj) { DEBUG_EMAIL(("item->email->subject->subj = %s\n", item->email->subject->subj)); } if (item->email->header) { int len; char *soh = NULL; // real start of headers. // some of the headers we get from the file are not properly defined. // they can contain some email stuff too. We will cut off the header // when we see a \n\n or \r\n\r\n removeCR(item->email->header); temp = strstr(item->email->header, "\n\n"); if (temp) { DEBUG_EMAIL(("Found body text in header\n")); temp[1] = '\0'; // stop after first \n } // Now, write out the header... soh = skip_header_prologue(item->email->header); if (mode != MODE_SEPERATE) { // don't put rubbish in if we are doing seperate if (strncmp(soh, "X-From_: ", 9) == 0 ) { fputs("From ", f_output); soh += 9; } else fprintf(f_output, "From \"%s\" %s\n", item->email->outlook_sender_name, c_time); } fprintf(f_output, "%s", soh); len = strlen(soh); if (!len || (soh[len-1] != '\n')) fprintf(f_output, "\n"); } else { //make up our own headers if (mode != MODE_SEPERATE) { // don't want this first line for this mode if (item->email->outlook_sender_name) { temp = item->email->outlook_sender_name; } else { temp = "(readpst_null)"; } fprintf(f_output, "From \"%s\" %s\n", temp, c_time); } temp = item->email->outlook_sender; if (!temp) temp = ""; fprintf(f_output, "From: \"%s\" <%s>\n", item->email->outlook_sender_name, temp); if (item->email->subject) { fprintf(f_output, "Subject: %s\n", item->email->subject->subj); } else { fprintf(f_output, "Subject: \n"); } fprintf(f_output, "To: %s\n", item->email->sentto_address); if (item->email->cc_address) { fprintf(f_output, "Cc: %s\n", item->email->cc_address); } if (item->email->sent_date) { char c_time[C_TIME_SIZE]; strftime(c_time, C_TIME_SIZE, "%a, %d %b %Y %H:%M:%S %z", gmtime(&em_time)); fprintf(f_output, "Date: %s\n", c_time); } } fprintf(f_output, "MIME-Version: 1.0\n"); if (boundary && boundary_created) { // if we created the boundary, then it has NOT already been printed // in the headers above. if (item->attach) { // write the boundary stuff if we have attachments fprintf(f_output, "Content-type: multipart/mixed;\n\tboundary=\"%s\"\n", boundary); } else if (boundary) { // else if we have multipart/alternative then tell it so fprintf(f_output, "Content-type: multipart/alternative;\n\tboundary=\"%s\"\n", boundary); } else if (item->email->htmlbody) { fprintf(f_output, "Content-type: text/html\n"); } } fprintf(f_output, "\n"); // start the body DEBUG_EMAIL(("About to print Body\n")); if (item->email->body) { if (boundary) { fprintf(f_output, "\n--%s\n", boundary); fprintf(f_output, "Content-type: text/plain\n"); if (base64_body) fprintf(f_output, "Content-Transfer-Encoding: base64\n"); fprintf(f_output, "\n"); } removeCR(item->email->body); if (base64_body) { char *enc = base64_encode(item->email->body, strlen(item->email->body)); if (enc) { write_email_body(f_output, enc); free(enc); } } else { write_email_body(f_output, item->email->body); } } if (item->email->htmlbody) { if (boundary) { fprintf(f_output, "\n--%s\n", boundary); fprintf(f_output, "Content-type: text/html\n"); if (base64_body) fprintf(f_output, "Content-Transfer-Encoding: base64\n"); fprintf(f_output, "\n"); } removeCR(item->email->htmlbody); if (base64_body) { char *enc = base64_encode(item->email->htmlbody, strlen(item->email->htmlbody)); if (enc) { write_email_body(f_output, enc); free(enc); } } else { write_email_body(f_output, item->email->htmlbody); } } if (item->email->rtf_compressed && save_rtf) { //int32_t tester; DEBUG_EMAIL(("Adding RTF body as attachment\n")); current_attach = (pst_item_attach*)xmalloc(sizeof(pst_item_attach)); memset(current_attach, 0, sizeof(pst_item_attach)); current_attach->next = item->attach; item->attach = current_attach; current_attach->data = lzfu_decompress(item->email->rtf_compressed, item->email->rtf_compressed_size, ¤t_attach->size); current_attach->filename2 = xmalloc(strlen(RTF_ATTACH_NAME)+2); strcpy(current_attach->filename2, RTF_ATTACH_NAME); current_attach->mimetype = xmalloc(strlen(RTF_ATTACH_TYPE)+2); strcpy(current_attach->mimetype, RTF_ATTACH_TYPE); //memcpy(&tester, item->email->rtf_compressed+sizeof(int32_t), sizeof(int32_t)); //LE32_CPU(tester); //printf("lz produced %d bytes, rtf claims %d bytes\n", current_attach->size, tester); } if (item->email->encrypted_body || item->email->encrypted_htmlbody) { // if either the body or htmlbody is encrypted, add them as attachments if (item->email->encrypted_body) { DEBUG_EMAIL(("Adding Encrypted Body as attachment\n")); current_attach = (pst_item_attach*) xmalloc(sizeof(pst_item_attach)); memset(current_attach, 0, sizeof(pst_item_attach)); current_attach->next = item->attach; item->attach = current_attach; current_attach->data = item->email->encrypted_body; current_attach->size = item->email->encrypted_body_size; item->email->encrypted_body = NULL; } if (item->email->encrypted_htmlbody) { DEBUG_EMAIL(("Adding encrypted HTML body as attachment\n")); current_attach = (pst_item_attach*) xmalloc(sizeof(pst_item_attach)); memset(current_attach, 0, sizeof(pst_item_attach)); current_attach->next = item->attach; item->attach = current_attach; current_attach->data = item->email->encrypted_htmlbody; current_attach->size = item->email->encrypted_htmlbody_size; item->email->encrypted_htmlbody = NULL; } write_email_body(f_output, "The body of this email is encrypted. This isn't supported yet, but the body is now an attachment\n"); } // attachments attach_num = 0; for (current_attach = item->attach; current_attach; current_attach = current_attach->next) { DEBUG_EMAIL(("Attempting Attachment encoding\n")); if (!current_attach->data) { DEBUG_EMAIL(("Data of attachment is NULL!. Size is supposed to be %i\n", current_attach->size)); } if (mode == MODE_SEPERATE && !mode_MH) write_separate_attachment(f_name, current_attach, ++attach_num, pst); else write_inline_attachment(f_output, current_attach, boundary, pst); } if (mode != MODE_SEPERATE) { /* do not add a boundary after the last attachment for mode_MH */ DEBUG_EMAIL(("Writing buffer between emails\n")); if (boundary) fprintf(f_output, "\n--%s--\n", boundary); fprintf(f_output, "\n\n"); } if (boundary) free (boundary); DEBUG_RET(); } void write_vcard(FILE* f_output, pst_item_contact* contact, char comment[]) { // We can only call rfc escape once per printf, since the second call // may free the buffer returned by the first call. // I had tried to place those into a single printf - Carl. DEBUG_ENT("write_vcard"); // the specification I am following is (hopefully) RFC2426 vCard Mime Directory Profile fprintf(f_output, "BEGIN:VCARD\n"); fprintf(f_output, "FN:%s\n", pst_rfc2426_escape(contact->fullname)); //fprintf(f_output, "N:%s;%s;%s;%s;%s\n", fprintf(f_output, "N:%s;", (!contact->surname) ? "" : pst_rfc2426_escape(contact->surname)); fprintf(f_output, "%s;", (!contact->first_name) ? "" : pst_rfc2426_escape(contact->first_name)); fprintf(f_output, "%s;", (!contact->middle_name) ? "" : pst_rfc2426_escape(contact->middle_name)); fprintf(f_output, "%s;", (!contact->display_name_prefix) ? "" : pst_rfc2426_escape(contact->display_name_prefix)); fprintf(f_output, "%s\n", (!contact->suffix) ? "" : pst_rfc2426_escape(contact->suffix)); if (contact->nickname) fprintf(f_output, "NICKNAME:%s\n", pst_rfc2426_escape(contact->nickname)); if (contact->address1) fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address1)); if (contact->address2) fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address2)); if (contact->address3) fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address3)); if (contact->birthday) fprintf(f_output, "BDAY:%s\n", pst_rfc2425_datetime_format(contact->birthday)); if (contact->home_address) { //fprintf(f_output, "ADR;TYPE=home:%s;%s;%s;%s;%s;%s;%s\n", fprintf(f_output, "ADR;TYPE=home:%s;", (!contact->home_po_box) ? "" : pst_rfc2426_escape(contact->home_po_box)); fprintf(f_output, "%s;", ""); // extended Address fprintf(f_output, "%s;", (!contact->home_street) ? "" : pst_rfc2426_escape(contact->home_street)); fprintf(f_output, "%s;", (!contact->home_city) ? "" : pst_rfc2426_escape(contact->home_city)); fprintf(f_output, "%s;", (!contact->home_state) ? "" : pst_rfc2426_escape(contact->home_state)); fprintf(f_output, "%s;", (!contact->home_postal_code) ? "" : pst_rfc2426_escape(contact->home_postal_code)); fprintf(f_output, "%s\n", (!contact->home_country) ? "" : pst_rfc2426_escape(contact->home_country)); fprintf(f_output, "LABEL;TYPE=home:%s\n", pst_rfc2426_escape(contact->home_address)); } if (contact->business_address) { //fprintf(f_output, "ADR;TYPE=work:%s;%s;%s;%s;%s;%s;%s\n", fprintf(f_output, "ADR;TYPE=work:%s;", (!contact->business_po_box) ? "" : pst_rfc2426_escape(contact->business_po_box)); fprintf(f_output, "%s;", ""); // extended Address fprintf(f_output, "%s;", (!contact->business_street) ? "" : pst_rfc2426_escape(contact->business_street)); fprintf(f_output, "%s;", (!contact->business_city) ? "" : pst_rfc2426_escape(contact->business_city)); fprintf(f_output, "%s;", (!contact->business_state) ? "" : pst_rfc2426_escape(contact->business_state)); fprintf(f_output, "%s;", (!contact->business_postal_code) ? "" : pst_rfc2426_escape(contact->business_postal_code)); fprintf(f_output, "%s\n", (!contact->business_country) ? "" : pst_rfc2426_escape(contact->business_country)); fprintf(f_output, "LABEL;TYPE=work:%s\n", pst_rfc2426_escape(contact->business_address)); } if (contact->other_address) { //fprintf(f_output, "ADR;TYPE=postal:%s;%s;%s;%s;%s;%s;%s\n", fprintf(f_output, "ADR;TYPE=postal:%s;",(!contact->other_po_box) ? "" : pst_rfc2426_escape(contact->other_po_box)); fprintf(f_output, "%s;", ""); // extended Address fprintf(f_output, "%s;", (!contact->other_street) ? "" : pst_rfc2426_escape(contact->other_street)); fprintf(f_output, "%s;", (!contact->other_city) ? "" : pst_rfc2426_escape(contact->other_city)); fprintf(f_output, "%s;", (!contact->other_state) ? "" : pst_rfc2426_escape(contact->other_state)); fprintf(f_output, "%s;", (!contact->other_postal_code) ? "" : pst_rfc2426_escape(contact->other_postal_code)); fprintf(f_output, "%s\n", (!contact->other_country) ? "" : pst_rfc2426_escape(contact->other_country)); fprintf(f_output, "LABEL;TYPE=postal:%s\n", pst_rfc2426_escape(contact->other_address)); } if (contact->business_fax) fprintf(f_output, "TEL;TYPE=work,fax:%s\n", pst_rfc2426_escape(contact->business_fax)); if (contact->business_phone) fprintf(f_output, "TEL;TYPE=work,voice:%s\n", pst_rfc2426_escape(contact->business_phone)); if (contact->business_phone2) fprintf(f_output, "TEL;TYPE=work,voice:%s\n", pst_rfc2426_escape(contact->business_phone2)); if (contact->car_phone) fprintf(f_output, "TEL;TYPE=car,voice:%s\n", pst_rfc2426_escape(contact->car_phone)); if (contact->home_fax) fprintf(f_output, "TEL;TYPE=home,fax:%s\n", pst_rfc2426_escape(contact->home_fax)); if (contact->home_phone) fprintf(f_output, "TEL;TYPE=home,voice:%s\n", pst_rfc2426_escape(contact->home_phone)); if (contact->home_phone2) fprintf(f_output, "TEL;TYPE=home,voice:%s\n", pst_rfc2426_escape(contact->home_phone2)); if (contact->isdn_phone) fprintf(f_output, "TEL;TYPE=isdn:%s\n", pst_rfc2426_escape(contact->isdn_phone)); if (contact->mobile_phone) fprintf(f_output, "TEL;TYPE=cell,voice:%s\n", pst_rfc2426_escape(contact->mobile_phone)); if (contact->other_phone) fprintf(f_output, "TEL;TYPE=msg:%s\n", pst_rfc2426_escape(contact->other_phone)); if (contact->pager_phone) fprintf(f_output, "TEL;TYPE=pager:%s\n", pst_rfc2426_escape(contact->pager_phone)); if (contact->primary_fax) fprintf(f_output, "TEL;TYPE=fax,pref:%s\n", pst_rfc2426_escape(contact->primary_fax)); if (contact->primary_phone) fprintf(f_output, "TEL;TYPE=phone,pref:%s\n", pst_rfc2426_escape(contact->primary_phone)); if (contact->radio_phone) fprintf(f_output, "TEL;TYPE=pcs:%s\n", pst_rfc2426_escape(contact->radio_phone)); if (contact->telex) fprintf(f_output, "TEL;TYPE=bbs:%s\n", pst_rfc2426_escape(contact->telex)); if (contact->job_title) fprintf(f_output, "TITLE:%s\n", pst_rfc2426_escape(contact->job_title)); if (contact->profession) fprintf(f_output, "ROLE:%s\n", pst_rfc2426_escape(contact->profession)); if (contact->assistant_name || contact->assistant_phone) { fprintf(f_output, "AGENT:BEGIN:VCARD\n"); if (contact->assistant_name) fprintf(f_output, "FN:%s\n", pst_rfc2426_escape(contact->assistant_name)); if (contact->assistant_phone) fprintf(f_output, "TEL:%s\n", pst_rfc2426_escape(contact->assistant_phone)); } if (contact->company_name) fprintf(f_output, "ORG:%s\n", pst_rfc2426_escape(contact->company_name)); if (comment) fprintf(f_output, "NOTE:%s\n", pst_rfc2426_escape(comment)); fprintf(f_output, "VERSION: 3.0\n"); fprintf(f_output, "END:VCARD\n\n"); DEBUG_RET(); } void write_appointment(FILE* f_output, pst_item_appointment* appointment, pst_item_email* email, FILETIME* create_date, FILETIME* modify_date) { fprintf(f_output, "BEGIN:VEVENT\n"); if (create_date) fprintf(f_output, "CREATED:%s\n", pst_rfc2445_datetime_format(create_date)); if (modify_date) fprintf(f_output, "LAST-MOD:%s\n", pst_rfc2445_datetime_format(modify_date)); if (email && email->subject) fprintf(f_output, "SUMMARY:%s\n", pst_rfc2426_escape(email->subject->subj)); if (email && email->body) fprintf(f_output, "DESCRIPTION:%s\n", pst_rfc2426_escape(email->body)); if (appointment && appointment->start) fprintf(f_output, "DTSTART;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(appointment->start)); if (appointment && appointment->end) fprintf(f_output, "DTEND;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(appointment->end)); if (appointment && appointment->location) fprintf(f_output, "LOCATION:%s\n", pst_rfc2426_escape(appointment->location)); if (appointment) { switch (appointment->showas) { case PST_FREEBUSY_TENTATIVE: fprintf(f_output, "STATUS:TENTATIVE\n"); break; case PST_FREEBUSY_FREE: // mark as transparent and as confirmed fprintf(f_output, "TRANSP:TRANSPARENT\n"); case PST_FREEBUSY_BUSY: case PST_FREEBUSY_OUT_OF_OFFICE: fprintf(f_output, "STATUS:CONFIRMED\n"); break; } switch (appointment->label) { case PST_APP_LABEL_NONE: fprintf(f_output, "CATEGORIES:NONE\n"); break; case PST_APP_LABEL_IMPORTANT: fprintf(f_output, "CATEGORIES:IMPORTANT\n"); break; case PST_APP_LABEL_BUSINESS: fprintf(f_output, "CATEGORIES:BUSINESS\n"); break; case PST_APP_LABEL_PERSONAL: fprintf(f_output, "CATEGORIES:PERSONAL\n"); break; case PST_APP_LABEL_VACATION: fprintf(f_output, "CATEGORIES:VACATION\n"); break; case PST_APP_LABEL_MUST_ATTEND: fprintf(f_output, "CATEGORIES:MUST-ATTEND\n"); break; case PST_APP_LABEL_TRAVEL_REQ: fprintf(f_output, "CATEGORIES:TRAVEL-REQUIRED\n"); break; case PST_APP_LABEL_NEEDS_PREP: fprintf(f_output, "CATEGORIES:NEEDS-PREPARATION\n"); break; case PST_APP_LABEL_BIRTHDAY: fprintf(f_output, "CATEGORIES:BIRTHDAY\n"); break; case PST_APP_LABEL_ANNIVERSARY: fprintf(f_output, "CATEGORIES:ANNIVERSARY\n"); break; case PST_APP_LABEL_PHONE_CALL: fprintf(f_output, "CATEGORIES:PHONE-CALL\n"); break; } } fprintf(f_output, "END:VEVENT\n\n"); } void create_enter_dir(struct file_ll* f, pst_item *item) { f->email_count = 0; f->skip_count = 0; f->type = item->type; f->stored_count = (item->folder) ? item->folder->email_count : 0; DEBUG_ENT("create_enter_dir"); if (mode == MODE_KMAIL) f->name = mk_kmail_dir(item->file_as); //create directory and form filename else if (mode == MODE_RECURSE) f->name = mk_recurse_dir(item->file_as); else if (mode == MODE_SEPERATE) { // do similar stuff to recurse here. mk_seperate_dir(item->file_as); f->name = (char*) xmalloc(10); memset(f->name, 0, 10); // sprintf(f->name, SEP_MAIL_FILE_TEMPLATE, f->email_count); } else { f->name = (char*) xmalloc(strlen(item->file_as)+strlen(OUTPUT_TEMPLATE)+1); sprintf(f->name, OUTPUT_TEMPLATE, item->file_as); } f->dname = (char*) xmalloc(strlen(item->file_as)+1); strcpy(f->dname, item->file_as); if (overwrite != 1) { int x = 0; char *temp = (char*) xmalloc (strlen(f->name)+10); //enough room for 10 digits sprintf(temp, "%s", f->name); check_filename(temp); while ((f->output = fopen(temp, "r"))) { DEBUG_MAIN(("need to increase filename because one already exists with that name\n")); DEBUG_MAIN(("- increasing it to %s%d\n", f->name, x)); x++; sprintf(temp, "%s%08d", f->name, x); DEBUG_MAIN(("- trying \"%s\"\n", f->name)); if (x == 99999999) { DIE(("create_enter_dir: Why can I not create a folder %s? I have tried %i extensions...\n", f->name, x)); } fclose(f->output); } if (x > 0) { //then the f->name should change free (f->name); f->name = temp; } else { free(temp); } } DEBUG_MAIN(("f->name = %s\nitem->folder_name = %s\n", f->name, item->file_as)); if (mode != MODE_SEPERATE) { check_filename(f->name); if (!(f->output = fopen(f->name, "w"))) { DIE(("create_enter_dir: Could not open file \"%s\" for write\n", f->name)); } } DEBUG_RET(); } void close_enter_dir(struct file_ll *f) { DEBUG_MAIN(("main: Email Count for folder %s is %i\n", f->dname, f->email_count)); if (output_mode != OUTPUT_QUIET) printf("\t\"%s\" - %i items done, skipped %i, should have been %i\n", f->dname, f->email_count, f->skip_count, f->stored_count); if (f->output) fclose(f->output); free(f->name); free(f->dname); if (mode == MODE_KMAIL) close_kmail_dir(); else if (mode == MODE_RECURSE) close_recurse_dir(); else if (mode == MODE_SEPERATE) close_seperate_dir(); } diff --git a/src/readpstlog.c b/src/readpstlog.c index c89e3c8..16d6f82 100644 --- a/src/readpstlog.c +++ b/src/readpstlog.c @@ -1,257 +1,261 @@ #include "define.h" #include #include #include #ifndef _WIN32 # include #endif #ifndef __GNUC__ # include "XGetopt.h" #endif #define BUF_SIZE 4096 int usage(); size_t get(void * buf, int size, unsigned int count, FILE *fp); int split_args(char *args, int **targ); int is_in(int a, int *b, int c); int main(int argc, char** argv) { int identity = 0; int level = 0; off_t *i = NULL; int x, ptr, stop=0, flag; char *fname, *buf, rec_type; unsigned char version; int *show_type=NULL, show_size=0; int *ex_type=NULL, ex_size=0; - unsigned int funcname=0, filename=0, text=0, end=0, dtype=0, line=0, c; + unsigned int funcname=0, filename=0, text=0, end=0, dtype=0, line=0; + int c; + char ch; FILE *fp; struct pst_debug_file_rec_m mfile_rec; struct pst_debug_file_rec_l lfile_rec; char format = 'D'; // default while ((c = getopt(argc, argv, "f:t:x:")) != -1) { - switch(c) { + ch = c; + switch(ch) { case 'f': // change the output format format = toupper(optarg[0]); break; case 't': //change the type of statements shown show_size = split_args(optarg, &show_type); // type = atoi(optarg); break; case 'x': // change the type of statements excluded ex_size = split_args(optarg, &ex_type); break; } } if (argc > optind) { fname = argv[optind++]; } else { usage(); exit(2); } fp = fopen(fname, "rb"); if (fp == NULL) { printf("Error. couldn't open debug file\n"); return 2; } if (get(&version, sizeof(char), 1, fp)==0) { printf("Error. could not read version byte from front of file"); return 3; } if (version > DEBUG_VERSION) { printf("Version number is higher than the format I know about."); return 4; } buf = (char*) xmalloc(BUF_SIZE); while (!stop) { off_t temp; if (fread(&temp, sizeof(off_t), 1, fp)<=0) break; x = (int)temp; ptr = 0; if (x > 0) { if (i) free(i); i = (off_t*)xmalloc(sizeof(off_t)*(x+1)); // plus 1 cause we want to read the offset of the next index if (get(i, sizeof(off_t), x+1, fp)==0) { // we have reached the end of the debug file printf("oh dear. we must now end\n"); break; } while (ptr < x) { fseek(fp, i[ptr++], SEEK_SET); get(&rec_type, 1, sizeof(char), fp); if (rec_type == 'L') { get(&lfile_rec, sizeof(lfile_rec), 1, fp); funcname=lfile_rec.funcname; filename=lfile_rec.filename; text = lfile_rec.text; end = lfile_rec.end; dtype = lfile_rec.type; line = lfile_rec.line; } else if (rec_type == 'M') { get(&mfile_rec, sizeof(mfile_rec), 1, fp); funcname = mfile_rec.funcname; filename = mfile_rec.filename; text = mfile_rec.text; end = mfile_rec.end; dtype = mfile_rec.type; line = mfile_rec.line; } if (dtype == DEBUG_FUNCENT_NO) level++; if ((show_type == NULL || is_in(dtype, show_type, show_size)) && (ex_type == NULL || !is_in(dtype, ex_type, ex_size))) { - c = 0; flag = 0; + unsigned int c = 0; + flag = 0; while (c < end) { int ii = (level-1) * 4; if (ii < 0) ii = 0; if (ii > 64) ii = 64; char indent[ii+1]; memset(indent, ' ', ii); indent[ii] = '\0'; if (c + (BUF_SIZE-1) < end) { get(buf, 1, BUF_SIZE-1, fp); buf[BUF_SIZE-1] = '\0'; c += BUF_SIZE-1; } else { get(buf, 1, end-c, fp); buf[end-c] = '\0'; c = end; } if (flag == 0) { if (format == 'I') { // indented text format char *b = buf+text; identity++; //printf("%s %d %s/%s[%d]: ", indent, identity, &buf[filename], &buf[funcname], line); printf("%s %s/%s[%d]: ", indent, &buf[filename], &buf[funcname], line); while (b) { char *p = strchr(b, '\n'); if (p) { *p = '\0'; printf("%s\n%s ", b, indent); b = p + 1; } else { printf("%s", b); b = NULL; } } } else if (format == 'T') { // text format printf("%s/%s[%d]: %s", &buf[filename], &buf[funcname], line, &buf[text]); } else { printf("Type: %d\nFile[line]: %s[%d]\nFunction:%s\nText:%s", dtype, &buf[filename], line, &buf[funcname], &buf[text]); } flag = 1; } else { if (format == 'I') { char *b = buf; while (b) { char *p = strchr(b, '\n'); if (p) { *p = '\0'; printf("%s\n%s ", b, indent); b = p + 1; } else { printf("%s", b); b = NULL; } } } else printf("%s", buf); } } printf("\n"); } if (dtype == DEBUG_FUNCRET_NO) level--; } if (fseek(fp, i[ptr], SEEK_SET)==-1) { printf("finished\n"); break; } } else { printf("...no more items\n"); break; } } free(buf); fclose(fp); if (format == 'I') printf("final indent level = %i\n", level); return 0; } size_t get(void *buf, int size, unsigned int count, FILE *fp) { size_t z; if ((z=fread(buf, size, count, fp)) < count) { printf("Read Failed! (size=%d, count=%d,z=%ld)\n", size, count, (long)z); exit(1); } return z; } int usage() { printf("readlog -t[show_type] -x[exclude_type] -f[format] filename\n"); printf("\tformat:\n\t\tt: text log format\n"); printf("\t\ti: indented text log format\n"); printf("\tshow_type:\n\t\tcomma separated list of types to show " "[ie, 2,4,1,6]\n"); printf("\texclude_type:\n\t\tcomma separated list of types to exclude " "[ie, 1,5,3,7]\n"); return 0; } int split_args(char *args, int **targ) { int count = 1, *i, x, z; char *tmp = args, *y; if (*targ != NULL) { free(*targ); } // find the number of tokens in the string. Starting // from 1 cause there will always be one while ((tmp = strchr(tmp, ',')) != NULL) { tmp++; count++; } *targ = (int*)xmalloc(count * sizeof(int)); i = *targ; // for convienience tmp = args; z = 0; for (x = 0; x < count; x++) { y = strtok(tmp, ","); tmp = NULL; // must be done after first call if (y != NULL) { i[x] = atoi(y); z++; } } return z; } // checks to see if the first arg is in the array of the second arg, // the size of which is specified with the third arg. If the second // arg is NULL, then it is obvious that it is not there. int is_in(int a, int *b, int c){ int d = 0; if (b == NULL || c == 0) { // no array or no items in array return 0; } while (d < c) { if (a == b[d]) return 1; d++; } return 0; } diff --git a/src/vbuf.c b/src/vbuf.c index a19f5d5..0fb17b6 100644 --- a/src/vbuf.c +++ b/src/vbuf.c @@ -1,932 +1,1006 @@ -// {{{ includes #include -//#include "defines.h" #include #include +#include #include #include #include #include #include #include #include "vbuf.h" #include "generic.h" #ifdef WITH_DMALLOC #include #endif -// }}} -int skip_nl( char *s ) // {{{ returns the width of the newline at s[0] +int skip_nl(char *s) { - if( s[0] == '\n' ) return 1; - if( s[0] == '\r' && s[1] == '\n' ) return 2; - if( s[0] == '\0' ) return 0; - return -1; -} // }}} -int find_nl( vstr *vs ) // {{{ find newline of type type in b -{ - char *nextr, *nextn; + if (s[0] == '\n') + return 1; + if (s[0] == '\r' && s[1] == '\n') + return 2; + if (s[0] == '\0') + return 0; + return -1; +} - nextr = memchr( vs->b, '\r', vs->dlen ); - nextn = memchr( vs->b, '\n', vs->dlen ); - //case 1: UNIX, we find \n first - if( nextn && (nextr == NULL || nextr > nextn ) ) { - return nextn - vs->b; - } +int find_nl(vstr * vs) +{ + char *nextr, *nextn; - //case 2: DOS, we find \r\n - if( NULL != nextr && NULL != nextn && 1 == (char*)nextn - (char*)nextr ) { - return nextr - vs->b; - } + nextr = memchr(vs->b, '\r', vs->dlen); + nextn = memchr(vs->b, '\n', vs->dlen); - //case 3: we find nothing + //case 1: UNIX, we find \n first + if (nextn && (nextr == NULL || nextr > nextn)) { + return nextn - vs->b; + } + //case 2: DOS, we find \r\n + if (NULL != nextr && NULL != nextn && 1 == (char *) nextn - (char *) nextr) { + return nextr - vs->b; + } + //case 3: we find nothing + + return -1; +} - return -1; -} // }}} -// {{{ UTF8 <-> UTF16 <-> ISO8859 Character set conversion functions and (ack) their globals +// UTF8 <-> UTF16 <-> ISO8859 Character set conversion functions and (ack) their globals //TODO: the following should not be -char *wwbuf=NULL; -size_t nwwbuf=0; -static int unicode_up=0; +char *wwbuf = NULL; +size_t nwwbuf = 0; +static int unicode_up = 0; iconv_t i16to8, i8to16, i8859_1to8, i8toi8859_1; -void unicode_init() // {{{ + +void unicode_init() { - char *wipe = ""; - char dump[4]; + char *wipe = ""; + char dump[4]; - if( unicode_up ) unicode_close(); + if (unicode_up) + unicode_close(); - if( (iconv_t)-1 == (i16to8 = iconv_open( "UTF-8", "UTF-16" ) ) ) { - fprintf(stderr, "doexport(): Couldn't open iconv descriptor for UTF-16 to UTF-8.\n"); - exit( 1 ); - } + if ((iconv_t) - 1 == (i16to8 = iconv_open("UTF-8", "UTF-16"))) { + fprintf(stderr, "doexport(): Couldn't open iconv descriptor for UTF-16 to UTF-8.\n"); + exit(1); + } - if( (iconv_t)-1 == (i8to16 = iconv_open( "UTF-16", "UTF-8" ) ) ) { - fprintf(stderr, "doexport(): Couldn't open iconv descriptor for UTF-8 to UTF-16.\n"); - exit( 2 ); - } + if ((iconv_t) - 1 == (i8to16 = iconv_open("UTF-16", "UTF-8"))) { + fprintf(stderr, "doexport(): Couldn't open iconv descriptor for UTF-8 to UTF-16.\n"); + exit(2); + } + //iconv will prefix output with an FF FE (utf-16 start seq), the following dumps that. + memset(dump, 'x', 4); + ASSERT(0 == utf8to16(wipe, 1, dump, 4), "unicode_init(): attempt to dump FF FE failed."); - //iconv will prefix output with an FF FE (utf-16 start seq), the following dumps that. - memset( dump, 'x', 4 ); - ASSERT( 0 == utf8to16( wipe, 1, dump, 4 ), "unicode_init(): attempt to dump FF FE failed." ); + if ((iconv_t) - 1 == (i8859_1to8 = iconv_open("UTF-8", "ISO_8859-1"))) { + fprintf(stderr, "doexport(): Couldn't open iconv descriptor for ASCII to UTF-8.\n"); + exit(1); + } - if( (iconv_t)-1 == (i8859_1to8 = iconv_open( "UTF-8", "ISO_8859-1" ) ) ) { - fprintf(stderr, "doexport(): Couldn't open iconv descriptor for ASCII to UTF-8.\n"); - exit( 1 ); - } + if ((iconv_t) - 1 == (i8toi8859_1 = iconv_open("ISO_8859-1", "UTF-8"))) { + fprintf(stderr, "doexport(): Couldn't open iconv descriptor for UTF-8 to ASCII.\n"); + exit(1); + } + unicode_up = 1; +} - if( (iconv_t)-1 == (i8toi8859_1 = iconv_open( "ISO_8859-1", "UTF-8" ) ) ) { - fprintf(stderr, "doexport(): Couldn't open iconv descriptor for UTF-8 to ASCII.\n"); - exit( 1 ); - } - unicode_up = 1; -} -// }}} -void unicode_close() // {{{ +void unicode_close() { - unicode_up = 0; - iconv_close( i8to16 ); - iconv_close( i16to8 ); - iconv_close( i8859_1to8 ); - iconv_close( i8toi8859_1 ); + unicode_up = 0; + iconv_close(i8to16); + iconv_close(i16to8); + iconv_close(i8859_1to8); + iconv_close(i8toi8859_1); } -// }}} -//int utf16_write( FILE* stream, const void *buf, size_t count ) // {{{ write utf-8 or iso_8869-1 to stream after converting it to utf-16 + +//int utf16_write( FILE* stream, const void *buf, size_t count ) // write utf-8 or iso_8869-1 to stream after converting it to utf-16 //{ // -// //TODO: if anything big comes through here we are sunk, should do it -// //bit-by-bit, not one-big-gulp -// -// size_t inbytesleft, outbytesleft; -// char *inbuf, *outbuf; -// size_t icresult; -// size_t rl; +// //TODO: if anything big comes through here we are sunk, should do it +// //bit-by-bit, not one-big-gulp +// +// size_t inbytesleft, outbytesleft; +// char *inbuf, *outbuf; +// size_t icresult; +// size_t rl; // -// //do we have enough buffer space? -// if( !wwbuf || nwwbuf < (count * 2 + 2) ) { -// wwbuf = F_REALLOC( wwbuf, count * 2 +2 ); +// //do we have enough buffer space? +// if( !wwbuf || nwwbuf < (count * 2 + 2) ) { +// wwbuf = F_REALLOC( wwbuf, count * 2 +2 ); // -// nwwbuf = count * 2 + 2; -// } +// nwwbuf = count * 2 + 2; +// } // -// inbytesleft = count; outbytesleft = nwwbuf; -// inbuf = (char*)buf; outbuf = wwbuf; +// inbytesleft = count; outbytesleft = nwwbuf; +// inbuf = (char*)buf; outbuf = wwbuf; // -//// fprintf(stderr, "X%s, %dX", (char*)buf, strlen( buf )); -//// fflush(stderr); +//// fprintf(stderr, "X%s, %dX", (char*)buf, strlen( buf )); +//// fflush(stderr); // -// if( (rl = strlen( buf ) + 1) != count ) { -// fprintf(stderr, "utf16_write(): reported buffer size (%d) does not match string length (%d)\n", -// count, -// rl); +// if( (rl = strlen( buf ) + 1) != count ) { +// fprintf(stderr, "utf16_write(): reported buffer size (%d) does not match string length (%d)\n", +// count, +// rl); // -// //hexdump( (char*)buf, 0, count, 1 ); +// //hexdump( (char*)buf, 0, count, 1 ); // -// raise( SIGSEGV ); -// inbytesleft = rl; -// } +// raise( SIGSEGV ); +// inbytesleft = rl; +// } // -//// fprintf(stderr, " attempting to convert:\n"); -//// hexdump( (char*)inbuf, 0, count, 1 ); +//// fprintf(stderr, " attempting to convert:\n"); +//// hexdump( (char*)inbuf, 0, count, 1 ); // -// icresult = iconv( i8to16, &inbuf, &inbytesleft, &outbuf, &outbytesleft ); +// icresult = iconv( i8to16, &inbuf, &inbytesleft, &outbuf, &outbytesleft ); // -//// fprintf(stderr, " converted:\n"); -//// hexdump( (char*)buf, 0, count, 1 ); +//// fprintf(stderr, " converted:\n"); +//// hexdump( (char*)buf, 0, count, 1 ); // -//// fprintf(stderr, " to:\n"); -//// hexdump( (char*)wwbuf, 0, nwwbuf, 1 ); +//// fprintf(stderr, " to:\n"); +//// hexdump( (char*)wwbuf, 0, nwwbuf, 1 ); // -// if( (size_t)-1 == icresult ) { -// fprintf(stderr, "utf16_write(): iconv failure(%d): %s\n", errno, strerror( errno ) ); -// fprintf(stderr, " attempted to convert:\n"); -// hexdump( (char*)inbuf, 0, count, 1 ); +// if( (size_t)-1 == icresult ) { +// fprintf(stderr, "utf16_write(): iconv failure(%d): %s\n", errno, strerror( errno ) ); +// fprintf(stderr, " attempted to convert:\n"); +// hexdump( (char*)inbuf, 0, count, 1 ); // -// fprintf(stderr, " result:\n"); -// hexdump( (char*)outbuf, 0, count, 1 ); +// fprintf(stderr, " result:\n"); +// hexdump( (char*)outbuf, 0, count, 1 ); // -// fprintf(stderr, "I'm going to segfault now.\n"); -// raise( SIGSEGV ); -// exit(1); -// } +// fprintf(stderr, "I'm going to segfault now.\n"); +// raise( SIGSEGV ); +// exit(1); +// } // -// if( inbytesleft > 0 ) { -// fprintf(stderr, "utf16_write(): iconv returned a short count.\n"); -// exit(1); -// } +// if( inbytesleft > 0 ) { +// fprintf(stderr, "utf16_write(): iconv returned a short count.\n"); +// exit(1); +// } // -// return fwrite( wwbuf, nwwbuf - outbytesleft - 2, 1, stream ); +// return fwrite( wwbuf, nwwbuf - outbytesleft - 2, 1, stream ); //} -// }}} //char *utf16buf = NULL; //int utf16buf_len = 0; // -//int utf16_fprintf( FILE* stream, const char *fmt, ... ) // {{{ +//int utf16_fprintf( FILE* stream, const char *fmt, ... ) //{ -// int result=0; -// va_list ap; +// int result=0; +// va_list ap; // -// if( utf16buf == NULL ) { -// utf16buf = (char*)F_MALLOC( SZ_MAX + 1 ); +// if( utf16buf == NULL ) { +// utf16buf = (char*)F_MALLOC( SZ_MAX + 1 ); // -// utf16buf_len = SZ_MAX + 1; -// } +// utf16buf_len = SZ_MAX + 1; +// } // -// va_start( ap, fmt ); +// va_start( ap, fmt ); // -// result = vsnprintf( utf16buf, utf16buf_len, fmt, ap ); +// result = vsnprintf( utf16buf, utf16buf_len, fmt, ap ); // -// if( result + 1 > utf16buf_len ) { //didn't have space, realloc() and try again -// fprintf(stderr, "utf16_fprintf(): buffer too small (%d), F_MALLOC(%d)\n", utf16buf_len, result); -// free( utf16buf ); -// utf16buf_len = result + 1; -// utf16buf = (char*)F_MALLOC( utf16buf_len ); +// if( result + 1 > utf16buf_len ) { //didn't have space, realloc() and try again +// fprintf(stderr, "utf16_fprintf(): buffer too small (%d), F_MALLOC(%d)\n", utf16buf_len, result); +// free( utf16buf ); +// utf16buf_len = result + 1; +// utf16buf = (char*)F_MALLOC( utf16buf_len ); // -// result = vsnprintf( utf16buf, utf16buf_len, fmt, ap ); -// } +// result = vsnprintf( utf16buf, utf16buf_len, fmt, ap ); +// } // // -// //didn't have space...again...something weird is going on... -// ASSERT( result + 1 <= utf16buf_len, "utf16_fprintf(): Unpossible error!\n"); +// //didn't have space...again...something weird is going on... +// ASSERT( result + 1 <= utf16buf_len, "utf16_fprintf(): Unpossible error!\n"); // -// if( 1 != utf16_write( stream, utf16buf, result + 1 ) ) -// DIE( "Write error? -> %s or %s\n", strerror( errno ), uerr_str( uerr_get() ) ); +// if( 1 != utf16_write( stream, utf16buf, result + 1 ) ) +// DIE( "Write error? -> %s or %s\n", strerror( errno ), uerr_str( uerr_get() ) ); // -// return result; +// return result; //} -//// }}} -//int utf16to8( char *inbuf_o, char *outbuf_o, int length ) // {{{ +// +//int utf16to8( char *inbuf_o, char *outbuf_o, int length ) //{ -// int inbytesleft = length; -// int outbytesleft = length; -// char *inbuf = inbuf_o; -// char *outbuf = outbuf_o; -// int rlen = -1, tlen; -// int icresult = -1; -// -// int i, strlen=-1; -// -// DEBUG( -// fprintf(stderr, " utf16to8(): attempting to convert:\n"); -// //hexdump( (char*)inbuf_o, 0, length, 1 ); -// fflush(stderr); -// ); -// -// for( i=0; i actual string length -// //enum: zero terminated, length valid -// // zero terminated, length short //we won't go beyond length ever, so this is same as NZT case -// // zero terminated, length long -// // not zero terminated -// // TODO: MEMORY BUG HERE! -// for( tlen = 0; tlen <= inbytesleft - 2; tlen+=2 ) { -// if( inbuf_o[tlen] == 0 && inbuf_o[tlen+1] == 0 ){ -// rlen = tlen + 2; -// tlen = rlen; -// break; -// } -// if( tlen == inbytesleft )fprintf(stderr, "Space allocated for string > actual string length. Go windows!\n"); -// } -// -// if( rlen >= 0 ) -// icresult = iconv( i16to8, &inbuf, &rlen, &outbuf, &outbytesleft ); -// -// if( icresult == (size_t)-1 ) { -// fprintf(stderr, "utf16to8(): iconv failure(%d): %s\n", errno, strerror( errno ) ); -// fprintf(stderr, " attempted to convert:\n"); -// hexdump( (char*)inbuf_o, 0, length, 1 ); -// fprintf(stderr, " result:\n"); -// hexdump( (char*)outbuf_o, 0, length, 1 ); -// fprintf(stderr, " MyDirtyOut:\n"); -// for( i=0; i actual string length +// //enum: zero terminated, length valid +// // zero terminated, length short //we won't go beyond length ever, so this is same as NZT case +// // zero terminated, length long +// // not zero terminated +// // TODO: MEMORY BUG HERE! +// for( tlen = 0; tlen <= inbytesleft - 2; tlen+=2 ) { +// if( inbuf_o[tlen] == 0 && inbuf_o[tlen+1] == 0 ){ +// rlen = tlen + 2; +// tlen = rlen; +// break; +// } +// if( tlen == inbytesleft )fprintf(stderr, "Space allocated for string > actual string length. Go windows!\n"); +// } +// +// if( rlen >= 0 ) +// icresult = iconv( i16to8, &inbuf, &rlen, &outbuf, &outbytesleft ); +// +// if( icresult == (size_t)-1 ) { +// fprintf(stderr, "utf16to8(): iconv failure(%d): %s\n", errno, strerror( errno ) ); +// fprintf(stderr, " attempted to convert:\n"); +// hexdump( (char*)inbuf_o, 0, length, 1 ); +// fprintf(stderr, " result:\n"); +// hexdump( (char*)outbuf_o, 0, length, 1 ); +// fprintf(stderr, " MyDirtyOut:\n"); +// for( i=0; ib); - } - - return (-1 == len )?0:1; -} // }}} -int vb_utf16to8( vbuf *dest, char *buf, int len ) // {{{ -{ - int inbytesleft = len; - char *inbuf = buf; - //int rlen = -1, tlen; - int icresult = -1; - VBUF_STATIC( dumpster, 100 ); - - //int i; //, strlen=-1; - int outbytesleft; - char *outbuf; - - ASSERT( unicode_up, "vb_utf16to8() called before unicode started." ); - - if( 2 > dest->blen ) vbresize( dest, 2 ); - dest->dlen = 0; - - //Bad Things can happen if a non-zero-terminated utf16 string comes through here - if( !utf16_is_terminated( buf, len ) ) return -1; - - do { - outbytesleft = dest->blen - dest->dlen; - outbuf = dest->b + dest->dlen; - icresult = iconv( i16to8, &inbuf, &inbytesleft, &outbuf, &outbytesleft ); - dest->dlen = outbuf - dest->b; - vbgrow( dest, inbytesleft); - } while( (size_t)-1 == icresult && E2BIG == errno ); - - if( 0 != vb_utf8to16T( dumpster, dest->b, dest->dlen ) ) - DIE("Reverse conversion failed."); - - if( icresult == (size_t)-1 ) { - //TODO: error - //ERR_UNIX( errno, "vb_utf16to8():iconv failure: %s", strerror( errno ) ); - unicode_init(); - return -1; - /* - fprintf(stderr, " attempted to convert:\n"); - hexdump( (char*)cin, 0, inlen, 1 ); - fprintf(stderr, " result:\n"); - hexdump( (char*)bout->b, 0, bout->dlen, 1 ); - fprintf(stderr, " MyDirtyOut:\n"); - for( i=0; i actual string length - //enum: zero terminated, length valid - // zero terminated, length short //we won't go beyond length ever, so this is same as NZT case - // zero terminated, length long - // not zero terminated - // TODO: MEMORY BUG HERE! - // - /* - for( tlen = 0; tlen <= inbytesleft - 2; tlen+=2 ) { - if( inbuf_o[tlen] == 0 && inbuf_o[tlen+1] == 0 ){ - rlen = tlen + 2; - tlen = rlen; - break; - } - if( tlen == inbytesleft )fprintf(stderr, "Space allocated for string > actual string length. Go windows!\n"); - } - */ - - //if( rlen >= 0 ) - icresult = iconv( i8to16, &inbuf, &inbytesleft, &outbuf, &outbytesleft ); - - if( icresult == (size_t)-1 ) { - DIE("iconv failure(%d): %s\n", errno, strerror( errno ) ); - //fprintf(stderr, " attempted to convert:\n"); - //hexdump( (char*)inbuf_o, 0, iblen, 1 ); - //fprintf(stderr, " result:\n"); - //hexdump( (char*)outbuf_o, 0, oblen, 1 ); - //fprintf(stderr, " MyDirtyOut:\n"); -// for( i=0; i= 0 ) - int outbytesleft; - char *outbuf; - if( 2 > bout->blen ) vbresize( bout, 2 ); - bout->dlen = 0; - - do { - outbytesleft = bout->blen - bout->dlen; - outbuf = bout->b + bout->dlen; - icresult = iconv( i8to16, &inbuf, &inbytesleft, &outbuf, &outbytesleft ); - bout->dlen = outbuf - bout->b; - vbgrow( bout, 20 ); - } while( (size_t)-1 == icresult && E2BIG == errno ); - - if( icresult == (size_t)-1 ) { - WARN("iconv failure: %s", strerror( errno ) ); - //ERR_UNIX( errno, "vb_utf8to16():iconv failure: %s", strerror( errno ) ); - unicode_init(); - return -1; - /* - fprintf(stderr, "vb_utf8to16(): iconv failure(%d == %d?): %s\n", errno, E2BIG, strerror( errno ) ); - fprintf(stderr, " attempted to convert:\n"); - hexdump( (char*)cin, 0, inlen, 1 ); - fprintf(stderr, " result:\n"); - hexdump( (char*)bout->b, 0, bout->dlen, 1 ); - fprintf(stderr, " MyDirtyOut:\n"); - for( i=0; i 0; l -=2) { - *dest = *src; - dest++; src +=2; - } - *dest = 0; -} -// }}} -#endif +// + -void cheap_ascii2uni(char *src, char *dest, int l) /* {{{ Quick and dirty ascii to unicode */ +int utf16_is_terminated(char *str, int length) { - for (; l > 0; l--) { - *dest++ = *src++; - *dest++ = 0; + VSTR_STATIC(errbuf, 100); + int len = -1; + int i; + for (i = 0; i < length; i += 2) { + if (str[i] == 0 && str[i + 1] == 0) { + len = i; + } + } + + if (-1 == len) { + vshexdump(errbuf, str, 0, length, 1); + WARN("String is not zero terminated (probably broken data from registry) %s.", errbuf->b); + } - } + return (-1 == len) ? 0 : 1; } -// }}} -// }}} -// {{{ VARBUF Functions -vbuf *vballoc( size_t len ) // {{{ + +int vb_utf16to8(vbuf * dest, char *buf, int len) { - struct varbuf *result; + size_t inbytesleft = len; + char *inbuf = buf; + size_t icresult = (size_t)-1; + VBUF_STATIC(dumpster, 100); + + size_t outbytesleft = 0; + char *outbuf = NULL; + + ASSERT(unicode_up, "vb_utf16to8() called before unicode started."); + + if (2 > dest->blen) + vbresize(dest, 2); + dest->dlen = 0; + + //Bad Things can happen if a non-zero-terminated utf16 string comes through here + if (!utf16_is_terminated(buf, len)) + return -1; + + do { + outbytesleft = dest->blen - dest->dlen; + outbuf = dest->b + dest->dlen; + icresult = iconv(i16to8, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + dest->dlen = outbuf - dest->b; + vbgrow(dest, inbytesleft); + } while ((size_t)-1 == icresult && E2BIG == errno); + + if (0 != vb_utf8to16T(dumpster, dest->b, dest->dlen)) + DIE("Reverse conversion failed."); + + if (icresult == (size_t)-1) { + //TODO: error + //ERR_UNIX( errno, "vb_utf16to8():iconv failure: %s", strerror( errno ) ); + unicode_init(); + return -1; + /* + fprintf(stderr, " attempted to convert:\n"); + hexdump( (char*)cin, 0, inlen, 1 ); + fprintf(stderr, " result:\n"); + hexdump( (char*)bout->b, 0, bout->dlen, 1 ); + fprintf(stderr, " MyDirtyOut:\n"); + for( i=0; i actual string length + //enum: zero terminated, length valid + // zero terminated, length short //we won't go beyond length ever, so this is same as NZT case + // zero terminated, length long + // not zero terminated + // TODO: MEMORY BUG HERE! + // + /* + for( tlen = 0; tlen <= inbytesleft - 2; tlen+=2 ) { + if( inbuf_o[tlen] == 0 && inbuf_o[tlen+1] == 0 ){ + rlen = tlen + 2; + tlen = rlen; + break; + } + if( tlen == inbytesleft )fprintf(stderr, "Space allocated for string > actual string length. Go windows!\n"); + } + */ + + //if( rlen >= 0 ) + icresult = iconv(i8to16, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + + if (icresult == (size_t)-1) { + DIE("iconv failure(%d): %s\n", errno, strerror(errno)); + //fprintf(stderr, " attempted to convert:\n"); + //hexdump( (char*)inbuf_o, 0, iblen, 1 ); + //fprintf(stderr, " result:\n"); + //hexdump( (char*)outbuf_o, 0, oblen, 1 ); + //fprintf(stderr, " MyDirtyOut:\n"); +// for( i=0; i (size_t)INT_MAX) { + return (-1); + } + return (int) icresult; +} + + +int vb_utf8to16T(vbuf * bout, char *cin, int inlen) +{ + //TODO: This (and 8to16) are the most horrible things I have ever seen... + size_t inbytesleft = inlen; + char *inbuf = cin; + //int rlen = -1, tlen; + size_t icresult = (size_t)-1; + size_t outbytesleft = 0; + char *outbuf = NULL; + + if (2 > bout->blen) + vbresize(bout, 2); + bout->dlen = 0; + + do { + outbytesleft = bout->blen - bout->dlen; + outbuf = bout->b + bout->dlen; + icresult = iconv(i8to16, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + bout->dlen = outbuf - bout->b; + vbgrow(bout, 20); + } while ((size_t)-1 == icresult && E2BIG == errno); + + if (icresult == (size_t)-1) { + WARN("iconv failure: %s", strerror(errno)); + //ERR_UNIX( errno, "vb_utf8to16():iconv failure: %s", strerror( errno ) ); + unicode_init(); + return -1; + /* + fprintf(stderr, "vb_utf8to16(): iconv failure(%d == %d?): %s\n", errno, E2BIG, strerror( errno ) ); + fprintf(stderr, " attempted to convert:\n"); + hexdump( (char*)cin, 0, inlen, 1 ); + fprintf(stderr, " result:\n"); + hexdump( (char*)bout->b, 0, bout->dlen, 1 ); + fprintf(stderr, " MyDirtyOut:\n"); + for( i=0; i (size_t) INT_MAX) { + return (-1); + } + return icresult; +} - result = F_MALLOC( sizeof( struct varbuf ) ); - result->dlen = 0; - result->blen = 0; - result->buf = NULL; +/* Quick and dirty UNICODE to std. ascii */ +void cheap_uni2ascii(char *src, char *dest, int l) +{ - vbresize( result, len ); + for (; l > 0; l -= 2) { + *dest = *src; + dest++; + src += 2; + } + *dest = 0; +} - return result; -} // }}} -void vbcheck( vbuf *vb ) // {{{ +/* Quick and dirty ascii to unicode */ +void cheap_ascii2uni(char *src, char *dest, int l) { - ASSERT( vb->b - vb->buf <= vb->blen, "vbcheck(): vb->b outside of buffer range."); - ASSERT( vb->dlen <= vb->blen, "vbcheck(): data length > buffer length."); + for (; l > 0; l--) { + *dest++ = *src++; + *dest++ = 0; + + } +} + - ASSERT( vb->blen < 1024*1024, "vbcheck(): blen is a bit large...hmmm."); -} // }}} -void vbfree( vbuf *vb ) // {{{ +vbuf *vballoc(size_t len) { - free( vb->buf ); - free( vb ); -} // }}} -void vbclear( struct varbuf *vb ) // {{{ditch the data, keep the buffer + struct varbuf *result; + + result = F_MALLOC(sizeof(struct varbuf)); + + result->dlen = 0; + result->blen = 0; + result->buf = NULL; + + vbresize(result, len); + + return result; + +} + + +void vbcheck(vbuf * vb) { - vbresize( vb, 0 ); -} // }}} -void vbresize( struct varbuf *vb, size_t len ) // {{{ DESTRUCTIVELY grow or shrink buffer + ASSERT(vb->b >= vb->buf, "vbcheck(): data not inside buffer"); + ASSERT((size_t)(vb->b - vb->buf) <= vb->blen, "vbcheck(): vb->b outside of buffer range."); + ASSERT(vb->dlen <= vb->blen, "vbcheck(): data length > buffer length."); + ASSERT(vb->blen < 1024 * 1024, "vbcheck(): blen is a bit large...hmmm."); +} + + +void vbfree(vbuf * vb) { - vb->dlen = 0; + free(vb->buf); + free(vb); +} - if( vb->blen >= len ) { - vb->b = vb->buf; - return; - } - vb->buf = F_REALLOC( vb->buf, len ); - vb->b = vb->buf; - vb->blen = len; -} // }}} -int vbavail( vbuf *vb ) // {{{ +void vbclear(struct varbuf *vb) // ditch the data, keep the buffer { - return vb->blen - ((char*)vb->b - (char*)vb->buf + vb->dlen); -} // }}} -//void vbdump( vbuf *vb ) // {{{ TODO: to stdout? Yuck + vbresize(vb, 0); +} + + +void vbresize(struct varbuf *vb, size_t len) // DESTRUCTIVELY grow or shrink buffer +{ + vb->dlen = 0; + + if (vb->blen >= len) { + vb->b = vb->buf; + return; + } + + vb->buf = F_REALLOC(vb->buf, len); + vb->b = vb->buf; + vb->blen = len; +} + + +size_t vbavail(vbuf * vb) +{ + return vb->blen - vb->dlen - (size_t)(vb->b - vb->buf); +} + + +//void vbdump( vbuf *vb ) // TODO: to stdout? Yuck //{ -// printf("vb dump-------------\n"); +// printf("vb dump-------------\n"); // printf("dlen: %d\n", vb->dlen ); -// printf("blen: %d\n", vb->blen ); -// printf("b - buf: %d\n", vb->b - vb->buf ); -// printf("buf:\n"); -// hexdump( vb->buf, 0, vb->blen, 1 ); -// printf("b:\n"); -// hexdump( vb->b, 0, vb->dlen, 1 ); -// printf("^^^^^^^^^^^^^^^^^^^^\n"); -//} // }}} -void vbgrow( struct varbuf *vb, size_t len ) // {{{ out: vbavail(vb) >= len, data are preserved -{ - if( 0 == len ) return; - - if( 0 == vb->blen ) { - vbresize( vb, len ); - return; - } - - if( vb->dlen + len > vb->blen ) { - if( vb->dlen + len < vb->blen * 1.5 ) len = vb->blen * 1.5; - char *nb = F_MALLOC( vb->blen + len ); - //printf("vbgrow() got %p back from malloc(%d)\n", nb, vb->blen + len); - vb->blen = vb->blen + len; - memcpy( nb, vb->b, vb->dlen ); - - //printf("vbgrow() I am going to free %p\n", vb->buf ); - free( vb->buf ); - vb->buf = nb; - vb->b = vb->buf; - } else { - if( vb->b != vb->buf ) - memcpy( vb->buf, vb->b, vb->dlen ); - } - - vb->b = vb->buf; - - ASSERT( vbavail( vb ) >= len, "vbgrow(): I have failed in my mission." ); -} // }}} -void vbset( vbuf *vb, void *b, size_t len ) // {{{ set vbuf b size=len, resize if necessary, relen = how much to over-allocate -{ - vbresize( vb, len ); - - memcpy( vb->b, b, len ); - vb->dlen = len; -} // }}} -void vsskipws( vstr *vs ) // {{{ -{ - char *p = vs->b; - while( p - vs->b < vs->dlen && isspace( p[0] ) ) p++; - - vbskip( (vbuf*)vs, p - vs->b ); -} // }}} -void vbappend( struct varbuf *vb, void *b, size_t len ) // {{{ append len bytes of b to vbuf, resize if necessary -{ - if( 0 == vb->dlen ) { - vbset( vb, b, len ); - return; - } - - vbgrow( vb, len ); - - memcpy( vb->b + vb->dlen, b, len ); - vb->dlen += len; - - //printf("vbappend() end: >%s/%d<\n", vbuf->b, vbuf->dlen ); -} // }}} -void vbskip( struct varbuf *vb, size_t skip ) // {{{ dumps the first skip bytes from vbuf -{ - ASSERT( skip <= vb->dlen, "vbskip(): Attempt to seek past end of buffer." ); - //memmove( vbuf->b, vbuf->b + skip, vbuf->dlen - skip ); - vb->b += skip; - vb->dlen -= skip; -} // }}} -void vboverwrite( struct varbuf *vbdest, struct varbuf *vbsrc ) // {{{ overwrite vbdest with vbsrc -{ - vbresize( vbdest, vbsrc->blen ); - memcpy( vbdest->b, vbsrc->b, vbsrc->dlen ); - vbdest->blen = vbsrc->blen; - vbdest->dlen = vbsrc->dlen; -} // }}} -// }}} -// {{{ VARSTR Functions -vstr *vsalloc( size_t len ) // {{{ -{ - vstr *result = (vstr*)vballoc( len + 1 ); - vsset( result, "" ); - return result; -} // }}} -char *vsstr( vstr *vs ) // {{{ -{ - return vs->b; -} // }}} -size_t vslen( vstr *vs ) // {{{ -{ - return strlen( vsstr( vs )); -} // }}} -void vsfree( vstr *vs ) // {{{ -{ - vbfree( (vbuf*)vs ); -} // }}} -void vscharcat( vstr *vb, int ch ) // {{{ -{ - vbgrow( (vbuf*)vb, 1); - vb->b[vb->dlen-1] = ch; - vb->b[vb->dlen] = '\0'; - vb->dlen++; -} // }}} -void vsnprepend( vstr *vb, char *str, size_t len ) // {{{ prependappend string str to vbuf, vbuf must already contain a valid string -{ - ASSERT( vb->b[vb->dlen-1] == '\0', "vsncat(): attempt to append string to non-string."); - int sl = strlen( str ); - int n = (slb + n, vb->b, vb->dlen - 1 ); - memcpy( vb->b, str, n ); - //strncat( vb->b, str, n ); - - vb->dlen += n; - vb->b[ vb->dlen - 1 ] = '\0'; -} // }}} -void vsskip( vstr *vs, size_t len ) // {{{ len < dlen-1 -> skip len chars, else DIE -{ - ASSERT( len < vs->dlen - 1, "Attempt to skip past end of string" ); - vbskip( (vbuf*)vs, len ); -} // }}} -int vsskipline( vstr *vs ) // {{{ in: vb->b == "stuff\nmore_stuff"; out: vb->b == "more_stuff" -{ - int nloff = find_nl( vs ); - int nll = skip_nl( vs->b + nloff ); - - if( nloff < 0 ) { - //TODO: error - printf("vb_skipline(): there seems to be no newline here.\n"); - return -1; - } - if( skip_nl < 0 ) { - //TODO: error - printf("vb_skipline(): there seems to be no newline here...except there should be. :P\n"); - return -1; - } - - memmove( vs->b, vs->b + nloff + nll, vs->dlen - nloff - nll ); - - vs->dlen -= nloff + nll; - - return 0; -} // }}} -int vscatprintf( vstr *vs, char *fmt, ... ) // {{{ -{ - int size; - va_list ap; - - /* Guess we need no more than 100 bytes. */ - //vsresize( vb, 100 ); - if(!vs->b || vs->dlen == 0) { - vsset( vs, "" ); - } - - while (1) { - /* Try to print in the allocated space. */ - va_start(ap, fmt); - size = vsnprintf (vs->b + vs->dlen - 1, vs->blen - vs->dlen, fmt, ap); - va_end(ap); - - /* If that worked, return the string. */ - if (size > -1 && size < vs->blen - vs->dlen ) { - vs->dlen += size; - return size; - } - /* Else try again with more space. */ - if ( size >= 0 ) /* glibc 2.1 */ - vbgrow( (vbuf*)vs, size+1 ); /* precisely what is needed */ - else /* glibc 2.0 */ - vbgrow( (vbuf*)vs, vs->blen); - } -} // }}} -int vslast( vstr *vs ) // {{{ returns the last character stored in a vstr -{ - if( vs->dlen < 1 ) return -1; - if( vs->b[vs->dlen-1] != '\0' ) return -1; - if( vs->dlen == 1 ) return '\0'; - return vs->b[vs->dlen-2]; -} // }}} -void vs_printf( vstr *vs, char *fmt, ... ) // {{{ print over vb -{ - int size; - va_list ap; - - /* Guess we need no more than 100 bytes. */ - vbresize( (vbuf*)vs, 100 ); - - while (1) { - /* Try to print in the allocated space. */ - va_start(ap, fmt); - size = vsnprintf (vs->b, vs->blen, fmt, ap); - va_end(ap); - - /* If that worked, return the string. */ - if (size > -1 && size < vs->blen) { - vs->dlen = size + 1; - return; - } - /* Else try again with more space. */ - if ( size >= 0 ) /* glibc 2.1 */ - vbresize( (vbuf*)vs, size+1 ); /* precisely what is needed */ - else /* glibc 2.0 */ - vbresize( (vbuf*)vs, vs->blen*2); - } -} // }}} -void vs_printfa( vstr *vs, char *fmt, ... ) // {{{ printf append to vs -{ - int size; - va_list ap; - - if( vs->blen - vs->dlen < 50 ) - vbgrow( (vbuf*)vs, 100 ); - - while (1) { - /* Try to print in the allocated space. */ - va_start(ap, fmt); - size = vsnprintf (vs->b + vs->dlen - 1, vs->blen - vs->dlen + 1, fmt, ap); - va_end(ap); - - /* If that worked, return the string. */ - if (size > -1 && size < vs->blen) { - vs->dlen += size; - return; - } - /* Else try again with more space. */ - if ( size >= 0 ) /* glibc 2.1 */ - vbgrow( (vbuf*)vs, size+1 - vs->dlen ); /* precisely what is needed */ - else /* glibc 2.0 */ - vbgrow( (vbuf*)vs, size ); - } -} // }}} -void vshexdump( vstr *vs, char *b, size_t start, size_t stop, int ascii ) // {{{ -{ - char c; - int diff,i; - - while (start < stop ) { - diff = stop - start; - if (diff > 16) diff = 16; - - vs_printfa(vs, ":%08X ",start); - - for (i = 0; i < diff; i++) { - if( 8 == i ) vs_printfa( vs, " " ); - vs_printfa(vs, "%02X ",(unsigned char)*(b+start+i)); - } - if (ascii) { - for (i = diff; i < 16; i++) vs_printfa(vs, " "); - for (i = 0; i < diff; i++) { - c = *(b+start+i); - vs_printfa(vs, "%c", isprint(c) ? c : '.'); - } - } - vs_printfa(vs, "\n"); - start += 16; - } -} // }}} -void vsset( vstr *vs, char *s ) // {{{ Store string s in vs -{ - vsnset( vs, s, strlen( s ) ); -} // }}} -void vsnset( vstr *vs, char *s, size_t n ) // {{{ Store string s in vs -{ - vbresize( (vbuf*)vs, n + 1 ); - memcpy( vs->b, s, n); - vs->b[n] = '\0'; - vs->dlen = n+1; -} // }}} -void vsgrow( vstr *vs, size_t len ) // {{{ grow buffer by len bytes, data are preserved -{ - vbgrow( (vbuf*)vs, len ); -} // }}} -size_t vsavail( vstr *vs ) // {{{ -{ - return vbavail( (vbuf*)vs ); -} // }}} -void vsnset16( vstr *vs, char *s, size_t len ) // {{{ Like vbstrnset, but for UTF16 -{ - vbresize( (vbuf*)vs, len+1 ); - memcpy( vs->b, s, len ); - - vs->b[len] = '\0'; - vs->dlen = len+1; - vs->b[len] = '\0'; -} // }}} -void vscat( vstr *vs, char *str ) // {{{ -{ - vsncat( vs, str, strlen(str ) ); -} // }}} -int vscmp( vstr *vs, char *str ) // {{{ -{ - return strcmp( vs->b, str ); -} // }}} -void vsncat( vstr *vs, char *str, size_t len ) // {{{ append string str to vstr, vstr must already contain a valid string -{ - ASSERT( vs->b[vs->dlen-1] == '\0', "vsncat(): attempt to append string to non-string."); - int sl = strlen( str ); - int n = (slb + vs->dlen - 1, str, n ); - //strncat( vs->b, str, n ); - - vs->dlen += n; - vs->b[ vs->dlen - 1 ] = '\0'; -} // }}} -void vstrunc( vstr *v, int off ) // {{{ Drop chars [off..dlen] -{ - if( off >= v->dlen - 1 ) return; //nothing to do - v->b[off] = '\0'; - v->dlen = off + 1; -} -// }}} -// }}} -// {{{ User input -// TODO: not sure how useful this stuff is here -int fmyinput(char *prmpt, char *ibuf, int maxlen) /* {{{ get user input */ +// printf("blen: %d\n", vb->blen ); +// printf("b - buf: %d\n", vb->b - vb->buf ); +// printf("buf:\n"); +// hexdump( vb->buf, 0, vb->blen, 1 ); +// printf("b:\n"); +// hexdump( vb->b, 0, vb->dlen, 1 ); +// printf("^^^^^^^^^^^^^^^^^^^^\n"); +//} + + +void vbgrow(struct varbuf *vb, size_t len) // out: vbavail(vb) >= len, data are preserved { - printf("%s",prmpt); + if (0 == len) + return; + + if (0 == vb->blen) { + vbresize(vb, len); + return; + } - fgets(ibuf,maxlen+1,stdin); + if (vb->dlen + len > vb->blen) { + if (vb->dlen + len < vb->blen * 1.5) + len = vb->blen * 1.5; + char *nb = F_MALLOC(vb->blen + len); + //printf("vbgrow() got %p back from malloc(%d)\n", nb, vb->blen + len); + vb->blen = vb->blen + len; + memcpy(nb, vb->b, vb->dlen); - ibuf[strlen(ibuf)-1] = 0; + //printf("vbgrow() I am going to free %p\n", vb->buf ); + free(vb->buf); + vb->buf = nb; + vb->b = vb->buf; + } else { + if (vb->b != vb->buf) + memcpy(vb->buf, vb->b, vb->dlen); + } - return(strlen(ibuf)); + vb->b = vb->buf; + + ASSERT(vbavail(vb) >= len, "vbgrow(): I have failed in my mission."); } -// }}} -//}}} -// -// -//{{{ String formatting and output to FILE *stream or just stdout, etc + + +void vbset(vbuf * vb, void *b, size_t len) // set vbuf b size=len, resize if necessary, relen = how much to over-allocate +{ + vbresize(vb, len); + + memcpy(vb->b, b, len); + vb->dlen = len; +} + + +void vsskipws(vstr * vs) +{ + char *p = vs->b; + while ((size_t)(p - vs->b) < vs->dlen && isspace(p[0])) + p++; + + vbskip((vbuf *) vs, p - vs->b); +} + + +// append len bytes of b to vbuf, resize if necessary +void vbappend(struct varbuf *vb, void *b, size_t len) +{ + if (0 == vb->dlen) { + vbset(vb, b, len); + return; + } + vbgrow(vb, len); + memcpy(vb->b + vb->dlen, b, len); + vb->dlen += len; +} + + +// dumps the first skip bytes from vbuf +void vbskip(struct varbuf *vb, size_t skip) +{ + ASSERT(skip <= vb->dlen, "vbskip(): Attempt to seek past end of buffer."); + vb->b += skip; + vb->dlen -= skip; +} + + +// overwrite vbdest with vbsrc +void vboverwrite(struct varbuf *vbdest, struct varbuf *vbsrc) +{ + vbresize(vbdest, vbsrc->blen); + memcpy(vbdest->b, vbsrc->b, vbsrc->dlen); + vbdest->blen = vbsrc->blen; + vbdest->dlen = vbsrc->dlen; +} + + +vstr *vsalloc(size_t len) +{ + vstr *result = (vstr *) vballoc(len + 1); + vsset(result, ""); + return result; +} + + +char *vsstr(vstr * vs) +{ + return vs->b; +} + + +size_t vslen(vstr * vs) +{ + return strlen(vsstr(vs)); +} + + +void vsfree(vstr * vs) +{ + vbfree((vbuf *) vs); +} + + +void vscharcat(vstr * vb, int ch) +{ + vbgrow((vbuf *) vb, 1); + vb->b[vb->dlen - 1] = ch; + vb->b[vb->dlen] = '\0'; + vb->dlen++; +} + + +// prependappend string str to vbuf, vbuf must already contain a valid string +void vsnprepend(vstr * vb, char *str, size_t len) +{ + ASSERT(vb->b[vb->dlen - 1] == '\0', "vsncat(): attempt to append string to non-string."); + size_t sl = strlen(str); + size_t n = (sl < len) ? sl : len; + vbgrow((vbuf *) vb, n + 1); + memmove(vb->b + n, vb->b, vb->dlen - 1); + memcpy(vb->b, str, n); + vb->dlen += n; + vb->b[vb->dlen - 1] = '\0'; +} + + +// len < dlen-1 -> skip len chars, else DIE +void vsskip(vstr * vs, size_t len) +{ + ASSERT(len < vs->dlen - 1, "Attempt to skip past end of string"); + vbskip((vbuf *) vs, len); +} + + +// in: vb->b == "stuff\nmore_stuff"; out: vb->b == "more_stuff" +int vsskipline(vstr * vs) +{ + int nloff = find_nl(vs); + int nll = skip_nl(vs->b + nloff); + + if (nloff < 0) { + //TODO: error + printf("vb_skipline(): there seems to be no newline here.\n"); + return -1; + } + if (nll < 0) { + //TODO: error + printf("vb_skipline(): there seems to be no newline here...except there should be. :P\n"); + return -1; + } + + memmove(vs->b, vs->b + nloff + nll, vs->dlen - nloff - nll); + + vs->dlen -= nloff + nll; + + return 0; +} + + +int vscatprintf(vstr * vs, char *fmt, ...) +{ + int size; + va_list ap; + + /* Guess we need no more than 100 bytes. */ + //vsresize( vb, 100 ); + if (!vs->b || vs->dlen == 0) { + vsset(vs, ""); + } + + while (1) { + /* Try to print in the allocated space. */ + va_start(ap, fmt); + size = vsnprintf(vs->b + vs->dlen - 1, vs->blen - vs->dlen, fmt, ap); + va_end(ap); + + /* If that worked, return the string. */ + if ((size > -1) && ((size_t)size < vs->blen - vs->dlen)) { + vs->dlen += size; + return size; + } + /* Else try again with more space. */ + if (size >= 0) /* glibc 2.1 */ + vbgrow((vbuf *) vs, size + 1); /* precisely what is needed */ + else /* glibc 2.0 */ + vbgrow((vbuf *) vs, vs->blen); + } +} + + +// returns the last character stored in a vstr +int vslast(vstr * vs) +{ + if (vs->dlen < 1) + return -1; + if (vs->b[vs->dlen - 1] != '\0') + return -1; + if (vs->dlen == 1) + return '\0'; + return vs->b[vs->dlen - 2]; +} + + +// print over vb +void vs_printf(vstr * vs, char *fmt, ...) +{ + int size; + va_list ap; + + /* Guess we need no more than 100 bytes. */ + vbresize((vbuf *) vs, 100); + + while (1) { + /* Try to print in the allocated space. */ + va_start(ap, fmt); + size = vsnprintf(vs->b, vs->blen, fmt, ap); + va_end(ap); + + /* If that worked, return the string. */ + if ((size > -1) && ((size_t)size < vs->blen)) { + vs->dlen = size + 1; + return; + } + /* Else try again with more space. */ + if (size >= 0) /* glibc 2.1 */ + vbresize((vbuf *) vs, size + 1); /* precisely what is needed */ + else /* glibc 2.0 */ + vbresize((vbuf *) vs, vs->blen * 2); + } +} + + +// printf append to vs +void vs_printfa(vstr * vs, char *fmt, ...) +{ + int size; + va_list ap; + + if (vs->blen - vs->dlen < 50) + vbgrow((vbuf *) vs, 100); + + while (1) { + /* Try to print in the allocated space. */ + va_start(ap, fmt); + size = vsnprintf(vs->b + vs->dlen - 1, vs->blen - vs->dlen + 1, fmt, ap); + va_end(ap); + + /* If that worked, return the string. */ + if ((size > -1) && ((size_t)size < vs->blen)) { + vs->dlen += size; + return; + } + /* Else try again with more space. */ + if (size >= 0) /* glibc 2.1 */ + vbgrow((vbuf *) vs, size + 1 - vs->dlen); /* precisely what is needed */ + else /* glibc 2.0 */ + vbgrow((vbuf *) vs, size); + } +} + + +void vshexdump(vstr * vs, char *b, size_t start, size_t stop, int ascii) +{ + char c; + int diff, i; + + while (start < stop) { + diff = stop - start; + if (diff > 16) + diff = 16; + + vs_printfa(vs, ":%08X ", start); + + for (i = 0; i < diff; i++) { + if (8 == i) + vs_printfa(vs, " "); + vs_printfa(vs, "%02X ", (unsigned char) *(b + start + i)); + } + if (ascii) { + for (i = diff; i < 16; i++) + vs_printfa(vs, " "); + for (i = 0; i < diff; i++) { + c = *(b + start + i); + vs_printfa(vs, "%c", isprint(c) ? c : '.'); + } + } + vs_printfa(vs, "\n"); + start += 16; + } +} + + +void vsset(vstr * vs, char *s) // Store string s in vs +{ + vsnset(vs, s, strlen(s)); +} + + +void vsnset(vstr * vs, char *s, size_t n) // Store string s in vs +{ + vbresize((vbuf *) vs, n + 1); + memcpy(vs->b, s, n); + vs->b[n] = '\0'; + vs->dlen = n + 1; +} + + +void vsgrow(vstr * vs, size_t len) // grow buffer by len bytes, data are preserved +{ + vbgrow((vbuf *) vs, len); +} + + +size_t vsavail(vstr * vs) +{ + return vbavail((vbuf *) vs); +} + + +void vsnset16(vstr * vs, char *s, size_t len) // Like vbstrnset, but for UTF16 +{ + vbresize((vbuf *) vs, len + 1); + memcpy(vs->b, s, len); + + vs->b[len] = '\0'; + vs->dlen = len + 1; + vs->b[len] = '\0'; +} + + +void vscat(vstr * vs, char *str) +{ + vsncat(vs, str, strlen(str)); +} + + +int vscmp(vstr * vs, char *str) +{ + return strcmp(vs->b, str); +} + + +void vsncat(vstr * vs, char *str, size_t len) // append string str to vstr, vstr must already contain a valid string +{ + ASSERT(vs->b[vs->dlen - 1] == '\0', "vsncat(): attempt to append string to non-string."); + size_t sl = strlen(str); + size_t n = (sl < len) ? sl : len; + //string append + vbgrow((vbuf *) vs, n + 1); + memcpy(vs->b + vs->dlen - 1, str, n); + vs->dlen += n; + vs->b[vs->dlen - 1] = '\0'; +} + + +void vstrunc(vstr * v, size_t off) // Drop chars [off..dlen] +{ + if (off >= v->dlen - 1) + return; //nothing to do + v->b[off] = '\0'; + v->dlen = off + 1; +} + + +// TODO: not sure how useful this stuff is here +int fmyinput(char *prmpt, char *ibuf, int maxlen) +{ /* get user input */ + printf("%s", prmpt); + + fgets(ibuf, maxlen + 1, stdin); + + ibuf[strlen(ibuf) - 1] = 0; + + return (strlen(ibuf)); +} + + +// String formatting and output to FILE *stream or just stdout, etc // TODO: a lot of old, unused stuff in here -void vswinhex8(vstr *vs, unsigned char *hbuf, int start, int stop, int loff ) // {{{ Produce regedit-style hex output */ -{ - int i; - int lineflag=0; - - for( i=start; i= 77) { - lineflag=1; - loff=0; - vscatprintf( vs, "\\%s ", STUPID_CR ); - } - break; - case 1: - if( loff >= 75 ) { - loff=0; - vscatprintf( vs, "\\%s ", STUPID_CR ); - } - break; - } - // if( 24 < i || 0 == (i - 17) % 25 ) fprintf( stream, "\\\n " ); - } - } - - // fprintf( stream, "\n" ); -} // }}} +void vswinhex8(vstr * vs, unsigned char *hbuf, int start, int stop, int loff) // Produce regedit-style hex output */ +{ + int i; + int lineflag = 0; + + for (i = start; i < stop; i++) { + loff += vscatprintf(vs, "%02x", hbuf[i]); + if (i < stop - 1) { + loff += vscatprintf(vs, ","); + switch (lineflag) { + case 0: + if (loff >= 77) { + lineflag = 1; + loff = 0; + vscatprintf(vs, "\\%s ", STUPID_CR); + } + break; + case 1: + if (loff >= 75) { + loff = 0; + vscatprintf(vs, "\\%s ", STUPID_CR); + } + break; + } + } + } +} diff --git a/src/vbuf.h b/src/vbuf.h index 8abe4a7..3fd7a55 100644 --- a/src/vbuf.h +++ b/src/vbuf.h @@ -1,142 +1,142 @@ -/* {{{ vbuf.h - variable length buffer functions +/* vbuf.h - variable length buffer functions * * Functions that try to make dealing with buffers easier. * * vbuf * * vstr * - should always contain a valid string * - * }}} */ + */ #ifndef VBUF_H #define VBUF_H #define SZ_MAX 4096 #include #include #include /***************************************************/ -// {{{ Tokenizer const TOK_EMPTY, TOK_ELEMENT, DELIM +// Tokenizer const TOK_EMPTY, TOK_ELEMENT, DELIM #define DELIM '\\' #define TOK_EMPTY 0 #define TOK_DELIM 1 #define TOK_PARENT 2 #define TOK_CURRENT 3 #define TOK_ELEMENT 4 #define TOK_ERROR 10 #define TOK_BUF_SMALL 11 -// }}} -// Variable-length buffers -struct varbuf { // {{{ + +// Variable-length buffers +struct varbuf { size_t dlen; //length of data stored in buffer size_t blen; //length of buffer - char *buf; //buffer - char *b; //start of stored data -}; // }}} + char *buf; //buffer + char *b; //start of stored data +}; -// The exact same thing as a varbuf but should always contain at least '\0' -struct varstr { // {{{ +// The exact same thing as a varbuf but should always contain at least '\0' +struct varstr { size_t dlen; //length of data stored in buffer size_t blen; //length of buffer - char *buf; //buffer - char *b; //start of stored data -}; // }}} + char *buf; //buffer + char *b; //start of stored data +}; typedef struct varbuf vbuf; typedef struct varstr vstr; #define VBUF_STATIC(x,y) static vbuf *x = NULL; if(!x) x = vballoc(y); #define VSTR_STATIC(x,y) static vstr *x = NULL; if(!x) x = vsalloc(y); // vbuf functions struct varbuf *vballoc( size_t len ); void vbfree( vbuf *vb ); void vbclear( vbuf *vb ); //ditch the data, keep the buffer void vbresize( vbuf *vb, size_t len ); -int vbavail( vbuf *vb ); +size_t vbavail( vbuf *vb ); void vbdump( vbuf *vb ); void vbgrow( vbuf *vb, size_t len ); // grow buffer by len bytes, data are preserved void vbset( vbuf *vb, void *data, size_t len ); -void vbskipws( vbuf *vb ); +void vbskipws( vbuf *vb ); void vbappend( vbuf *vb, void *data, size_t length ); -void vbskip( vbuf *vb, size_t skip ); +void vbskip( vbuf *vb, size_t skip ); void vboverwrite( vbuf *vbdest, vbuf *vbsrc ); // vstr functions vstr *vsalloc( size_t len ); char *vsb( vstr *vs ); size_t vslen( vstr *vs ); //strlen void vsfree( vstr *vs ); void vsset( vstr *vs, char *s ); // Store string s in vb void vsnset( vstr *vs, char *s, size_t n ); // Store string s in vb void vsgrow( vstr *vs, size_t len ); // grow buffer by len bytes, data are preserved size_t vsavail( vstr *vs ); void vscat( vstr *vs, char *str ); void vsncat( vstr *vs, char *str, size_t len ); void vsnprepend( vstr *vs, char *str, size_t len ) ; void vsskip( vstr *vs, size_t len ); int vscmp( vstr *vs, char *str ); void vsskipws( vstr *vs ); void vs_printf( vstr *vs, char *fmt, ... ); void vs_printfa( vstr *vs, char *fmt, ... ); void vshexdump( vstr *vs, char *b, size_t start, size_t stop, int ascii ); int vscatprintf( vstr *vs, char *fmt, ... ); void vsvprintf( vstr *vs, char *fmt, va_list ap ); -void vstrunc( vstr *vs, int off ); // Drop chars [off..dlen] +void vstrunc( vstr *vs, size_t off ); // Drop chars [off..dlen] int vslast( vstr *vs ); // returns the last character stored in a vstr string void vscharcat( vstr *vs, int ch ); int vsutf16( vstr *vs, vbuf *in ); //in: in=zero-terminated utf16; out: vs=utf8; returns: 0 on success, else on fail int vs_parse_escaped_string( vstr *vs, char *str, size_t len ); /* * Windows unicode output trash - this stuff sucks * TODO: most of this should not be here */ void unicode_init(); void unicode_close(); int utf16_write( FILE* stream, const void *buf, size_t count ); int utf16_fprintf( FILE* stream, const char *fmt, ... ); int utf16to8( char *inbuf_o, char *outbuf_o, int length ); int utf8to16( char *inbuf_o, int iblen, char *outbuf_o, int oblen); int vb_utf8to16T( vbuf *bout, char *cin, int inlen ); int vb_utf16to8( vbuf *dest, char *buf, int len ); int iso8859_1to8( char *inbuf_o, char *outbuf_o, int length ); int utf8toascii( const char *inbuf_o, char *outbuf_o, int length ); /* dump ascii hex in windoze format */ void winhex(FILE* stream, unsigned char *hbuf, int start, int stop, int loff); void winhex8(FILE *stream, unsigned char *hbuf, int start, int stop, int loff ); void vbwinhex8(vbuf *vb, unsigned char *hbuf, int start, int stop, int loff ); /* general search routine, find something in something else */ int find_in_buf(char *buf, char *what, int sz, int len, int start); /* Get INTEGER from memory. This is probably low-endian specific? */ int get_int( char *array ); int find_nl( vstr *vs ); // find newline of type type in b int skip_nl( char *s ); // returns the width of the newline at s[0] //int vb_readline( struct varbuf *vb, int *ctype, FILE *in ); // read *AT LEAST* one full line of data from in int vb_skipline( struct varbuf *vb ); // in: vb->b == "stuff\nmore_stuff"; out: vb->b == "more_stuff" /* Get a string of HEX bytes (space separated), * or if first char is ' get an ASCII string instead. */ int gethexorstr(char **c, char *wb); char *esc_index( char *s, int c ); // just like index(3), but works on strings with escape sequences char *esc_rindex( char *s, int c ); // just like rindex(3), but works on strings with escape sequences char *tok_esc_char( char *s, int *is_esc, int *c ); int vb_path_token( vbuf *tok, char **path ); // returns things like TOK_EMPTY, TOK_ERROR, complete list at top int gettoken( char *tok, int len, char **path, char delim ); // Path tokenizer: increments path, dumps token in tok #endif