/*

  Copyright (C) 2000, 2001 Silicon Graphics, Inc.  All Rights Reserved.

   Path64 is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   Path64 is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
   License for more details.

   You should have received a copy of the GNU General Public License
   along with Path64; see the file COPYING.  If not, write to the Free
   Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.

   Special thanks goes to SGI for their continued support to open source

*/


/* ====================================================================
 * ====================================================================
 *
 * Module: dra_demangle.c
 *
 * Revision history:
 *  20-Aug-96: Original Version, nenad
 *
 * Description: Implementation of demangling utility functions
 *              needed in emitting user-understandable error
 *              messages by the (dsm_)prelinker, linker, and
 *              run-time error checking code generated by LNO.
 *
 * ====================================================================
 * ==================================================================== 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "dra_demangle.h"


/*
 *  Constants used in (de)mangling
 *
 *  NOTE: These #define's have their 'static const' counterparts
 *        in be/be/dra_internal.h
 *        Any change in this file should also be reflected there. 
 */

#define DRA_MANGLE_SIG      "__nn__"
#define DRA_MANGLE_SIG_LEN  6

#define DRA_STAR_CODE      'S'
#define DRA_BLOCK_CODE     'B'
#define DRA_CYCLIC_CODE    'C'
#define DRA_NDIMS_END      'D'
#define DRA_ESIZE_END      'E'
#define DRA_ARG_SEPARATOR  '_'

#define DRA_STAR_STRING    "*"
#define DRA_BLOCK_STRING   "BLOCK"
#define DRA_CYCLIC_STRING  "CYCLIC"

#define DRA_DEM_BUFSIZE 4096

static char dem_buf[DRA_DEM_BUFSIZE];



/* 
 * Extract only the function name
 */

char* 
DRA_Demangle_Func(const char *mangled_name)
{
  char *func_name;
  char *postfix_sig;
  int   func_name_len;

  /* Check for prefix signature */
  if (mangled_name == NULL ||
      strncmp(mangled_name, DRA_MANGLE_SIG, DRA_MANGLE_SIG_LEN) != 0)
    return NULL;

  func_name = (char*) mangled_name + DRA_MANGLE_SIG_LEN;

  /* Check for postfix signature */
  postfix_sig = strstr(func_name, DRA_MANGLE_SIG);
  
  if (postfix_sig == NULL || 
      postfix_sig[DRA_MANGLE_SIG_LEN] == 0)
    return NULL;

  /* Check that the name is not too long */
  func_name_len = postfix_sig - func_name;
  if (func_name_len >= DRA_DEM_BUFSIZE)
    return NULL;

  /* Copy original function name into static buffer */
  (void) strncpy(dem_buf, func_name, func_name_len);
  dem_buf[func_name_len] = 0;
  
  return dem_buf;
}



/* 
 * Extract and demangle the argument list
 */

