/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $XConsortium: struct.c /main/4 1995/11/01 15:56:35 rswiston $ */
/*	Copyright (c) 1991, 1992 UNIX System Laboratories, Inc. */
/*	All Rights Reserved     */

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
/*	UNIX System Laboratories, Inc.			*/
/*	The copyright notice above does not evidence any       */
/*	actual or intended publication of such source code.    */

#undef printf


#include	"shell.h" 
#include <signal.h>
#include <fcntl.h>
#include <X11/X.h>
#include <X11/Intrinsic.h>
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>
#include <X11/StringDefs.h>
#include <Xm/XmStrDefs.h>
#include <Xm/List.h>
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <Xm/Xm.h>
#include <Xm/Protocols.h>
#include "hash.h"
#include "stdio.h"
#define NO_AST
#include "dtksh.h"
#undef NO_AST
#include "exksh.h"
#include "xmksh.h"
#include "dtkcmds.h"
#include "xmcvt.h"
#include "widget.h"
#include "extra.h"
#include "xmwidgets.h"
#include "struct.h"
#include "basetbl.h"
#include "docall.h"
#include "exksh_tbls.h"
#include "msgs.h"

const static char use[] = "0x%x";
const static char use2[] = "%s=0x%x";

static char structInited = 0;
static void *Hashnams = NULL;

static struct memtbl **Dynmem = NULL;
static int Ndynmem = 0;
static int Sdynmem = 0;

struct structlist {
	char *prefix;
	int id;
	int size;
	struct memtbl **mem;
};

struct structlist *Structlist = NULL;
int Nstructlist;



static int freemem( 
                        struct memtbl *mem) ;
static growmem( void ) ;
static char * endtok( 
                        char *start) ;
static int chg_structlist( 
                        struct memtbl **memptr,
                        int id) ;
static struct_init( void ) ;



memtbl_t *
ffind(
        memtbl_t *tbl,
        char *fld,
        char **pptr )
{
	static memtbl_t tbluse[2];
	memtbl_t *tbl2;
	char *p, *q, op;
	unsigned int len, sub;

	if (!fld || !(*fld))
		return(tbl);
	tbl2 = tbluse;
	tbluse[0] = *tbl;
	tbluse[1] = Null_tbl;
	q = fld;
	while (tbl2 && q && *q) {
		p = q;
		if (*q == '[') {
			if (!tbl2->ptr)
				return(NULL);
			q++;
			xk_par_int(&q, &sub, NULL);
			if (*q != ']')
				return(NULL);
			*pptr = ((char **) (*pptr))[0];
			*pptr += sub * tbl2->size;
			q++;
			tbluse[0].ptr--;
			continue;
		}
		if ((len = strcspn(p, "[.")) < strlen(p)) {
			q = p + len;
			op = *q;
			*q = '\0';
		}
		else
			q = NULL;
		tbl2 = asl_find(NULL, tbluse, p, pptr);
		if (tbl2 && (tbl2 != tbluse)) {
			/* A field should not be a subfield of itself */

			tbluse[0] = *tbl2;
			tbl2 = tbluse;
			tbl2->name = ".";
			tbl2->offset = 0;
		}
		if (q) {
			if (op == '.')
				*q++ = op;
			else
				*q = op;
		}
	}
	return(tbl2);
}

static int
freemem(
        struct memtbl *mem )
{
	free(mem->name);
	/*
	int i;

	** Because structures and typedefs now inherit fields (i.e. copy
	** the memtbl entry) we must keep the fields of a structure
	** around permanently, (unless we implement a reference count).
	** Let's keep the code handy in case we do.
		if (mem->kind == K_STRUCT) {
			struct memtbl *fmem;

			fmem = Dynmem[mem->tbl];
			for (i = 0; fmem[i].name; i++) {
				free(fmem[i].name);
				if (fmem[i].tname)
					free(fmem[i].tname);
			}
		}
	*/
	free(mem);
}

static
growmem( void )
{
	if (!(Dynmem = (struct memtbl **) realloc(Dynmem, (Sdynmem + 20) * sizeof(memtbl_t *))))
		return(SH_FAIL);
	chg_structlist(Dynmem, DYNMEM_ID);
	memset(((char *) Dynmem) + Sdynmem * sizeof(memtbl_t *), '\0', 20 * sizeof(memtbl_t *));
	Sdynmem += 20;
}

