#!/usr/bin/perl

require 'c_types.pl';

$[ = 1;			# set array base to 1
$, = ' ';		# set output field separator
$\ = "\n";		# set output record separator

$always_use_temp_storage = 1;

sub toupper {
    local($_) = @_;
    tr/a-z/A-Z/;
    return $_;
}

sub tolower {
    local($_) = @_;
    tr/A-Z/a-z/;
    return $_;
}

print '/* marshal.c */';
print "/* This is a generated file.  Please modify `marshal.pl' */";
print '';

print '#include <backend-server.h>';
print '';
print '#include <glibtop/sysdeps.h>';
print '#include <glibtop/union.h>';
print '';
print '#include "command.h"';
print '#include <glibtop/call-vector.h>';
print '';

$feature_count = 0;

while (<>) {
    chop;	# strip record separator

    if (/^[^#]/) {
	&output($_);
    }
}

sub output {
    local($line) = @_;
    @line_fields = split(/\|/, $line, 9999);
    $retval = $line_fields[1];
    $feature = $line_fields[2];
    $param_def = $line_fields[4];

    $orig = $feature;
    $feature =~ s/^@//;
    $space = $feature;
    $space =~ s/./ /g;

    $features{++$feature_count} = $orig;
    return if $orig =~ /^@/;

    $orig_retval = $retval;
    if ($retval eq 'retval') {
      $retval_param = '&retval';
      $call_prefix = '';
      $call_prefix_space = '';
      $prefix = 'retval = ';
      $prefix_space = '         ';
      $retval = 'int';
    }
    elsif ($retval !~ /^void$/) {
      $retval_param = 'NULL';
      $prefix = 'retval = ';
      $prefix_space = '         ';
      $call_prefix = 'retval = ';
      $call_prefix_space = '         ';
    }
    else {
      $retval_param = 'NULL';
      $call_prefix = '';
      $call_prefix_space = '';
      $prefix = '';
      $prefix_space = '';
    }

    if ($retval =~ /^(array|pointer)\((.*)\)$/) {
      $retval = ($2 eq 'string') ? 'char **' : "$2 *";
    }

    $need_temp_storage = $always_use_temp_storage;
    $local_var_init_code = "\t/* variable initialization */\n";
    $local_var_init_code .= "\t_LIBGTOP_SEND_len = 0;\n";
    $local_var_init_code .= "\t_LIBGTOP_DATA_len = 0;\n";
    $local_var_init_code .= "\t_LIBGTOP_SEND_offset = 0;\n";
    $local_var_init_code  .= "\t_LIBGTOP_SEND_ptr = ".
	  "(char *) _LIBGTOP_SEND_buf;\n";
    $marshal_code = "\t/* marshal start */\n";
    $marshal_code .= "\tmemset (_LIBGTOP_SEND_buf, 0, _LIBGTOP_SEND_len);\n";
    $first_param_name = '';
    $size_code = '';

    $call_param = '';
    $param_decl = '';
    $need_temp_len = 0;
    $nr_params = (@params = split(/:/, $param_def, 9999));
    for ($param = 1; $param <= $nr_params; $param++) {
      $list = $params[$param];
      $type = $params[$param];
      $type =~ s/\(.*//;
      $list =~ s/^.*\(//;
      $list =~ s/\)$//;
      $count = (@fields = split(/,/, $list, 9999));
      for ($field = 1; $field <= $count; $field++) {
	my $c_type = $typeinfo->{$type}->[1];

	if ($first_param_name eq '') {
	  $first_param_name = $fields[$field];
	}

	if ($typeinfo->{$type}->[2]) {
	  $need_temp_storage = 1;
	}

	if ($param_decl eq '') {
	  $param_decl = ",\n            " . $space . '    ';
	}
	else {
	  $param_decl = $param_decl . ', ';
	}
	$param_decl = $param_decl . $c_type . ' ' . $fields[$field];
	$call_param = $call_param . ', ' . $fields[$field];

	$size_code .= "\t_LIBGTOP_SEND_len += ";
	if ($typeinfo->{$type}->[2]) {
	  $size_code .= sprintf ("sizeof (size_t)");
	} else {
	  $size_code .= sprintf ("sizeof (%s)", $c_type);
	}
	$size_code .= ";\n";

	if (defined $sizeof_funcs->{$type}) {
	  $size_code .= "\t_LIBGTOP_DATA_len += ";
	  $size_code .= $sizeof_funcs->{$type}->($fields[$field]);
	  $size_code .= ";\n";
	}

	$marshal_code .= "\t_LIBGTOP_SEND_ptr = ".
	  "(char *) _LIBGTOP_SEND_buf + _LIBGTOP_SEND_offset;\n";

	if (defined $marshal_funcs->{$type}) {
	  $marshal_code .= $marshal_funcs->{$type}->($c_type, $fields[$field], "\t");
	} else {
	  $marshal_code .= sprintf ("\tmemcpy (_LIBGTOP_SEND_ptr, %s, %s);\n",
				    '&'.$fields[$field], "sizeof ($c_type)");
	}

	$marshal_code .= "\t_LIBGTOP_SEND_offset += ";
	if ($typeinfo->{$type}->[2]) {
	  $marshal_code .= sprintf ("sizeof (size_t)");
	} else {
	  $marshal_code .= sprintf ("sizeof (%s)", $c_type);
	}
	$marshal_code .= ";\n";
      }
    }

    $local_var_code = "";
    $local_var_code .= "\tunsigned _LIBGTOP_SEND_offset, _LIBGTOP_SEND_len;\n";
    $local_var_code .= "\tunsigned _LIBGTOP_DATA_len;\n";
    if ($need_temp_len) {
      $local_var_code .= "\tunsigned _LIBGTOP_SEND_temp_len;\n";
    }
    $local_var_code .= "\tvoid *_LIBGTOP_SEND_buf, *_LIBGTOP_DATA_buf;\n";
    $local_var_code .= "\tchar *_LIBGTOP_SEND_ptr, *_LIBGTOP_DATA_ptr;\n";
    if ($retval !~ /^void$/) {
      $local_var_code .= sprintf ("\t%s retval = (%s) 0;\n",
				  $retval, $retval);
    }

    $total_code = '';

    $send_buf_code = "\t/* send buffer */\n";
    $send_buf_code .= "\t_LIBGTOP_SEND_buf = ";
    if ($need_temp_storage) {
      $send_buf_code .= "g_malloc (_LIBGTOP_SEND_len+1)";
    } else {
      $send_buf_code .= '(void *) &'.$first_param_name;
    }
    $send_buf_code .= ";\n\n";

    $send_buf_code .= "\t/* data buffer */\n";
    $send_buf_code .= "\t_LIBGTOP_DATA_buf = ";
    if ($need_temp_storage) {
      $send_buf_code .= "g_malloc (_LIBGTOP_DATA_len+1)";
    } else {
      $send_buf_code .= 'NULL';
    }
    $send_buf_code .= ";\n";
    $send_buf_code .= "\t_LIBGTOP_DATA_ptr = _LIBGTOP_DATA_buf;\n";

    $call_code = '';
    $call_code .= sprintf ("\t%sglibtop_call_i (server, closure, GLIBTOP_CMND_%s,\n",
			   $call_prefix, &toupper($feature));
    $call_code .= sprintf ("\t\t\t%s%s, %s,\n", $call_prefix_space,
			   "_LIBGTOP_SEND_len", "_LIBGTOP_SEND_buf");
    $call_code .= sprintf ("\t\t\t%s%s, %s,\n", $call_prefix_space,
			   "_LIBGTOP_DATA_len", "_LIBGTOP_DATA_buf");
    if ($line_fields[3] eq '') {
      $call_code .= sprintf ("\t\t\t%s0, NULL,\n", $call_prefix_space);
    } elsif ($line_fields[3] eq  'array') {
      $call_code .= sprintf ("\t\t\t%ssizeof (glibtop_array), array,\n",
			     $call_prefix_space);
    } else {
      $call_code .= sprintf ("\t\t\t%ssizeof (glibtop_%s), buf,\n",
			     $call_prefix_space, $feature);
    }
    $call_code .= sprintf ("\t\t\t%s%s);\n", $call_prefix_space, $retval_param);

    if ($need_temp_storage) {
      $send_buf_free_code = "\tg_free (_LIBGTOP_SEND_buf);\n";
      $send_buf_free_code .= "\tg_free (_LIBGTOP_DATA_buf);\n";
    } else {
      $send_buf_free_code = "";
    }

    if (!($size_code eq '')) {
      $size_code = sprintf (qq[\t/* send size */\n%s\n], $size_code);
    }

    if ($orig_retval eq 'pointer(string)') {
      $call_code .= "\n";
      $local_var_code .= "\tchar *_LIBGTOP_ARRAY_ptr, **ptrlist;\n";
      $local_var_code .= "\toff_t *_LIBGTOP_ARRAY_off_ptr;\n";
      $local_var_code .= "\tint i;\n";
      $call_code .= "\tptrlist = g_malloc (array->number+1 * sizeof (char *));\n";
      $call_code .= "\t_LIBGTOP_ARRAY_off_ptr = (off_t *) retval;\n";
      $call_code .= "\t_LIBGTOP_ARRAY_ptr = (char *) retval;\n";
      $call_code .= "\t_LIBGTOP_ARRAY_off_ptr++;\n\n";
      $call_code .= "\tfor (i = 0; i < array->number; i++)\n";
      $call_code .= "\t\tptrlist [i] = g_strdup (_LIBGTOP_ARRAY_ptr + *_LIBGTOP_ARRAY_off_ptr++);\n";
      $call_code .= "\tptrlist [array->number] = NULL;\n\n";
      $call_code .= "\tg_free (retval);\n";
      $call_code .= "\tretval = ptrlist;\n";
    }

    $total_code .= sprintf ("%s%s\n%s\n%s\n%s\n",
			    $size_code, $send_buf_code, $marshal_code,
			    $call_code, $send_buf_free_code);

    $return_code = '';
    if ($retval !~ /^void$/) {
      $return_code .= "\treturn retval;\n";
    } else {
      $return_code .= "\treturn;\n";
    }

    $total_code .= $return_code;

    $func_decl = 'static '.$retval."\n";
    if ($line_fields[3] eq '') {
      $func_decl .= sprintf ("_glibtop_get_%s_c (glibtop_server *server, void *closure%s)\n",
			     $feature, $param_decl);
    } elsif ($line_fields[3] eq 'array') {
      $func_decl .= sprintf ("_glibtop_get_%s_c (glibtop_server *server, void *closure, glibtop_array *array%s)\n",
			     $feature, $param_decl);
    } elsif ($line_fields[3] =~ /^array/) {
      $func_decl .= sprintf ("_glibtop_get_%s_c (glibtop_server *server, void *closure, glibtop_array *array, %s *buf%s)\n",
			     $feature, 'glibtop_'.$feature, $param_decl);
    } else {
      $func_decl .= sprintf ("_glibtop_get_%s_c (glibtop_server *server, void *closure, %s *buf%s)\n",
			     $feature, 'glibtop_'.$feature, $param_decl);
    }

    $total_code = sprintf ("%s{\n%s\n%s\n%s\n%s}\n", $func_decl,
			   $local_var_code, $local_var_init_code,
			   $total_code);


    $total_code = sprintf ("#if GLIBTOP_SUID_%s\n\n%s\n#endif /* GLIBTOP_SUID_%s */\n\n",
			   &toupper($feature), $total_code,
			   &toupper($feature));

    print $total_code;
}

$call_vector_code = '';
for ($nr = 1; $nr <= $feature_count; $nr++) {
  $feature = $features{$nr};

  $orig = $feature;
  $feature =~ s/^@//;

  if ($orig =~ /^@/) {
    $call_vector_code .= sprintf (qq[\tNULL,\n]);
  } else {
    $call_vector_code .= sprintf
      (qq[\#if GLIBTOP_SUID_%s\n\t_glibtop_get_%s_c,\n\#else\n\tNULL,\n\#endif\n],
       &toupper($feature), $feature);
  }
}

print 'glibtop_call_vector glibtop_backend_server_call_vector = {';
print $call_vector_code;
print '};';
print '';

