// wget http://projects.scratch.mit.edu/internalapi/project/21234602/get/

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

#ifndef JSMN_PARENT_LINKS
#define JSMN_PARENT_LINKS
#endif

#define JSMN_STRICT
#include "jsmn/jsmn.c"
#define JSMN_CHILD 4

// I originally added JSMN_CHILD to the enum, but I reverted jsmn to its original code
// and added that element here as a #define.  Being careful that it is higher than
// the elements in the enum in case the author adds any new elements of his own.
// (255 is probably better than 4)

char *typename[] = {
   "primitive", "object", "array", "string", "child"
};

typedef struct blockfm
{
   char *name;
   char *scratchblock;
} blockfm;

blockfm block[] = {
   {"append:toList:", "add %s to %m.list\n"},
   {"backdrop1", "switch backdrop to %m.backdrop and wait\n"},
   {"bounceOffEdge", "if on edge, bounce\n"},
   {"broadcast:", "broadcast %m.broadcast\n"},
   {"changeGraphicEffect:by:", "change %m.effect effect by %n\n"},
   {"changePenHueBy:", "change pen color by %n\n"},
   {"changePenShadeBy:", "change pen shade by %n\n"},
   {"changePenSizeBy:", "change pen size by %n\n"},
   {"changeSizeBy:", "change size by %n\n"},
   {"changeTempoBy:", "change tempo by %n\n"},
   {"changeVar:by:", "change %m.var by %n\n"},
   {"changeVolumeBy:", "change volume by %n\n"},
   {"changeXposBy:", "change x by %n\n"},
   {"changeYposBy:", "change y by %n\n"},
   {"clearPenTrails", "clear\n"},
   {"comeToFront", "go to front\n"},
   {"createCloneOf", "create clone of %m.spriteOnly\n"},
   {"deleteLine:ofList:", "delete %d.listDeleteItem of %m.list\n"},
   {"doAsk", "ask %s and wait\n"},
   {"doBroadcastAndWait", "broadcast %m.broadcast and wait\n"},
   {"doPlaySoundAndWait", "play sound %m.sound until done\n"},
   {"doWaitUntil", "wait until %b\n"},
   {"drum:duration:elapsed:from:", "play drum %n for %n beats\n"},
   {"filterReset", "clear graphic effects\n"},
   {"forward:", "move %n steps\n"},
   {"glideSecs:toX:y:elapsed:from:", "glide %n secs to x:%n y:%n\n"},
   {"goBackByLayers:", "go back %n layers\n"},
   {"gotoSpriteOrMouse:", "go to %m.spriteOrMouse\n"},
   {"gotoX:y:", "go to x:%n y:%n\n"},
   {"heading:", "point in direction %d.direction\n"},
   {"hide", "hide\n"},
   {"hideAll", "hide all sprites\n"},
   {"hideList:", "hide list %m.list\n"},
   {"hideVariable:", "hide variable %m.var\n"},
   {"insert:at:ofList:", "insert %s at %d.listItem of %m.list\n"},
   {"instrument:", "set instrument to %d.instrument\n"},
   {"lookLike:", "switch costume to %m.costume\n"},
   {"midiInstrument:", "set instrument to %n\n"},
   {"nextBackground", "next background\n"},
   {"nextCostume", "next costume\n"},
   {"nextScene", "next backdrop\n"},
   {"noteOn:duration:elapsed:from:", "play note %d.note for %n beats\n"},
   {"penColor:", "set pen color to %c\n"},
   {"penSize:", "set pen size to %n\n"},
   {"playDrum", "play drum %d.drum for %n beats\n"},
   {"playSound:", "play sound %m.sound\n"},
   {"pointTowards:", "point towards %m.spriteOrMouse\n"},
   {"putPenDown", "pen down\n"},
   {"putPenUp", "pen up\n"},
   {"rest:elapsed:from:", "rest for %n beats\n"},
   {"say:", "say %s\n"},
   {"say:duration:elapsed:from:", "say %s for %n secs\n"},
   {"scrollAlign", "align scene %m.scrollAlign\n"},
   {"scrollRight", "scroll right %n\n"},
   {"scrollUp", "scroll up %n\n"},
   {"setGraphicEffect:to:", "set %m.effect effect to %n\n"},
   {"setLine:ofList:to:", "replace item %d.listItem of %m.list with %s\n"},
   {"setPenHueTo:", "set pen color to %n\n"},
   {"setPenShadeTo:", "set pen shade to %n\n"},
   {"setRotationStyle", "set rotation style %m.rotationStyle\n"},
   {"setSizeTo:", "set size to %n\\%\n"},
   {"setTempoTo:", "set tempo to %n bpm\n"},
   {"setVar:to:", "set %m.var to %s\n"},
   {"setVideoState", "turn video %m.videoState\n"},
   {"setVideoTransparency", "set video transparency to %n\\%\n"},
   {"setVolumeTo:", "set volume to %n\\%\n"},
   {"show", "show\n"},
   {"showBackground:", "switch to background %m.costume\n"},
   {"showList:", "show list %m.list\n"},
   {"showVariable:", "show variable %m.var\n"},
   {"stampCostume", "stamp\n"},
   {"startScene", "switch backdrop to %m.backdrop\n"},
   {"stopAllSounds", "stop all sounds\n"},
   {"think:", "think %s\n"},
   {"think:duration:elapsed:from:", "think %s for %n secs\n"},
   {"timerReset", "reset timer\n"},
   {"turnLeft:", "turn Left %n degrees\n"},
   {"turnRight:", "turn Right %n degrees\n"},
   {"wait:elapsed:from:", "wait %n secs\n"},
   {"xpos:", "set x to %n\n"},
   {"ypos:", "set y to %n\n"},

   {"doForever", "forever\n%xend\n"},
   {"doForeverIf", "forever if %b\n%xend\n"},

   {"&", "%b and %b"},
   {"|", "%b or %b"},
   {"<", "%s < %s"},
   {"=", "%s = %s"},
   {">", "%s > %s"},
   {"not", "not %b"},
   {"isLoud", "loud?"},
   {"mousePressed", "mouse down?"},
   {"color:sees:", "color %c is touching %c?"},
   {"keyPressed:", "key %m.key pressed?"},
   {"list:contains:", "%m.list contains %s"},
   {"touching:", "touching %m.touching?"},
   {"touchingColor:", "touching color %c?"},

   {"doForLoop", "for each %m.varName in %s\n%xend\n"},
   {"doIf", "if %b then\n%xend\n"},
   {"doRepeat", "repeat %n\n%xend\n"},
   {"doUntil", "repeat until %b\n%xend\n"},
   {"doWhile", "while %b\n%xend\n"},

   {"doIfElse", "if %b then\n%xelse\n%xend\n"},

   {"deleteClone", "delete this clone\n"},
   {"doReturn", "stop script\n"},
   {"stopAll", "stop all\n"},
   {"stopScripts", "stop %m.stop\n"},

   {"whenClicked", "\nwhen this sprite clicked\n"},
   {"whenCloned", "\nwhen I start as a clone\n"},
   {"whenGreenFlag", "\nwhen gf clicked\n"},
   {"whenIReceive", "\nwhen I receive %m.broadcast\n"},
   {"whenKeyPressed", "\nwhen %m.key key pressed\n"},
   {"whenSceneStarts", "\nwhen backdrop switches to %m.backdrop\n"},
   {"whenSensorGreaterThan", "\nwhen %m.triggerSensor > %n\n"},

   {"%", "%n mod %n"},
   {"*", "%n * %n"},
   {"+", "%n + %n"},
   {"-", "%n - %n"},
   {"\\/", "%n / %n"},
   {"abs", "abs %n"},
   {"answer", "answer"},
   {"backgroundIndex", "backdrop #"},
   {"computeFunction:of:", "%m.mathOp of %n"},
   {"concatenate:with:", "join %s %s"},
   {"costumeIndex", "costume #"},
   {"distanceTo:", "distance to %m.spriteOrMouse"},
   {"getAttribute:of:", "%m.attribute of %m.spriteOrStage"},
   {"getLine:ofList:", "item %d.listItem of %m.list"},
   {"getUserId", "user id"},
   {"getUserName", "username"},
   {"heading", "direction"},
   {"letter:of:", "letter %n of %s"},
   {"lineCountOfList:", "length of %m.list"},
   {"mouseX", "mouse x"},
   {"mouseY", "mouse y"},
   {"randomFrom:to:", "pick random %n to %n"},
   {"rounded", "round %n"},
   {"scale", "size"},
   {"sceneName", "backdrop name"},
   {"senseVideoMotion", "video %m.videoMotionType on %m.stageOrThis"},
   {"soundLevel", "loudness"},
   {"sqrt", "sqrt %n"},
   {"stringLength:", "length of %s"},
   {"tempo", "tempo"},
   {"timeAndDate", "current %m.timeAndDate"},
   {"timer", "timer"},
   {"timestamp", "days since 2000"},
   {"volume", "volume"},
   {"xScroll", "x scroll"},
   {"xpos", "x position"},
   {"yScroll", "y scroll"},
   {"ypos", "y position"},
   {NULL, NULL}
};