int
do_struct(
        int argc,
        char **argv )
{
	struct memtbl *mem, *fmem;
	int i, j, argstart, redo;
	char *name, *fname;
	char *p;

        if (!structInited)
           struct_init();

	if (argc > 1 && C_PAIR(argv[1], '-', 'R')) {
		redo = 0;
		argstart = 2;
	}
	else {
		argstart = 1;
		redo = 1;
	}
        if ((argstart + 1) >= argc)
        {
		XK_USAGE(argv[0]);
	}
	name = argv[argstart++];
	for (i = 0; i < Ndynmem; i++)
		if (!(Dynmem[i]->flags & F_FIELD) && (strcmp(name, Dynmem[i]->name) == 0))
			break;
	if ((i < Ndynmem) && !redo) {
		if (!redo)
			return(SH_SUCC);
		if (Sdynmem - Ndynmem < 1)
			growmem();
	}
	else if (Sdynmem - Ndynmem < 2)
		growmem();
	/*
	** Number of memtbls needed: two for structure table and one for
	** each field plus one for null termination.  The number of
	** fields is argc - 2.
	*/
	if (!(mem = (struct memtbl *) malloc(2 * sizeof(struct memtbl))))
		return(SH_FAIL);
	if (!(fmem = (struct memtbl *) malloc((argc - 1) * sizeof(struct memtbl))))
		return(SH_FAIL);
	memset(mem, '\0', 2 * sizeof(struct memtbl));
	memset(fmem, '\0', (argc - 1) * sizeof(struct memtbl));
	if (i < Ndynmem) {
		mem->tbl = Ndynmem++;
		freemem(Dynmem[i]);
		xkhash_override(Hashnams, name, mem);
	}
	else {
		Ndynmem += 2;
		mem->tbl = i + 1;
	}
	Dynmem[i] = mem;
	Dynmem[mem->tbl] = fmem;
	mem->flags = F_TBL_IS_PTR;
	mem->id = DYNMEM_ID;
	mem->name = strdup(name);
	mem->kind = K_STRUCT;
	for (j = argstart; (j < argc) && argv[j]; j++) {
		if (p = strchr(argv[j], ':')) {
			fname = malloc(p - argv[j] + 1);
			strncpy(fname, argv[j], p - argv[j]);
			fname[p - argv[j]] = '\0';
			parse_decl(argv[0], fmem + j - argstart, p + 1, 0);
		}
		else {
			fname = strdup(argv[j]);
			fmem[j - argstart] = T_unsigned_long[0];
		}
		fmem[j - argstart].name = fname;
		fmem[j - argstart].flags |= F_FIELD;
		fmem[j - argstart].delim = 0;
		fmem[j - argstart].offset = mem->size;
		mem->size += (fmem[j - argstart].ptr) ? sizeof(void *) : fmem[j - argstart].size;
	}
	return(SH_SUCC);
}

int
do_typedef(
        int argc,
        char **argv )
{
	struct memtbl *mem;
	int i, redo;
	char *name, *decl;

        if (!structInited)
           struct_init();

        i = 1;
	if (argc > 1 && C_PAIR(argv[i], '-', 'R')) 
        {
		redo = 0;
                i++;
	}
	else 
		redo = 1;

        if ((i + 1) >= argc)
        {
		XK_USAGE(argv[0]);
	}
        decl = argv[i++];
        name = argv[i++];

	for (i = 0; i < Ndynmem; i++)
		if (!(Dynmem[i]->flags & F_FIELD) && (strcmp(name, Dynmem[i]->name) == 0))
			break;
	if ((i < Ndynmem) && !redo) {
		if (!redo)
			return(SH_SUCC);
	}
	else if (Sdynmem - Ndynmem < 1)
		growmem();
	if (!(mem = (struct memtbl *) malloc(2 * sizeof(struct memtbl))))
		return(SH_FAIL);
	mem[1] = Null_tbl;
	if (i < Ndynmem) {
		freemem(Dynmem[i]);
		xkhash_override(Hashnams, name, mem);
	}
	else
		Ndynmem++;
	Dynmem[i] = mem;
	parse_decl(argv[0], mem, decl, 0);
	mem->name = strdup(name);
	return(SH_SUCC);
}

