text: #!/usr/bin/env python
  
  import os, re, sys, textwrap
  from dist import compare_srcfile
  import log_data
  
  # Temporary file.
  tmp_file = '__tmp'
  
  # Map log record types to:
  # (C type, pack type, printf format, printf arg(s), printf setup)
  field_types = {
      'string' : ('const char *', 'S', '%s', 'arg', ''),
      'item' : ('WT_ITEM *', 'u', '%s', 'escaped',
          'WT_RET(__logrec_jsonify_str(session, &escaped, &arg));'),
      'recno' : ('uint64_t', 'r', '%" PRIu64 "', 'arg', ''),
      'uint32' : ('uint32_t', 'I', '%" PRIu32 "', 'arg', ''),
      'uint64' : ('uint64_t', 'Q', '%" PRIu64 "', 'arg', ''),
  }
  
  def cintype(f):
      return field_types[f[0]][0]
  
  def couttype(f):
      type = cintype(f)
      # We already have a pointer to a WT_ITEM
      if f[0] == 'item':
          return type
      if type[-1] != '*':
          type += ' '
      return type + '*'
  
  def clocaltype(f):
      type = cintype(f)
      # Allocate a WT_ITEM struct on the stack
      if f[0] == 'item':
          return type[:-2]
      return type
  
  def escape_decl(fields):
      for f in fields:
          if 'escaped' in field_types[f[0]][4]:
              return '\n\tchar *escaped;'
      return ''
  
  def has_escape(fields):
      for f in fields:
          if 'escaped' in field_types[f[0]][4]:
              return True
      return False
  
  def pack_fmt(fields):
      return ''.join(field_types[f[0]][1] for f in fields)
  
  def op_pack_fmt(r):
      return 'II' + pack_fmt(r.fields)
  
  def rec_pack_fmt(r):
      return 'I' + pack_fmt(r.fields)
  
  def printf_fmt(f):
      return field_types[f[0]][2]
  
  def printf_arg(f):
      arg = field_types[f[0]][3].replace('arg', f[1])
      return ' ' + arg
  
  def printf_setup(f):
      stmt = field_types[f[0]][4].replace('arg', f[1])
      return '' if stmt == '' else stmt + '\n\t'
  
  
  #####################################################################
  # Update log.h with #defines for types
  #####################################################################
  log_defines = (
      ''.join('/*! %s */\n#define\t%s\t%d\n' % (r.desc, r.macro_name(), i)
          for i, r in enumerate(log_data.rectypes)) +
      ''.join('/*! %s */\n#define\t%s\t%d\n' % (r.desc, r.macro_name(), i)
          for i, r in enumerate(log_data.optypes,start=1))
  )
  
  tfile = open(tmp_file, 'w')
  skip = 0
  for line in open('../src/include/wiredtiger.in', 'r'):
      if skip:
          if 'Log record declarations: END' in line:
              tfile.write('/*\n' + line)
              skip = 0
      else:
          tfile.write(line)
      if 'Log record declarations: BEGIN' in line:
          skip = 1
          tfile.write(' */\n')
          tfile.write('/*! invalid operation */\n')
          tfile.write('#define\tWT_LOGOP_INVALID\t0\n')
          tfile.write(log_defines)
  tfile.close()
  compare_srcfile(tmp_file, '../src/include/wiredtiger.in')
  
  #####################################################################
  # Create log_auto.c with handlers for each record / operation type.
  #####################################################################
  f='../src/log/log_auto.c'
  tfile = open(tmp_file, 'w')
  
  tfile.write('/* DO NOT EDIT: automatically built by dist/log.py. */\n')
  
  tfile.write('''
  #include "wt_internal.h"
  
  int
  __wt_logrec_alloc(WT_SESSION_IMPL *session, size_t size, WT_ITEM **logrecp)
  {
  \tWT_ITEM *logrec;
  
  \tWT_RET(__wt_scr_alloc(session, WT_ALIGN(size + 1, LOG_ALIGN), &logrec));
  \tWT_CLEAR(*(WT_LOG_RECORD *)logrec->data);
  \tlogrec->size = offsetof(WT_LOG_RECORD, record);
  
  \t*logrecp = logrec;
  \treturn (0);
  }
  
  void
  __wt_logrec_free(WT_SESSION_IMPL *session, WT_ITEM **logrecp)
  {
  \t__wt_scr_free(session, logrecp);
  }
  
  int
  __wt_logrec_read(WT_SESSION_IMPL *session,
      const uint8_t **pp, const uint8_t *end, uint32_t *rectypep)
  {
  \tuint64_t rectype;
  
  \tWT_UNUSED(session);
  \tWT_RET(__wt_vunpack_uint(pp, WT_PTRDIFF(end, *pp), &rectype));
  \t*rectypep = (uint32_t)rectype;
  \treturn (0);
  }
  
  int
  __wt_logop_read(WT_SESSION_IMPL *session,
      const uint8_t **pp, const uint8_t *end,
      uint32_t *optypep, uint32_t *opsizep)
  {
  \treturn (__wt_struct_unpack(
  \t    session, *pp, WT_PTRDIFF(end, *pp), "II", optypep, opsizep));
  }
  
  static size_t
  __logrec_json_unpack_str(char *dest, size_t destlen, const char *src,
      size_t srclen)
  {
  \tsize_t total;
  \tsize_t n;
  
  \ttotal = 0;
  \twhile (srclen > 0) {
  \t\tn = __wt_json_unpack_char(*src++, (u_char *)dest, destlen, 0);
  \t\tsrclen--;
  \t\tif (n > destlen)
  \t\t\tdestlen = 0;
  \t\telse {
  \t\t\tdestlen -= n;
  \t\t\tdest += n;
  \t\t}
  \t\ttotal += n;
  \t}
  \tif (destlen > 0)
  \t\t*dest = '\\0';
  \treturn (total + 1);
  }
  
  static int
  __logrec_jsonify_str(WT_SESSION_IMPL *session, char **destp, WT_ITEM *item)
  {
  \tsize_t needed;
  
  \tneeded = __logrec_json_unpack_str(NULL, 0, item->data, item->size);
  \tWT_RET(__wt_realloc(session, NULL, needed, destp));
  \t(void)__logrec_json_unpack_str(*destp, needed, item->data, item->size);
  \treturn (0);
  }
  ''')
  
  # Emit code to read, write and print log operations (within a log record)
  for optype in log_data.optypes:
      if not optype.fields:
          continue
  
      tfile.write('''
  int
  __wt_logop_%(name)s_pack(
      WT_SESSION_IMPL *session, WT_ITEM *logrec,
      %(arg_decls)s)
  {
  \tconst char *fmt = WT_UNCHECKED_STRING(%(fmt)s);
  \tsize_t size;
  \tuint32_t optype, recsize;
  
  \toptype = %(macro)s;
  \tWT_RET(__wt_struct_size(session, &size, fmt,
  \t    optype, 0%(arg_names)s));
  
  \t__wt_struct_size_adjust(session, &size);
  \tWT_RET(__wt_buf_extend(session, logrec, logrec->size + size));
  \trecsize = (uint32_t)size;
  \tWT_RET(__wt_struct_pack(session,
  \t    (uint8_t *)logrec->data + logrec->size, size, fmt,
  \t    optype, recsize%(arg_names)s));
  
  \tlogrec->size += (uint32_t)size;
  \treturn (0);
  }
  ''' % {
      'name' : optype.name,
      'macro' : optype.macro_name(),
      'arg_decls' : ', '.join(
          '%s%s%s' % (cintype(f), '' if cintype(f)[-1] == '*' else ' ', f[1])
          for f in optype.fields),
      'arg_names' : ''.join(', %s' % f[1] for f in optype.fields),
      'fmt' : op_pack_fmt(optype)
  })
  
      tfile.write('''
  int
  __wt_logop_%(name)s_unpack(
      WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end,
      %(arg_decls)s)
  {
  \tconst char *fmt = WT_UNCHECKED_STRING(%(fmt)s);
  \tuint32_t optype, size;
  
  \tWT_RET(__wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
  \t    &optype, &size%(arg_names)s));
  \tWT_ASSERT(session, optype == %(macro)s);
  
  \t*pp += size;
  \treturn (0);
  }
  ''' % {
      'name' : optype.name,
      'macro' : optype.macro_name(),
      'arg_decls' : ', '.join(
          '%s%sp' % (couttype(f), f[1]) for f in optype.fields),
      'arg_names' : ''.join(', %sp' % f[1] for f in optype.fields),
      'fmt' : op_pack_fmt(optype)
  })
  
      last_field = optype.fields[-1]
      tfile.write('''
  int
  __wt_logop_%(name)s_print(
      WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end, FILE *out)
  {
  \t%(arg_decls)s
  
  \t%(arg_init)sWT_RET(__wt_logop_%(name)s_unpack(
  \t    session, pp, end%(arg_addrs)s));
  
  \tfprintf(out, " \\"optype\\": \\"%(name)s\\",\\n");
  \t%(print_args)s
  \t%(arg_fini)sreturn (0);
  }
  ''' % {
      'name' : optype.name,
      'arg_decls' : ('\n\t'.join('%s%s%s;' %
          (clocaltype(f), '' if clocaltype(f)[-1] == '*' else ' ', f[1])
          for f in optype.fields)) + escape_decl(optype.fields),
      'arg_init' : ('escaped = NULL;\n\t' if has_escape(optype.fields) else ''),
      'arg_fini' : ('__wt_free(session, escaped);\n\t'
      if has_escape(optype.fields) else ''),
      'arg_addrs' : ''.join(', &%s' % f[1] for f in optype.fields),
      'print_args' : '\n\t'.join(
          '%sfprintf(out, "        \\"%s\\": \\"%s\\",\\n",%s);' %
          (printf_setup(f), f[1], printf_fmt(f), printf_arg(f))
          for f in optype.fields[:-1]) + str(
          '\n\t%sfprintf(out, "        \\"%s\\": \\"%s\\"",%s);' %
          (printf_setup(last_field), last_field[1],
              printf_fmt(last_field), printf_arg(last_field))),
  })
  
  # Emit the printlog entry point
  tfile.write('''
  int
  __wt_txn_op_printlog(
      WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end, FILE *out)
  {
  \tuint32_t optype, opsize;
  
  \t/* Peek at the size and the type. */
  \tWT_RET(__wt_logop_read(session, pp, end, &optype, &opsize));
  \tend = *pp + opsize;
  
  \tswitch (optype) {''')
  
  for optype in log_data.optypes:
      if not optype.fields:
          continue
  
      tfile.write('''
  \tcase %(macro)s:
  \t\tWT_RET(%(print_func)s(session, pp, end, out));
  \t\tbreak;
  ''' % {
      'macro' : optype.macro_name(),
      'print_func' : '__wt_logop_' + optype.name + '_print',
  })
  
  tfile.write('''
  \tWT_ILLEGAL_VALUE(session);
  \t}
  
  \treturn (0);
  }
  ''')
  
  tfile.close()
  compare_srcfile(tmp_file, f)
  