#ifndef TRUE
#define TRUE (0==0)
#define FALSE (0!=0)
#endif

// When I first coded this, I passed all data around as parameters, but since
// this file totally encapsulates the translator anyway, there's no real benefit
// from keeping the code 'pure' - and it's a lot easier to read without a bunch
// of extra parameters to every procedure.  Undoubtedly faster too.

static char *js;
#define BIGPROG 1000000
static jsmntok_t token[BIGPROG];
static jsmntok_t tree[BIGPROG];
static int codeindent = 0, lastch = '\n';

#define ARG(n) token[root + 1 + (n)].size

void indent(int n) {while (n-- > 0) fprintf(stdout, "  ");}
void codechar(int c) {if (lastch == '\n') indent(codeindent); putchar(lastch = c);}
void codestr(char *s) {while (*s != '\0') codechar(*s++);}

// if you declare token[] on the stack inside main, (as was originally done following the
// example in the jsmn package) you'll get a run time error if BIGPROG is much over 400000
// - it's better to be static or allocated off the heap.

int tokstrcmp(jsmntok_t t, char *s) {return ((strncmp(js + t.start, s, t.end - t.start) == 0) && (strlen(s) == (t.end - t.start)));}

void showtok(int tok) {
   int i;
   if ((token[tok].type != JSMN_OBJECT) && (token[tok].type != JSMN_ARRAY)) {
      for (i = token[tok].start; i < token[tok].end; i++) codechar(js[i]);
   }
}