char* 
DRA_Demangle_Arglist(const char *mangled_name,
                     const char dim_order)
{
  char *func_name;
  char *postfix_sig;
  char *arg_list;
  char *arg_begin;
  char *arg_end;
  char *arg_dim;
  char *arg_distr;
  char *buf;

  int arg_pos;
  int num_dims;
  int dim;
  int chunk;
  
  /* Dimension ordering must be either 'F' or 'C' */
  if (dim_order != DRA_DIMS_COLUMNWISE && dim_order != DRA_DIMS_ROWWISE)     
    return NULL;
  
  /* Check for prefix signature */
  if (mangled_name == NULL ||
      strncmp(mangled_name, DRA_MANGLE_SIG, DRA_MANGLE_SIG_LEN) != 0)
    return NULL;

  func_name = (char*) mangled_name + DRA_MANGLE_SIG_LEN;

  /* Check for postfix signature */
  postfix_sig = strstr(func_name, DRA_MANGLE_SIG);
  if (postfix_sig == NULL)
    return NULL;

  /* Check that argument list is not empty */
  arg_list = postfix_sig + DRA_MANGLE_SIG_LEN;
  if (arg_list[0] == 0 || arg_list[1] == 0)
    return NULL;
  
  buf = dem_buf;
  *buf++ = '('; 


  for (arg_pos = 0, arg_begin = arg_list; *arg_begin != 0; arg_pos++) {

    /* Underscore must follow each argument */
    arg_end = strchr(arg_begin, DRA_ARG_SEPARATOR);
    if (arg_end == NULL || arg_end == arg_begin) 
      return NULL;

    /* Insert comma before each argument, except for the first one */
    if (arg_pos > 0) 
      *buf++ = ',';

    /* Extract the number of dimensions */
    num_dims = (int) strtol(arg_begin, &arg_begin, 10);

    if (num_dims > 0) {

      /* 'D' must follow the number of dimensions */
      if (*arg_begin++ != DRA_NDIMS_END) 
        return NULL;

      *buf++ = '(';

      /* Skip element size */
      (void) strtol(arg_begin, &arg_begin, 10);
      
      /* 'E' must follow the element size */
      if (*arg_begin++ != DRA_ESIZE_END) 
        return NULL;

      /* For columnwise case (Fortran) decode dimensions in reverse order */
      if (dim_order == DRA_DIMS_COLUMNWISE)
        arg_dim = arg_end;
      else 
        arg_dim = arg_begin;

      /* Go through all dimensions of the reshaped argument */
      for (dim = 0; dim < num_dims; dim++) {
        
        if (dim_order == DRA_DIMS_COLUMNWISE) {
          do {
            arg_dim--;
          } while (*arg_dim != DRA_STAR_CODE    &&
                   *arg_dim != DRA_BLOCK_CODE   &&
                   *arg_dim != DRA_CYCLIC_CODE  &&
                   *arg_dim != DRA_ESIZE_END);
        }

        arg_distr = arg_dim;

        switch(*arg_distr++) {

          case DRA_STAR_CODE:
            if (buf-dem_buf+strlen(DRA_STAR_STRING)+2 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_STAR_STRING);
            break;

          case DRA_BLOCK_CODE:
            if (buf-dem_buf+strlen(DRA_BLOCK_STRING)+2 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_BLOCK_STRING);
            break;

          case DRA_CYCLIC_CODE:
            if (buf-dem_buf+strlen(DRA_CYCLIC_STRING)+25 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_CYCLIC_STRING);
            chunk = (int) strtol(arg_distr, &arg_distr, 10);
            if (chunk != 0) 
              buf += sprintf(buf, "(%d)", chunk);
            else 
              buf += sprintf(buf, "(sym)");
            break;

          default:
            return NULL;
        }

        if (dim == num_dims-1) {
          if (dim_order == DRA_DIMS_ROWWISE && arg_distr != arg_end)
            return NULL;
          if (dim_order == DRA_DIMS_COLUMNWISE && arg_dim != arg_begin)
            return NULL;
          *buf++ = ')';
        }
        else 
          *buf++ = ',';
      }
    }

    /* Non reshaped argument (num_dims == 0) */    
    else { 
      if (buf-dem_buf+2 >= DRA_DEM_BUFSIZE)
        return NULL;
      *buf++ = '-';

      /* Underscore must follow each argument */
      if (*arg_begin != DRA_ARG_SEPARATOR)
        return NULL;
    }

    arg_begin = arg_end+1;
  } 
      
  *buf++ = ')';
  *buf = 0;

  return dem_buf;
}



/*
 * Extract and demangle both the function name and the argument list
 */

