view lib-src/b2m.c @ 5560:58b38d5b32d0

Implement print-circle, allowing recursive and circular structures to be read. src/ChangeLog addition: 2011-09-04 Aidan Kehoe <kehoea@parhasard.net> * alloc.c: * alloc.c (ALLOC_FROB_BLOCK_LISP_OBJECT_1): * alloc.c (ALLOC_FROB_BLOCK_LISP_OBJECT): * alloc.c (cons_print_preprocess): * alloc.c (vector_print_preprocess): * alloc.c (vector_nsubst_structures_descend): * alloc.c (Fmake_symbol): * alloc.c (UNMARK_symbol): * alloc.c (sweep_symbols): * alloc.c (reinit_alloc_objects_early): * alloc.c (reinit_alloc_early): * bytecode.c: * bytecode.c (compiled_function_print_preprocess): * bytecode.c (compiled_function_nsubst_structures_descend): * bytecode.c (set_compiled_function_arglist): * bytecode.c (set_compiled_function_interactive): * bytecode.c (bytecode_objects_create): * chartab.c: * chartab.c (print_preprocess_mapper): * chartab.c (nsubst_structures_mapper): * chartab.c (char_table_nsubst_structures_descend): * chartab.c (chartab_objects_create): * elhash.c: * elhash.c (nsubst_structures_map_hash_table): * elhash.c (hash_table_nsubst_structures_descend): * elhash.c (print_preprocess_mapper): * elhash.c (hash_table_print_preprocess): * elhash.c (inchash_eq): * elhash.c (hash_table_objects_create): * elhash.c (syms_of_elhash): * elhash.h: * emacs.c (main_1): * fns.c: * fns.c (check_eq_nokey): * fns.c (Fnsubst): * fns.c (syms_of_fns): * lisp.h: * lisp.h (struct Lisp_Symbol): * lisp.h (IN_OBARRAY): * lisp.h (struct): * lisp.h (PRINT_PREPROCESS): * lread.c (read1): * lrecord.h: * lrecord.h (struct lrecord_implementation): * lrecord.h (DEFINE_DUMPABLE_MODULE_LISP_OBJECT): * print.c: * print.c (PRINT_CIRCLE_LIMIT): * print.c (print_continuous_numbering_changed): * print.c (print_prepare): * print.c (print_finish): * print.c (Fprin1_to_string): * print.c (print_cons): * print.c (print_preprocess_inchash_eq): * print.c (print_preprocess): * print.c (print_sort_get_numbers): * print.c (print_sort_compare_ordinals): * print.c (print_gensym_or_circle): * print.c (nsubst_structures_descend): * print.c (nsubst_structures): * print.c (print_internal): * print.c (print_symbol): * print.c (vars_of_print): * rangetab.c: * rangetab.c (range_table_print_preprocess): * rangetab.c (range_table_nsubst_structures_descend): * rangetab.c (rangetab_objects_create): * rangetab.c (syms_of_rangetab): * symbols.c: * symbols.c (symbol_print_preprocess): * symbols.c (Fintern): * symbols.c (Funintern): * symbols.c (reinit_symbol_objects_early): * symbols.c (init_symbols_once_early): * symsinit.h: Implement print-circle, printing circular structures in a readable fashion, and treating them appropriately on read. This is by means of two new object methods, print_preprocess (detecting circularities), and nsubst_structures_descend (replacing placeholders with the read objects). Expose the substitution to Lisp via #'nsubst and its new :descend-structures keyword. Store information as to whether symbols are interned in obarray or not in their header, making checking for keywords and uninterned symbols (and thus printing) cheaper. Default print_gensym to t, as Common Lisp does, and as a more-than-decade old comment suggests. lisp/ChangeLog addition: 2011-09-04 Aidan Kehoe <kehoea@parhasard.net> * bytecomp.el (byte-compile-output-file-form): * bytecomp.el (byte-compile-output-docform): Bind print-circle, print-continuous-numbering in these functions, now those variables are available. * lisp.el (forward-sexp): * lisp.el (backward-sexp): Recognise leading #N= as being part of an expression. tests/ChangeLog addition: 2011-09-04 Aidan Kehoe <kehoea@parhasard.net> * automated/lisp-reader-tests.el: * automated/lisp-tests.el (literal-with-uninterned): * automated/symbol-tests.el (foo): Test print-circle, for printing (mutually-)recursive and circular structures. Bind print-continuous-numbering where appropriate.
author Aidan Kehoe <kehoea@parhasard.net>
date Sun, 04 Sep 2011 19:51:35 +0100
parents 061f4f90f874
children
line wrap: on
line source