void HEX(int tok)
{
#define hexdigit(i) codechar("0123456789ABCDEF"[i&15])
   char *s, dec[256]; // need range check
   int i, hexnum;
   s = dec;
   if ((token[tok].type != JSMN_OBJECT) && (token[tok].type != JSMN_ARRAY)) {
      for (i = token[tok].start; i < token[tok].end; i++) *s++ = js[i]; *s = '\0';
      hexnum = atoi(dec);
      codechar('#');
      hexdigit(hexnum >> 20); hexdigit(hexnum >> 16); hexdigit(hexnum >> 12);
      hexdigit(hexnum >> 8); hexdigit(hexnum >> 4); hexdigit(hexnum);
   }
}

void reorder(jsmntok_t * tree, int tree_max,
	      int root, int depth, int display)
{
   static int tokenptr = 0, treeptr = 0;
   int base, children, i;
   // This makes up for a deficit in the lightweight json parser.  The jsmn
   // code as supplied requires you to walk *all* leaves of the tree in left
   // to right order whenever you access the json structure.  This is not
   // useful when you want to rearrange the order of blocks etc
   // so this code takes the serialised tree and inserts explicit branch
   // nodes, so that you may follow any path down the tree using your
   // preferred traverse order and omitting any parts that you are not
   // interested in.  Unfortunately I don't see how to add this to the
   // original code as it builds the tree, so I have retrofitted it by having
   // it copy from the original tree to a new copy.  Somewhat wasteful of
   // space, but not really an issue for Scratch json.

   tree[treeptr].type = token[root].type;
   tree[treeptr].start = token[root].start;
   tree[treeptr].end = token[root].end;
   tree[treeptr].size = token[root].size;
#ifdef JSMN_PARENT_LINKS
   tree[treeptr].parent = token[root].parent;
#endif
   if (treeptr + 1 >= tree_max) {
      // we could actually calculate the output size exactly with a dummy
      // pass through the data, allocate enough space off the heap, and then
      // copy the result back on top of the original array, followed by
      // freeing the heap space.  Checking of course that the original array
      // is large enough before mallocing the temp workspace...  next version 
      // perhaps...
      fprintf(stderr,
	      "reorder: the output array is not large enough - %d limit reached\n",
	      tree_max);
      exit(4);
   }
   tokenptr += 1;
   treeptr += 1;
   base = treeptr;

   if (token[root].type == JSMN_OBJECT || token[root].type == JSMN_ARRAY) {
      children = token[root].size;
      if (treeptr + 1 + children >= tree_max) {
	 fprintf(stderr,
		 "reorder: the output array is not large enough - %d limit exceeded\n",
		 tree_max);
	 exit(5);
      }
      treeptr += children;	// reserve extra space for the children
      for (i = 0; i < children; i++) {
	 tree[base + i].type = JSMN_CHILD;
	 tree[base + i].size = treeptr;
	 reorder(tree, tree_max, tokenptr, depth + 3, display);
      }
   }
}
void codegen(int root);
void translate(int root);
void statement(int root)
{
   int children, i;

   children = token[root].size;

   switch ((int)token[root].type) {

   case JSMN_CHILD:
      statement(ARG(0));
      return;

   case JSMN_PRIMITIVE:
      if (js[token[root].start] == 'f') {
        // false - used for empty boolean parameters
      } else if (js[token[root].start] == 't') {
        // true - not sure if it is used in code
      } else if (js[token[root].start] == 'n') {
        // null
      } else {
        showtok(root); // otherwise a number. (starts with 0..9)
      }
      return;

   case JSMN_STRING:
      if (tokstrcmp(token[root], "_mouse_")) {
        codestr("mouse-pointer"); // hack.  Would use an array as for the block names, if there were more of these...
      } else {
        showtok(root);
      }
      break;

   case JSMN_ARRAY:
      if (token[ARG(0)].type == JSMN_ARRAY) { // need to check that this one is correct...
        for (i = 0; i < children; i++) {
          statement(ARG(i));// possibly more children???
	}
        break;
      }

      // TO DO: not handling comments yet.
      if (tokstrcmp(token[ARG(0)], "readVariable")) { // Handle this with a block string?
	 statement(ARG(1));
      } else if (tokstrcmp(token[ARG(0)], "procDef")) { // TO DO.
	 codestr("\ndefine ");
	 showtok(ARG(1)); // followed by number, boolean or string arguments in subarray ARG(2), encoded as %n %b and %s in ARG(1)
	 codestr("\n");
      } else if (tokstrcmp(token[ARG(0)], "getParam")) { // TO DO.
	 codestr("(param ");
	 statement(ARG(1));
	 codestr(")");
      } else if (tokstrcmp(token[ARG(0)], "call")) { // TO DO.
	 //codestr("CALL ");
	 statement(ARG(1)); // followed by number, boolean or string arguments in subarray ARG(2), encoded as %n %b and %s in ARG(1)
	 //translate(ARG(2)); // tweak translate to take two parameters - format string, and start of argument list
         //                   // watch out for the '%x' code which will need fixing...
	 codestr("\n");
      } else {
	translate(root);
      }
      break;

   default:
      codestr("[statement: did not expect an unknown jsmn type ");
      printf("%s", typename[token[root].type]);
      codestr(" here.]");
      fprintf(stderr, "Error.\n");
      exit(__LINE__);
   }
}