char* 
DRA_Demangle(const char *mangled_name,
             const char dim_order)
{
  char *func_name;
  char *postfix_sig;
  char *arg_list;
  char *arg_begin;
  char *arg_end;
  char *arg_dim;
  char *arg_distr;
  char *buf;

  int func_name_len;
  int arg_pos;
  int num_dims;
  int dim;
  int chunk;


  /* Dimension ordering must be either 'F' or 'C' */
  if (dim_order != DRA_DIMS_COLUMNWISE && dim_order != DRA_DIMS_ROWWISE)     
    return NULL;
  
  /* Check for prefix signature */
  if (mangled_name == NULL ||
      strncmp(mangled_name, DRA_MANGLE_SIG, DRA_MANGLE_SIG_LEN) != 0)
    return NULL;

  func_name = (char*) mangled_name + DRA_MANGLE_SIG_LEN;

  /* Check for postfix signature */
  postfix_sig = strstr(func_name, DRA_MANGLE_SIG);
  
  if (postfix_sig == NULL)
    return NULL;

  /* Check that the name is not too long */
  func_name_len = postfix_sig - func_name;
  if (func_name_len + 4 >= DRA_DEM_BUFSIZE)
    return NULL;

  /* Copy original function name into static buffer */
  (void) strncpy(dem_buf, func_name, func_name_len);
  
  /* Check that argument list is not empty */
  arg_list = postfix_sig + DRA_MANGLE_SIG_LEN;

  if (arg_list[0] == 0 || arg_list[1] == 0)
    return NULL;
  
  buf = dem_buf + (postfix_sig-func_name);
  *buf++ = '('; 


  for (arg_pos = 0, arg_begin = arg_list; *arg_begin != 0; arg_pos++) {

    /* Underscore must follow each argument */
    arg_end = strchr(arg_begin, DRA_ARG_SEPARATOR);
    if (arg_end == NULL || arg_end == arg_begin) 
      return NULL;

    /* Insert comma before each argument, except for the first one */
    if (arg_pos > 0) 
      *buf++ = ',';

    /* Extract the number of dimensions */
    num_dims = (int) strtol(arg_begin, &arg_begin, 10);

    if (num_dims > 0) {

      /* 'D' must follow the number of dimensions */
      if (*arg_begin++ != DRA_NDIMS_END) 
        return NULL;

      *buf++ = '(';

      /* Skip element size */
      (void) strtol(arg_begin, &arg_begin, 10);
      
      /* 'E' must follow the element size */
      if (*arg_begin++ != DRA_ESIZE_END) 
        return NULL;

      /* For columnwise case (Fortran) decode dimensions in reverse order */
      if (dim_order == DRA_DIMS_COLUMNWISE)
        arg_dim = arg_end;
      else 
        arg_dim = arg_begin;

      /* Go through all dimensions of the reshaped argument */
      for (dim = 0; dim < num_dims; dim++) {
        
        if (dim_order == DRA_DIMS_COLUMNWISE) {
          do {
            arg_dim--;
          } while (*arg_dim != DRA_STAR_CODE    &&
                   *arg_dim != DRA_BLOCK_CODE   &&
                   *arg_dim != DRA_CYCLIC_CODE  &&
                   *arg_dim != DRA_ESIZE_END);
        }

        arg_distr = arg_dim;

        switch(*arg_distr++) {

          case DRA_STAR_CODE:
            if (buf-dem_buf+strlen(DRA_STAR_STRING)+2 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_STAR_STRING);
            break;

          case DRA_BLOCK_CODE:
            if (buf-dem_buf+strlen(DRA_BLOCK_STRING)+2 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_BLOCK_STRING);
            break;

          case DRA_CYCLIC_CODE:
            if (buf-dem_buf+strlen(DRA_CYCLIC_STRING)+25 >= DRA_DEM_BUFSIZE)
              return NULL;
            buf += sprintf(buf, DRA_CYCLIC_STRING);
            chunk = (int) strtol(arg_distr, &arg_distr, 10);
            if (chunk != 0)
              buf += sprintf(buf, "(%d)", chunk);
            else 
              buf += sprintf(buf, "(sym)");
            break;

          default:
            return NULL;
        }

        if (dim == num_dims-1) {
          if (dim_order == DRA_DIMS_ROWWISE && arg_distr != arg_end)
            return NULL;
          if (dim_order == DRA_DIMS_COLUMNWISE && arg_dim != arg_begin)
            return NULL;
          *buf++ = ')';
        }
        else 
          *buf++ = ',';
      }
    }

    /* Non reshaped argument (num_dims == 0) */    
    else { 
      if (buf-dem_buf+2 >= DRA_DEM_BUFSIZE)
        return NULL;

      *buf++ = '-';
      /* Underscore must follow each argument */
      if (*arg_begin != DRA_ARG_SEPARATOR)
        return NULL;
    }

    arg_begin = arg_end+1;
  } 
      
  *buf++ = ')';
  *buf = 0;

  return dem_buf;
}