static char *
endtok(
        char *start )
{
	while(*start && !isspace(*start))
		start++;
	return(start);
}

int
parse_decl(
        char * argv0,
        struct memtbl *mem,
        char *decl,
        int tst )
{
	struct memtbl *tbl;
	char *p, *end;
	char hold;
	int flag = 0, done;
        char * msg;
        char * errbuf;
        char * errmsg;

	end = decl;
	do {
		p = end;
		xk_skipwhite(&p);
		end = endtok(p);
		hold = *end;
		*end = '\0';
		done = ((strcmp(p, (const char *) "struct") != 0) &&
			(strcmp(p, (const char *) "const") != 0) &&
			(strcmp(p, (const char *) "unsigned") != 0) &&
			(strcmp(p, (const char *) "signed") != 0) &&
			(strcmp(p, (const char *) "union") != 0));
		*end = hold;
	} while (!done && hold);
	if (!p[0]) {
		if (tst) {
			return(FAIL);
		}
		errmsg = strdup(GetSharedMsg(DT_BAD_DECL));
		printerrf(argv0, errmsg,
                           decl,NULL, NULL, NULL, NULL, NULL, NULL, NULL);
                free(errmsg);
		mem[0] = T_unsigned_long[0];
		return(SUCCESS);
	}
	hold = *end;
	*end = '\0';
	tbl = all_tbl_search(p, flag|NOHASH);
	*end = hold;
	if (!tbl) {
		if (tst) {
			return(FAIL);
		}
		errmsg = strdup(GetSharedMsg(DT_BAD_DECL));
		printerrf(argv0, errmsg,
                          decl, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
                free(errmsg);
		mem[0] = T_unsigned_long[0];
		return(SUCCESS);
	}
	mem[0] = tbl[0];
	for (p = end; *p; p++) {
		switch(*p) {
		case '[':
			{
				char *q = strchr(p, ']');

				if (!q) {
                                        errmsg=strdup(GETMESSAGE(12,1, 
                                           "Found a '[' character without a matching ']'"));
					printerr(argv0, errmsg, NULL);
                                        free(errmsg);
					continue;
				}
				p++;
				xk_par_int(&p, &(mem->subscr), NULL);
				mem->flags &= ~(F_SIMPLE);
				if (mem->subscr)
					mem->size *= mem->subscr;
				p = q;
				break;
			}
		case '*':
			if ((mem->kind == K_CHAR) && !(mem->ptr)) {
				char *name;

				name = mem->name;
				mem[0] = T_string_t[0];
				mem->name = name;
			}
			else {
				mem->ptr++;
				mem->flags &= ~(F_SIMPLE);
			}
			break;
		}
	}
	return(SUCCESS);
}

int
do_structlist(
        int argc,
        char **argv )
{
	int i, j, id = 0;
	char *prefix = NULL;
	struct memtbl **memptr = NULL;
        char * errmsg;

	for (i = 1; (i < argc) && argv[i]; i++) {
		if (argv[i][0] == '-') {
			for (j = 1; argv[i][j]; j++) {
				switch(argv[i][j]) {
				case 'i':
				   if (argv[i][j + 1])
					fdef(argv[i] + j + 1, &id);
				   else
					fdef(argv[++i], &id);
				   j = strlen(argv[i]) - 1;
			  	   break;
				case 'p':
				   if (argv[i][j + 1])
					prefix = argv[i] + j + 1;
				   else
					prefix = argv[++i];
				   j = strlen(prefix) - 1;
				   break;
				default:
                                   errmsg = strdup(GetSharedMsg(
                                                    DT_UNKNOWN_OPTION));
				   printerrf(argv[0], errmsg,
                                            argv[i], NULL, NULL, NULL, NULL,
                                            NULL, NULL, NULL);
                                   free(errmsg);
                                   xk_usage(argv[0]);
				   return(SH_FAIL);
				}
			}
		}
		else {
			if ((memptr = (memtbl_t **) getaddr(argv[i])) == NULL) 
                        {
                           errmsg=strdup(GETMESSAGE(12,2, 
                                "Unable to locate the following symbol: %s"));
			   printerrf(argv[0], errmsg, argv[i],
                                          NULL, NULL, NULL, NULL, NULL, NULL,
                                          NULL);
                           free(errmsg);
			   return(SH_FAIL);
			}
		}
	}

        if (memptr == NULL)
        {
           XK_USAGE(argv[0]);
        }

	for (i = 0; i < Nstructlist; i++)
        {
		if ((Structlist[i].mem == memptr) && 
                    (!prefix || (Structlist[i].prefix && 
                        (strcmp(Structlist[i].prefix, prefix) == 0))) &&
                    (!id || (Structlist[i].id == id)))
                {
			return(SH_SUCC);
                }
        }

	add_structlist(memptr, prefix, id);
}

static int
chg_structlist(
        struct memtbl **memptr,
        int id )
{
	int i;

	for (i = 0; i < Nstructlist; i++)
		if (Structlist[i].id == id) {
			Structlist[i].mem = memptr;
			return;
		}
}

int
add_structlist(
        struct memtbl **memptr,
        char *prefix,
        int id )
{
	int i;

	if (!Structlist)
		Structlist = (struct structlist *) malloc((Nstructlist + 1) * sizeof(struct structlist));
	else
		Structlist = (struct structlist *) realloc(Structlist, (Nstructlist + 1) * sizeof(struct structlist));
	if (!Structlist)
		return(SH_FAIL);
	Structlist[Nstructlist].mem = memptr;
	Structlist[Nstructlist].id = id;
	Structlist[Nstructlist].prefix = prefix ? strdup(prefix) : (char *)NULL;
	if (memptr[0] && memptr[0][0].name) {
		for (i = 1; memptr[i] && memptr[i][0].name && memptr[i][0].name[0]; i++)
			if (strcmp(memptr[i][0].name, memptr[i - 1][0].name) < 0)
				break;
		if (!(memptr[i] && memptr[i][0].name && memptr[i][0].name[0]))
			Structlist[Nstructlist].size = i - 1;
		else
			Structlist[Nstructlist].size = -1;
	}
	else
			Structlist[Nstructlist].size = 0;
	Nstructlist++;
	return(SH_SUCC);
}

int
strparse(
        memtbl_t *tbl,
        char **pbuf,
        char *val )
{
	char *p, *phold;
	int ret;

	if (!IS_SIMPLE(tbl) && !tbl->ptr && !(tbl->flags & F_TYPE_IS_PTR))
		tbl->ptr = 1;
	phold = p = strdup(val);
	ret = XK_PARSE(tbl, &p, (char *)pbuf, 0, 0, NULL, all_tbl_find);
	free(phold);
	return(ret != FAIL);
}

int
strfree(
        char *buf,
        char *type )
{
	memtbl_t tbl;

	if (parse_decl("strfree", &tbl, type, 1) == FAIL)
		return(SH_FAIL);
	if (!IS_SIMPLE(&tbl) && !tbl.ptr && !(tbl.flags & F_TYPE_IS_PTR))
		tbl.ptr = 1;
	if (XK_FREE(&tbl, (char *)&buf, 0, 0, all_tbl_find) == FAIL)
		return(SH_FAIL);
	return(SH_SUCC);
}

int
do_sizeof(
        int argc,
        char **argv )
{
	memtbl_t *tbl;
        char * errmsg;

	if (argc <= 1) {
		XK_USAGE(argv[0]);
	}
	if ((tbl = all_tbl_search(argv[1], 0)) == NULL) {
                errmsg=strdup(GETMESSAGE(12,3, 
                  "The following is not a valid data type or structure name: %s"));
		printerrf(argv[0], errmsg, argv[1], NULL, NULL,
                          NULL, NULL, NULL, NULL, NULL);
                free(errmsg);
		return(SH_FAIL);
	}
        if (argc >= 3) {
		char buf[50];

		sprintf(buf, use2, argv[2], tbl->ptr ? sizeof(void *) : tbl->size);
		env_set(buf);
	}
	else {
		sprintf(xk_ret_buffer, use, tbl->ptr ? sizeof(void *) : tbl->size);
		xk_ret_buf = xk_ret_buffer;
	}
	return(SH_SUCC);
}

memtbl_t *
all_tbl_find(
        char *name,
        int tbl,
        long id )
{
	int i;

	if (tbl != -1) {
		for (i = 0; i < Nstructlist; i++)
			if (id == Structlist[i].id)
				return(Structlist[i].mem[tbl]);
		return(NULL);
	}
	return(all_tbl_search(name, TYPEONLY));
}

memtbl_t *
all_tbl_search(
        char *name,
        int flag )
{
	register int i;
	void *found;

        if (!structInited)
           struct_init();

	if (found = (void *) xkhash_find(Hashnams, name))
		return((memtbl_t *) found);
	else {
		register int j;
		register memtbl_t **subtbl;

		for (i = 0; i < Nstructlist; i++) {
			if (subtbl = Structlist[i].mem)
				for (j = 0; subtbl[j]; j++)
					if (!(subtbl[j]->flags & F_FIELD) && (strcmp(name, subtbl[j]->name) == 0) && ((subtbl[j]->kind != K_TYPEDEF) || (subtbl[j]->tbl != -1))) {
						if (!(flag & NOHASH))
							xkhash_add(Hashnams, name, (char *)subtbl[j]);
						return(subtbl[j]);
					}
		}
	}
	return(NULL);
}

memtbl_t *
asl_find(
        memtbl_t *ptbl,
        memtbl_t *tbls,
        char *fld,
        char **pptr )
{
	int i;
	memtbl_t *tbl;

	if (!Structlist)
		return(NULL);
	if (!pptr && (ptbl == tbls))
		return(NULL);
	for (i = 0; tbls[i].name; i++) {
		if ((xk_Strncmp(tbls[i].name, fld, strlen(fld)) == 0) && (strlen(fld) == strlen(tbls[i].name))) {
			if (pptr && ptbl && ((ptbl->kind == K_STRUCT) || (ptbl->kind == K_ANY)))
				*pptr += tbls[i].offset;
			return(tbls + i);
		}
	}
	for (i = 0; tbls[i].name; i++) {
		if ((tbls[i].kind == K_TYPEDEF) || (tbls[i].kind == K_STRUCT) || (tbls[i].kind == K_UNION) || (tbls[i].kind == K_ANY)) {
			char *hold;

			if (!pptr) {
				if ((tbl = asl_find(tbls + i, all_tbl_find(tbls[i].tname, tbls[i].tbl, tbls[i].id), fld, pptr)) != NULL)
					return(tbl);
				continue;
			}
			hold = *pptr;
			if (tbls[i].ptr) {
				int nptr;

				nptr = tbls[i].ptr;
				/* if you hit a NULL, stop the loop */
				do {
					*pptr = *((char **) *pptr);
				} while (*pptr && --nptr);
			}
			if (*pptr) {
				if (!tbls[i].ptr)
					*pptr += tbls[i].offset;
				if ((tbl = asl_find(tbls + i, all_tbl_find(tbls[i].tname, tbls[i].tbl, tbls[i].id), fld, pptr)) != NULL)
					return(tbl);
				*pptr = hold;
			}
		}
	}
	return(NULL);
}

static
struct_init( void )
{
        char * errhdr;
        char * errmsg;

        structInited = 1;
	Hashnams = (void *) xkhash_init(50);
	if (!(Dynmem = (struct memtbl **) malloc(20 * sizeof(struct memtbl *))))
        {
                errhdr = strdup(GetSharedMsg(DT_ERROR));
                errmsg = strdup(GetSharedMsg(DT_ALLOC_FAILURE));
		printerr(errhdr, errmsg, NULL);
                free(errhdr);
                free(errmsg);
		exit(1);
	}
	Dynmem[0] = NULL;
	Sdynmem = 20;
	Ndynmem = 0;
	add_structlist(basemems, "base", BASE_ID);
	add_structlist(Dynmem, "dynamic", DYNMEM_ID);
}