void translate(int root)
{
         int argtype;
         int each = 0, nextarg = 1, i, j;
         char c, *format;
         char menutype[256];  // to do - add range check
         int found = FALSE;
         int argstr = ARG(0); // to do - define args

	 do {
	    if (tokstrcmp(token[argstr], block[each].name)) {
	       found = TRUE;
	       format = block[each].scratchblock;
	       i = j = 0;
	       for (;;) {
		  c = format[i];
		  if (c == '\\') {
		     codechar(format[++i]);
		  } else if (c == '%') {
		     argtype = format[++i];
		     if (format[i + 1] == '.') {
			i += 1;
			while (isalpha(format[++i])) {
			   menutype[j++] = format[i];
			}
			i -= 1;
			menutype[j] = '\0';
			(void)menutype;
		     }
		     switch (argtype) {
		     case 'p':
			// invented type for procedure parameters
		        nextarg = ARG(2);
			break;
		     case 'x':
			// invented type for nested block of text
			codeindent += 1;
			statement(ARG(nextarg++));
			codeindent -= 1;
			break;

                     // d, m, & c have a drop-down selector
		     case 'd':
		        if (token[ARG(nextarg)].type == JSMN_PRIMITIVE) { // [n] or [...string...] or [true] etc
			  codestr("("); statement(ARG(nextarg++)); codestr(" v)"); // was [] BEING TESTED
		        } else if (token[ARG(nextarg)].type == JSMN_ARRAY) { // [n] or [...string...] or [true] etc
		           codestr("("); statement(ARG(nextarg++)); codestr(")");
			} else if (token[ARG(nextarg)].type == JSMN_STRING) { // [n] or [...string...] or [true] etc
			  codestr("("); statement(ARG(nextarg++)); codestr(" v)");
		        } else {
		           codestr("{n1:"); codestr(typename[token[ARG(nextarg)].type]); codestr(": "); statement(ARG(nextarg)); codestr("}");
		           statement(ARG(nextarg++));
			}
		        break;

		     case 'm':
		        if (token[ARG(nextarg)].type == JSMN_PRIMITIVE) { // [n] or [...string...] or [true] etc
		           codestr("["); statement(ARG(nextarg++)); codestr(" v]");
		        } else if (token[ARG(nextarg)].type == JSMN_STRING) { // [n] or [...string...] or [true] etc
		           codestr("["); statement(ARG(nextarg++)); codestr(" v]");
		        } else {
			   //codestr("{s:"); codestr(typename[token[ARG(nextarg)].type]); codestr(": "); statement(ARG(nextarg)); codestr("}");
			   codestr("("); statement(ARG(nextarg++)); codestr(")");
		           //statement(ARG(nextarg++));
			}
                        break;

		     case 'c': // colour picker or name???
                        // if the param is a variable, show it (thus) otherwise:
		        if (token[ARG(nextarg)].type == JSMN_PRIMITIVE) { // [n] or [...string...] or [true] etc
		           codestr("["); HEX(ARG(nextarg++)); codestr("]");
			//} else if (token[ARG(nextarg)].type == JSMN_STRING) { // [n] or [...string...] or [true] etc
			  //codestr("["); HEX(ARG(nextarg++)); codestr("]");
		        } else {
			   codestr("("); statement(ARG(nextarg++)); codestr(")");
			}
		        break;

		     // string, number, boolean
		     case 's': // [string] var expr
		        if (token[ARG(nextarg)].type == JSMN_PRIMITIVE) { // [n] or [...string...] or [true] etc
		           codestr("["); statement(ARG(nextarg++)); codestr("]");
		        } else if (token[ARG(nextarg)].type == JSMN_STRING) { // [n] or [...string...] or [true] etc
		           codestr("["); statement(ARG(nextarg++)); codestr("]");
		        } else {
			   //codestr("{s:"); codestr(typename[token[ARG(nextarg)].type]); codestr(": "); statement(ARG(nextarg)); codestr("}");
			   codestr("("); statement(ARG(nextarg++)); codestr(")");
		           //statement(ARG(nextarg++));
			}
                        break;

		     case 'n': // [number] var expr
		        if (token[ARG(nextarg)].type == JSMN_PRIMITIVE) { // [n] or [...string...] or [true] etc
			  codestr("("); statement(ARG(nextarg++)); codestr(")"); // was [] BEING TESTED
		        } else if (token[ARG(nextarg)].type == JSMN_ARRAY) { // [n] or [...string...] or [true] etc
		           codestr("("); statement(ARG(nextarg++)); codestr(")");
		        } else if (token[ARG(nextarg)].type == JSMN_STRING) { // [n] or [...string...] or [true] etc
		           codestr("("); statement(ARG(nextarg++)); codestr(")");
		        } else {
		           codestr("{n2:"); codestr(typename[token[ARG(nextarg)].type]); codestr(": "); statement(ARG(nextarg)); codestr("}");
		           statement(ARG(nextarg++));
			}
		        break;

		     case 'b': // <boolean>
		        codestr("<"); statement(ARG(nextarg++)); codestr(">");
			break;

		     default:
		        fprintf(stderr, "warning: unknown format type %%%c\n", c);
		        statement(ARG(nextarg++));
		     }
                  } else if (c == '\0') {
		     break;
		  } else {
		     codechar(c);
		  }
		  i += 1;
	       }
	    }
	    each += 1;
	 } while (block[each].name != NULL);
	 if (!found) {
	    codestr("[% unknown block '");
	    showtok(ARG(0));
	    codestr("' %]");
	 }
}