/*
 * b2m - a filter for Babyl -> Unix mail files
 * The copyright on this file has been disclaimed.
 *
 * usage:	b2m < babyl > mailbox
 *
 * I find this useful whenever I have to use a
 * system which - shock horror! - doesn't run
 * GNU Emacs. At least now I can read all my
 * GNU Emacs Babyl format mail files!
 *
 * it's not much but it's free!
 *
 *   Ed Wilkinson
 *   E.Wilkinson@massey.ac.nz
 *   Mon Nov 7 15:54:06 PDT 1988
 */

/* Made conformant to the GNU coding standards January, 1995
   by Francesco Potorti` <pot@cnuce.cnr.it>. */

#ifdef HAVE_CONFIG_H
#include <config.h>
/* On some systems, Emacs defines static as nothing for the sake
   of unexec.  We don't want that here since we don't use unexec. */
#undef static
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <getopt.h>
#ifdef MSDOS
#include <fcntl.h>
#endif

#undef TRUE
#define TRUE	1
#undef FALSE
#define FALSE	0

#define streq(s,t)	(strcmp (s, t) == 0)
#define strneq(s,t,n)	(strncmp (s, t, n) == 0)

typedef int logical;

#define TM_YEAR_BASE 1900

/* Nonzero if TM_YEAR is a struct tm's tm_year value that causes
   asctime to have well-defined behavior.  */
#ifndef TM_YEAR_IN_ASCTIME_RANGE
# define TM_YEAR_IN_ASCTIME_RANGE(tm_year) \
    (1000 - TM_YEAR_BASE <= (tm_year) && (tm_year) <= 9999 - TM_YEAR_BASE)
#endif

/*
 * A `struct linebuffer' is a structure which holds a line of text.
 * `readline' reads a line from a stream into a linebuffer and works
 * regardless of the length of the line.
 */
struct linebuffer
{
  long size;
  char *buffer;
};

long *xmalloc (unsigned int), *xrealloc (char *, unsigned int);
char *concat (char *, char *, char *);
long readline (struct linebuffer *, register FILE *);
void fatal (char *);

/*
 * xnew -- allocate storage.  SYNOPSIS: Type *xnew (int n, Type);
 */
#define xnew(n, Type)	((Type *) xmalloc ((n) * sizeof (Type)))



char *progname;

struct option longopts[] =
{
  { "help",			no_argument,	   NULL,     'h'   },
  { "version",			no_argument,	   NULL,     'V'   },
  { 0 }
};

extern int optind;