void codeblock(int root) {int i; for (i = 0; i < token[root].size; i++) statement(ARG(i));}
void section(int root) {codeblock(ARG(2));}
void codegen(int root) {int i; for (i = 0; i < token[root].size; i++) section(ARG(i));}

void tree_walk(int root, int depth, int display)
{
   int keyword;			// token index
   int children, i;

   children = token[root].size;
   switch (token[root].type) {
   case JSMN_OBJECT:
      if (display) putchar('{');
      for (i = 0; i < children; i++) {
	 if ((i & 1) == 0) {
	    if (display) {putchar('\n'); indent(depth + 1);}
	    keyword = ARG(i);
	 } else {
	    if (display) printf(": ");
	 }
	 if (i &&
	     ((i & 1) == 1) && (tokstrcmp(token[keyword], "variables") ||
				tokstrcmp(token[keyword], "contents") ||
				tokstrcmp(token[keyword], "lists") ||
				tokstrcmp(token[keyword], "children") ||
				tokstrcmp(token[keyword], "sounds") ||
				tokstrcmp(token[keyword], "scripts") ||
				tokstrcmp(token[keyword], "costumes") ||
				tokstrcmp(token[keyword], "scriptComments"))) {
	    // now that we've added explicit child links using the 
	    // 'reorder' procedure, we can skip the subtree entirely,
	    // without reqiring to traverse it all in order to step 
	    // over data and reach the following sibling...  
	    if (tokstrcmp(token[keyword], "scripts")) {
	       fflush(stdout);
               codestr("\n[scratchblocks]\n");
	       codegen(ARG(i));
               codestr("[/scratchblocks]\n");
	       fflush(stdout);
	    } else if (tokstrcmp(token[keyword], "children")) {
	      tree_walk(ARG(i), depth, display); // sprites that hang off STAGE
	    } else {
	       if (display) fprintf(stdout, " << skipped >> ");
	    }
	 } else {
	    tree_walk(ARG(i), depth + 1, display);
	 }
	 if ((i & 1) && ((i + 1) != children)) {
	    if (display) printf(",");
	 }
      }
      if (display) {putchar('\n'); indent(depth); putchar('}');}
      break;

   case JSMN_ARRAY:
      if ((depth == 0) || (depth == 2)) { // list of sprite files 
	 for (i = 0; i < children; i++) tree_walk(ARG(i), depth + 1, display);
      } else if (depth == 1) { // list of blocks x y subblock 
	 for (i = 2; i < children; i++) tree_walk(ARG(i), depth + 1, display);
      } else if (depth >= 3) { // single statement
	 fprintf(stderr, "unexpected ARRAY at top level\n");
	 exit(__LINE__);
      }
      break;

   default:
      if (display) showtok(root);
   }
}