int
main (int argc, char **argv)
{
  logical labels_saved, printing, header, first, last_was_blank_line;
  time_t ltoday;
  struct tm *tm;
  char *labels, *p, *today;
  struct linebuffer data;

#ifdef MSDOS
  _fmode = O_BINARY;		/* all of files are treated as binary files */
#if __DJGPP__ > 1
  if (!isatty (fileno (stdout)))
    setmode (fileno (stdout), O_BINARY);
  if (!isatty (fileno (stdin)))
    setmode (fileno (stdin), O_BINARY);
#else /* not __DJGPP__ > 1 */
  (stdout)->_flag &= ~_IOTEXT;
  (stdin)->_flag &= ~_IOTEXT;
#endif /* not __DJGPP__ > 1 */
#endif
  progname = argv[0];

  while (1)
    {
      int opt = getopt_long (argc, argv, "hV", longopts, 0);
      if (opt == EOF)
	break;

      switch (opt)
	{
	case 'V':
	  printf ("%s (XEmacs %s)\n", "b2m", EMACS_VERSION);
	  puts ("b2m is in the public domain.");
	  exit (EXIT_SUCCESS);

	case 'h':
	  fprintf (stderr, "Usage: %s <babylmailbox >unixmailbox\n", progname);
	  exit (EXIT_SUCCESS);
	}
    }

  if (optind != argc)
    {
      fprintf (stderr, "Usage: %s <babylmailbox >unixmailbox\n", progname);
      exit (EXIT_SUCCESS);
    }

  labels_saved = printing = header = last_was_blank_line = FALSE;
  first = TRUE;
  ltoday = time (0);
  /* Convert to a string, checking for out-of-range time stamps.
     Don't use 'ctime', as that might dump core if the hardware clock
     is set to a bizarre value.  */
  tm = localtime (&ltoday);
  if (! (tm && TM_YEAR_IN_ASCTIME_RANGE (tm->tm_year)
	 && (today = asctime (tm))))
    fatal ("current time is out of range");
  data.size = 200;
  data.buffer = xnew (200, char);

  if (readline (&data, stdin) == 0
      || !strneq (data.buffer, "BABYL OPTIONS:", 14))
    fatal ("standard input is not a Babyl mailfile.");

  while (readline (&data, stdin) > 0)
    {
      if (streq (data.buffer, "*** EOOH ***") && !printing)
	{
	  printing = header = TRUE;
	  printf ("From \"Babyl to mail by %s\" %s", progname, today);
	  continue;
	}

      if (data.buffer[0] == '\037')
	{
	  if (data.buffer[1] == '\0')
	    continue;
	  else if (data.buffer[1] == '\f')
	    {
	      if (first)
		first = FALSE;
	      else if (! last_was_blank_line)
		puts("");
	      /* Save labels. */
	      readline (&data, stdin);
	      p = strtok (data.buffer, " ,\r\n\t");
	      labels = "X-Babyl-Labels: ";

	      while ((p = strtok (NULL, " ,\r\n\t")))
		labels = concat (labels, p, ", ");

	      p = &labels[strlen (labels) - 2];
	      if (*p == ',')
		*p = '\0';
	      printing = header = FALSE;
	      labels_saved = TRUE;
	      continue;
	    }
	}

      if ((data.buffer[0] == '\0') && header)
	{
	  header = FALSE;
	  if (labels_saved)
	    puts (labels);
	}

      if (printing)
	{
	  puts (data.buffer);
	  if (data.buffer[0] == '\0')
	    last_was_blank_line = TRUE;
	  else
	    last_was_blank_line = FALSE;
	}
    }

  return EXIT_SUCCESS;
}



/*
 * Return a newly-allocated string whose contents
 * concatenate those of s1, s2, s3.
 */
char *
concat (char *s1, char *s2, char *s3)
{
  int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
  char *result = xnew (len1 + len2 + len3 + 1, char);

  strcpy (result, s1);
  strcpy (result + len1, s2);
  strcpy (result + len1 + len2, s3);
  result[len1 + len2 + len3] = '\0';

  return result;
}

/*
 * Read a line of text from `stream' into `linebuffer'.
 * Return the number of characters read from `stream',
 * which is the length of the line including the newline, if any.
 */
long
readline (struct linebuffer *linebuffer, register FILE *stream)

{
  char *buffer = linebuffer->buffer;
  register char *p = linebuffer->buffer;
  register char *pend;
  int chars_deleted;

  pend = p + linebuffer->size;	/* Separate to avoid 386/IX compiler bug.  */

  while (1)
    {
      register int c = getc (stream);
      if (p == pend)
	{
	  linebuffer->size *= 2;
	  buffer = (char *) xrealloc (buffer, linebuffer->size);
	  p += buffer - linebuffer->buffer;
	  pend = buffer + linebuffer->size;
	  linebuffer->buffer = buffer;
	}
      if (c == EOF)
	{
	  *p = '\0';
	  chars_deleted = 0;
	  break;
	}
      if (c == '\n')
	{
	  if (p > buffer && p[-1] == '\r')
	    {
	      *--p = '\0';
	      chars_deleted = 2;
	    }
	  else
	    {
	      *p = '\0';
	      chars_deleted = 1;
	    }
	  break;
	}
      *p++ = c;
    }

  return (p - buffer + chars_deleted);
}

/*
 * Like malloc but get fatal error if memory is exhausted.
 */
long *
xmalloc (unsigned int size)
{
  long *result = (long *) malloc (size);
  if (result == NULL)
    fatal ("virtual memory exhausted");
  return result;
}

long *
xrealloc (char *ptr, unsigned int size)
{
  long *result = (long *) realloc (ptr, size);
  if (result == NULL)
    fatal ("virtual memory exhausted");
  return result;
}

void
fatal (char *message)
{
  fprintf (stderr, "%s: %s\n", progname, message);
  exit (EXIT_FAILURE);
}

/* b2m.c ends here */