// On windows, you can remove the memory mapping code and just read the
// file int a malloc()ed array with stdio.

#include <fcntl.h>    // open
#include <sys/mman.h> // mmap
#include <unistd.h>   // lseek

int main(int argc, char **argv)
{
   int i, r;
   jsmn_parser p;
   int json_fd;
   off_t flen;

   if (argc != 2) {
      fprintf(stderr, "syntax: %s file.json\n", argv[1]);
      exit(1);
   }

   json_fd = open(argv[1], O_RDONLY);
   if (json_fd == -1) {
      fprintf(stderr, "%s - %s\n", argv[0], strerror(errno));
      exit(2);
   }
   flen = lseek(json_fd, (off_t) 0L, SEEK_END);
   js = mmap(NULL, flen, PROT_READ, MAP_SHARED, json_fd, (off_t) 0L);
   jsmn_init(&p);
   r = jsmn_parse(&p, js, strlen(js), token, BIGPROG);
   if (r <= 0) {
      fprintf(stderr, "syntax: %s - parse failed\n", argv[1]);
      exit(3);
   }
   reorder(tree, BIGPROG, 0, 0, FALSE); for (i = 0; i < BIGPROG; i++) token[i] = tree[i];
   tree_walk(0, 0, FALSE);

   exit(0);
   return 0;
}