"Fossies" - the Fresh Open Source Software Archive

Member "gamgi0.17.5x/src/expat/gamgi_expat_import_object.c" (23 Feb 2022, 226813 Bytes) of package /linux/misc/gamgi-all-0.17.5x.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /************************************************
    2  *
    3  * $GAMGI/src/expat/gamgi_expat_import_object.c
    4  *
    5  * Copyright (C) 2004 Carlos Pereira
    6  *
    7  * Distributed under the terms of the GNU
    8  * General Public License: $GAMGI/LICENSE
    9  *
   10  */
   11 
   12 #include "gamgi_engine.h"
   13 #include "gamgi_gtk.h"
   14 #include "gamgi_mesa.h"
   15 #include "gamgi_math.h"
   16 #include "gamgi_chem.h"
   17 #include "gamgi_phys.h"
   18 #include "gamgi_io.h"
   19 #include "gamgi_expat.h"
   20 #include "gamgi_global.h"
   21 
   22 #include "gamgi_engine_create.h"
   23 #include "gamgi_engine_start.h"
   24 #include "gamgi_engine_list.h"
   25 #include "gamgi_gtk_dialog.h"
   26 #include "gamgi_gtk_history.h"
   27 #include "gamgi_gtk_cell_symmetry.h"
   28 #include "gamgi_mesa_start.h"
   29 #include "gamgi_mesa_atom.h"
   30 #include "gamgi_mesa_bond.h"
   31 #include "gamgi_mesa_render.h"
   32 #include "gamgi_mesa_text.h"
   33 #include "gamgi_math_vector.h"
   34 #include "gamgi_math_matrix.h"
   35 #include "gamgi_math_euler.h"
   36 #include "gamgi_math_quaternion.h"
   37 #include "gamgi_math_hash.h"
   38 #include "gamgi_math_cell.h"
   39 #include "gamgi_math_position.h"
   40 #include "gamgi_math_node.h"
   41 #include "gamgi_math_polygon.h"
   42 #include "gamgi_chem_atom.h"
   43 #include "gamgi_chem_orbital.h"
   44 #include "gamgi_phys_cell.h"
   45 #include "gamgi_phys_projection.h"
   46 #include "gamgi_phys_direction.h"
   47 #include "gamgi_io_token.h"
   48 #include "gamgi_io_error.h"
   49 #include "gamgi_expat_import.h"
   50 #include "gamgi_expat_import_config.h"
   51 #include "gamgi_global_copy.h"
   52 
   53 static gamgi_bool static_gml_end (const char *element, gamgi_gml *gml)
   54 {
   55 /************************************************
   56  * parent stack ends here: output parent = NULL *
   57  ************************************************/
   58 
   59 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
   60 
   61 /**********************************************************
   62  * link objects to external parents indicated by id names *
   63  **********************************************************/
   64 
   65 return gamgi_expat_import_connect (gml);
   66 }
   67 
   68 static gamgi_bool static_gml_start (const char *element, 
   69 const char **attributes, gamgi_gml *gml)
   70 {
   71 int fileline;
   72 
   73 /******************************************
   74  * used only in error messages: file line *
   75  * indicates where current element starts *
   76  ******************************************/
   77 
   78 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
   79 
   80 /***********************************
   81  * gml should be the first element *
   82  ***********************************/
   83 
   84 if (gamgi_expat_import_parent_gml (GAMGI_EXPAT_GML, gml) == FALSE)
   85   return gamgi_io_error_child (gml->ml.filename, 
   86   fileline, gml->ml.window);
   87 
   88 /****************************
   89  * object queue starts here *
   90  ****************************/
   91 
   92 gml->object_end = gamgi_engine_dlist_add_end (NULL);
   93 gml->object_end->data = NULL;
   94 
   95 gml->object_start = gml->object_end;
   96 
   97 /****************************
   98  * parent stack starts here *
   99  ****************************/
  100 
  101 gml->ml.parent = gamgi_engine_slist_add_start (NULL);
  102 gml->ml.parent->data = NULL;
  103 
  104 /*********************************
  105  * gml should have no attributes *
  106  *********************************/
  107 
  108 if (attributes[0] != NULL)
  109   return gamgi_io_error_attribute (attributes[0],
  110   gml->ml.filename, fileline, gml->ml.window);
  111 
  112 return gml->ml.valid;
  113 }
  114 
  115 static gamgi_bool static_gamgi_start (const char *element,
  116 const char **attributes, gamgi_gml *gml)
  117 {
  118 gamgi_gamgi_class *gamgi_class;
  119 gamgi_object *object;
  120 int fileline, i;
  121 int button = 0;
  122 int background = 0, foreground = 0;
  123 int title = 0, bold = 0, link = 0;
  124 
  125 /******************************************
  126  * used only in error messages: file line *
  127  * indicates where current element starts *
  128  ******************************************/
  129 
  130 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
  131 
  132 /***********************
  133  * parent stack: check *
  134  ***********************/
  135 
  136 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_GAMGI, gml) == FALSE)
  137   return gamgi_io_error_child (gml->ml.filename, 
  138   fileline, gml->ml.window);
  139 
  140 /*************************************************
  141  * gamgi_class contains the current config info, *
  142  * which is gamgi->gamgi unless new config info  *
  143  * has been previously stored in object->object. *
  144  *************************************************/
  145 
  146 object = GAMGI_CAST_OBJECT gamgi->gamgi;
  147 if (object->object == NULL)
  148   {
  149   gamgi_class = gamgi_global_copy_gamgi (gamgi->gamgi);
  150   object->object = GAMGI_CAST_OBJECT gamgi_class;
  151   }
  152 else gamgi_class = (gamgi_gamgi_class *) object->object;
  153 
  154 /************************
  155  * parent stack: update *
  156  ************************/
  157 
  158 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
  159 gml->ml.parent->data = gamgi_class;
  160 
  161 /******************************************************
  162  * parser is entering a gamgi (configuration) block:  *
  163  * switch the data handlers to configuration handlers *
  164  ******************************************************/
  165 
  166 XML_SetStartElementHandler (gml->ml.parser, gamgi_expat_import_config_start);
  167 XML_SetEndElementHandler (gml->ml.parser, gamgi_expat_import_config_end);
  168 
  169 /**********************************
  170  * attribute array: check, update *
  171  **********************************/
  172 
  173 for (i = 0; attributes[i] != NULL; i += 2)
  174   {
  175   /*******************************
  176    * mouse parameters:           *
  177    * beep sound, pick tolerance, *
  178    * left, middle, right buttons *
  179    *******************************/
  180 
  181   if (strcmp (attributes[i], "sound") == 0)
  182     {
  183     /*********
  184      * reset *
  185      *********/
  186 
  187     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  188       gamgi_class->beep = gamgi->gamgi->beep;
  189 
  190     /**********
  191      * update *
  192      **********/
  193 
  194     else if (gamgi_io_token_bool_scan (attributes[i + 1],
  195     &gamgi_class->beep) == FALSE)
  196       return gamgi_io_error_value (attributes[i + 1], 
  197       gml->ml.filename, fileline, gml->ml.window);
  198     }
  199 
  200   else if (strcmp (attributes[i], "tolerance") == 0)
  201     {
  202     /*********
  203      * reset *
  204      *********/
  205 
  206     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  207       gamgi_class->tolerance = gamgi->gamgi->tolerance;
  208 
  209     /**********
  210      * update *
  211      **********/
  212 
  213     else if (gamgi_io_token_int_scan (attributes[i + 1],
  214     &gamgi_class->tolerance, 0, GAMGI_MESA_PICK_TOLERANCE_MAX) == FALSE)
  215       return gamgi_io_error_value (attributes[i + 1], 
  216       gml->ml.filename, fileline, gml->ml.window);
  217     }
  218 
  219   else if (strcmp (attributes[i], "button1") == 0)
  220     {
  221     button++;
  222 
  223     /*********
  224      * reset *
  225      *********/
  226 
  227     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  228       gamgi_class->button1 = gamgi->gamgi->button1;
  229 
  230     /**********
  231      * update *
  232      **********/
  233 
  234     else if (gamgi_io_token_int_scan (attributes[i + 1],
  235     &gamgi_class->button1, 1, 3) == FALSE) 
  236       return gamgi_io_error_value (attributes[i + 1], 
  237       gml->ml.filename, fileline, gml->ml.window);
  238 
  239     /***********************************************************
  240      * convert 1,2,3 to BUTTON1_MASK,BUTTON2_MASK,BUTTON3_MASK *
  241      ***********************************************************/
  242 
  243     else gamgi_class->button1 = 
  244     gamgi_gtk_dialog_button_mask (gamgi_class->button1);
  245     }
  246 
  247   else if (strcmp (attributes[i], "button2") == 0)
  248     {
  249     button++;
  250 
  251     /*********
  252      * reset *
  253      *********/
  254 
  255     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  256       gamgi_class->button2 = gamgi->gamgi->button2;
  257 
  258     /**********
  259      * update *
  260      **********/
  261 
  262     else if (gamgi_io_token_int_scan (attributes[i + 1],
  263     &gamgi_class->button2, 1, 3) == FALSE) 
  264       return gamgi_io_error_value (attributes[i + 1], 
  265       gml->ml.filename, fileline, gml->ml.window);
  266 
  267     /***********************************************************
  268      * convert 1,2,3 to BUTTON1_MASK,BUTTON2_MASK,BUTTON3_MASK *
  269      ***********************************************************/
  270 
  271     else gamgi_class->button2 = 
  272     gamgi_gtk_dialog_button_mask (gamgi_class->button2);
  273     }
  274 
  275   else if (strcmp (attributes[i], "button3") == 0)
  276     {
  277     button++;
  278 
  279     /*********
  280      * reset *
  281      *********/
  282 
  283     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  284       gamgi_class->button3 = gamgi->gamgi->button3;
  285 
  286     /**********
  287      * update *
  288      **********/
  289 
  290     else if (gamgi_io_token_int_scan (attributes[i + 1],
  291     &gamgi_class->button3, 1, 3) == FALSE) 
  292       return gamgi_io_error_value (attributes[i + 1], 
  293       gml->ml.filename, fileline, gml->ml.window);
  294 
  295     /***********************************************************
  296      * convert 1,2,3 to BUTTON1_MASK,BUTTON2_MASK,BUTTON3_MASK *
  297      ***********************************************************/
  298 
  299     else gamgi_class->button3 = 
  300     gamgi_gtk_dialog_button_mask (gamgi_class->button3);
  301     }
  302 
  303   /*******************************
  304    * number of decimal figures:  *
  305    * length, angle, charge, mass *
  306    *******************************/
  307 
  308   else if (strcmp (attributes[i], "length") == 0) 
  309     {
  310     /*********
  311      * reset *
  312      *********/
  313 
  314     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  315       gamgi_class->length = gamgi->gamgi->length;
  316 
  317     /**********
  318      * update *
  319      **********/
  320 
  321     else if (gamgi_io_token_int_scan (attributes[i + 1],
  322     &gamgi_class->length, 0, GAMGI_MATH_DECIMAL_MAX) == FALSE) 
  323       return gamgi_io_error_value (attributes[i + 1], 
  324       gml->ml.filename, fileline, gml->ml.window);
  325     }
  326 
  327   else if (strcmp (attributes[i], "angle") == 0) 
  328     {
  329     /*********
  330      * reset *
  331      *********/
  332 
  333     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  334       gamgi_class->angle = gamgi->gamgi->angle;
  335 
  336     /**********
  337      * update *
  338      **********/
  339 
  340     else if (gamgi_io_token_int_scan (attributes[i + 1],
  341     &gamgi_class->angle, 0, GAMGI_MATH_DECIMAL_MAX) == FALSE) 
  342       return gamgi_io_error_value (attributes[i + 1], 
  343       gml->ml.filename, fileline, gml->ml.window);
  344     }
  345 
  346   else if (strcmp (attributes[i], "mass") == 0) 
  347     {
  348     /*********
  349      * reset *
  350      *********/
  351 
  352     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  353       gamgi_class->mass = gamgi->gamgi->mass;
  354 
  355     /**********
  356      * update *
  357      **********/
  358 
  359     else if (gamgi_io_token_int_scan (attributes[i + 1],
  360     &gamgi_class->mass, 0, GAMGI_MATH_DECIMAL_MAX) == FALSE) 
  361       return gamgi_io_error_value (attributes[i + 1], 
  362       gml->ml.filename, fileline, gml->ml.window);
  363     }
  364 
  365   else if (strcmp (attributes[i], "charge") == 0)
  366     {
  367     /*********
  368      * reset *
  369      *********/
  370 
  371     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  372       gamgi_class->charge = gamgi->gamgi->charge;
  373 
  374     /**********
  375      * update *
  376      **********/
  377 
  378     else if (gamgi_io_token_int_scan (attributes[i + 1],
  379     &gamgi_class->charge, 0, GAMGI_MATH_DECIMAL_MAX) == FALSE)
  380       return gamgi_io_error_value (attributes[i + 1],
  381       gml->ml.filename, fileline, gml->ml.window);
  382     }
  383 
  384   /*********************************
  385    * color parameters: background, *
  386    * foreground, title, bold, link *
  387    *********************************/
  388 
  389   /****************************************
  390    * red,green,blue background (optional) *
  391    ****************************************/
  392 
  393   else if (strcmp (attributes[i], "base_r") == 0)
  394     {
  395     background++;
  396 
  397     /*********
  398      * reset *
  399      *********/
  400 
  401     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  402       gamgi_class->background[0] = gamgi->gamgi->background[0];
  403 
  404     /**********
  405      * update *
  406      **********/
  407 
  408     else if (gamgi_io_token_float_scan (attributes[i + 1],
  409     &gamgi_class->background[0], 0.0, 1.0) == FALSE)
  410       return gamgi_io_error_value (attributes[i + 1],
  411       gml->ml.filename, fileline, gml->ml.window);
  412     }
  413 
  414   else if (strcmp (attributes[i], "base_g") == 0)
  415     {
  416     background++;
  417 
  418     /*********
  419      * reset *
  420      *********/
  421 
  422     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  423       gamgi_class->background[1] = gamgi->gamgi->background[1];
  424 
  425     /**********
  426      * update *
  427      **********/
  428 
  429     else if (gamgi_io_token_float_scan (attributes[i + 1],
  430     &gamgi_class->background[1], 0.0, 1.0) == FALSE)
  431       return gamgi_io_error_value (attributes[i + 1],
  432       gml->ml.filename, fileline, gml->ml.window);
  433     }
  434 
  435   else if (strcmp (attributes[i], "base_b") == 0)
  436     {
  437     background++;
  438 
  439     /*********
  440      * reset *
  441      *********/
  442 
  443     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  444       gamgi_class->background[2] = gamgi->gamgi->background[2];
  445 
  446     /**********
  447      * update *
  448      **********/
  449 
  450     else if (gamgi_io_token_float_scan (attributes[i + 1],
  451     &gamgi_class->background[2], 0.0, 1.0) == FALSE)
  452       return gamgi_io_error_value (attributes[i + 1],
  453       gml->ml.filename, fileline, gml->ml.window);
  454     }
  455 
  456   /****************************************
  457    * red,green,blue foreground (optional) *
  458    ****************************************/
  459 
  460   else if (strcmp (attributes[i], "text_r") == 0)
  461     {
  462     foreground++;
  463 
  464     /*********
  465      * reset *
  466      *********/
  467 
  468     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  469       gamgi_class->foreground[0] = gamgi->gamgi->foreground[0];
  470 
  471     /**********
  472      * update *
  473      **********/
  474 
  475     else if (gamgi_io_token_float_scan (attributes[i + 1],
  476     &gamgi_class->foreground[0], 0.0, 1.0) == FALSE)
  477       return gamgi_io_error_value (attributes[i + 1],
  478       gml->ml.filename, fileline, gml->ml.window);
  479     }
  480 
  481   else if (strcmp (attributes[i], "text_g") == 0)
  482     {
  483     foreground++;
  484 
  485     /*********
  486      * reset *
  487      *********/
  488 
  489     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  490       gamgi_class->foreground[1] = gamgi->gamgi->foreground[1];
  491 
  492     /**********
  493      * update *
  494      **********/
  495 
  496     else if (gamgi_io_token_float_scan (attributes[i + 1],
  497     &gamgi_class->foreground[1], 0.0, 1.0) == FALSE)
  498       return gamgi_io_error_value (attributes[i + 1],
  499       gml->ml.filename, fileline, gml->ml.window);
  500     }
  501 
  502   else if (strcmp (attributes[i], "text_b") == 0)
  503     {
  504     foreground++;
  505 
  506     /*********
  507      * reset *
  508      *********/
  509 
  510     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  511       gamgi_class->foreground[2] = gamgi->gamgi->foreground[2];
  512 
  513     /**********
  514      * update *
  515      **********/
  516 
  517     else if (gamgi_io_token_float_scan (attributes[i + 1],
  518     &gamgi_class->foreground[2], 0.0, 1.0) == FALSE)
  519       return gamgi_io_error_value (attributes[i + 1],
  520       gml->ml.filename, fileline, gml->ml.window);
  521     }
  522 
  523   /***********************************
  524    * red,green,blue title (optional) *
  525    ***********************************/
  526 
  527   else if (strcmp (attributes[i], "title_r") == 0)
  528     {
  529     title++;
  530 
  531     /*********
  532      * reset *
  533      *********/
  534 
  535     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  536       gamgi_class->title[0] = gamgi->gamgi->title[0];
  537 
  538     /**********
  539      * update *
  540      **********/
  541 
  542     else if (gamgi_io_token_float_scan (attributes[i + 1],
  543     &gamgi_class->title[0], 0.0, 1.0) == FALSE)
  544       return gamgi_io_error_value (attributes[i + 1],
  545       gml->ml.filename, fileline, gml->ml.window);
  546     }
  547 
  548   else if (strcmp (attributes[i], "title_g") == 0)
  549     {
  550     title++;
  551 
  552     /*********
  553      * reset *
  554      *********/
  555 
  556     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  557       gamgi_class->title[1] = gamgi->gamgi->title[1];
  558 
  559     /**********
  560      * update *
  561      **********/
  562 
  563     else if (gamgi_io_token_float_scan (attributes[i + 1],
  564     &gamgi_class->title[1], 0.0, 1.0) == FALSE)
  565       return gamgi_io_error_value (attributes[i + 1],
  566       gml->ml.filename, fileline, gml->ml.window);
  567     }
  568 
  569   else if (strcmp (attributes[i], "title_b") == 0)
  570     {
  571     title++;
  572 
  573     /*********
  574      * reset *
  575      *********/
  576 
  577     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  578       gamgi_class->title[2] = gamgi->gamgi->title[2];
  579 
  580     /**********
  581      * update *
  582      **********/
  583 
  584     else if (gamgi_io_token_float_scan (attributes[i + 1],
  585     &gamgi_class->title[2], 0.0, 1.0) == FALSE)
  586       return gamgi_io_error_value (attributes[i + 1],
  587       gml->ml.filename, fileline, gml->ml.window);
  588     }
  589 
  590   /**********************************
  591    * red,green,blue bold (optional) *
  592    **********************************/
  593 
  594   else if (strcmp (attributes[i], "bold_r") == 0)
  595     {
  596     bold++;
  597 
  598     /*********
  599      * reset *
  600      *********/
  601 
  602     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  603       gamgi_class->bold[0] = gamgi->gamgi->bold[0];
  604 
  605     /**********
  606      * update *
  607      **********/
  608 
  609     else if (gamgi_io_token_float_scan (attributes[i + 1],
  610     &gamgi_class->bold[0], 0.0, 1.0) == FALSE)
  611       return gamgi_io_error_value (attributes[i + 1],
  612       gml->ml.filename, fileline, gml->ml.window);
  613     }
  614 
  615   else if (strcmp (attributes[i], "bold_g") == 0)
  616     {
  617     bold++;
  618 
  619     /*********
  620      * reset *
  621      *********/
  622 
  623     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  624       gamgi_class->bold[1] = gamgi->gamgi->bold[1];
  625 
  626     /**********
  627      * update *
  628      **********/
  629 
  630     else if (gamgi_io_token_float_scan (attributes[i + 1],
  631     &gamgi_class->bold[1], 0.0, 1.0) == FALSE)
  632       return gamgi_io_error_value (attributes[i + 1],
  633       gml->ml.filename, fileline, gml->ml.window);
  634     }
  635 
  636   else if (strcmp (attributes[i], "bold_b") == 0)
  637     {
  638     bold++;
  639 
  640     /*********
  641      * reset *
  642      *********/
  643 
  644     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  645       gamgi_class->bold[2] = gamgi->gamgi->bold[2];
  646 
  647     /**********
  648      * update *
  649      **********/
  650 
  651     else if (gamgi_io_token_float_scan (attributes[i + 1],
  652     &gamgi_class->bold[2], 0.0, 1.0) == FALSE)
  653       return gamgi_io_error_value (attributes[i + 1],
  654       gml->ml.filename, fileline, gml->ml.window);
  655     }
  656 
  657   /**********************************
  658    * red,green,blue link (optional) *
  659    **********************************/
  660 
  661   else if (strcmp (attributes[i], "link_r") == 0)
  662     {
  663     link++;
  664 
  665     /*********
  666      * reset *
  667      *********/
  668 
  669     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  670       gamgi_class->link[0] = gamgi->gamgi->link[0];
  671 
  672     /**********
  673      * update *
  674      **********/
  675 
  676     else if (gamgi_io_token_float_scan (attributes[i + 1],
  677     &gamgi_class->link[0], 0.0, 1.0) == FALSE)
  678       return gamgi_io_error_value (attributes[i + 1],
  679       gml->ml.filename, fileline, gml->ml.window);
  680     }
  681 
  682   else if (strcmp (attributes[i], "link_g") == 0)
  683     {
  684     link++;
  685 
  686     /*********
  687      * reset *
  688      *********/
  689 
  690     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  691       gamgi_class->link[1] = gamgi->gamgi->link[1];
  692 
  693     /**********
  694      * update *
  695      **********/
  696 
  697     else if (gamgi_io_token_float_scan (attributes[i + 1],
  698     &gamgi_class->link[1], 0.0, 1.0) == FALSE)
  699       return gamgi_io_error_value (attributes[i + 1],
  700       gml->ml.filename, fileline, gml->ml.window);
  701     }
  702 
  703   else if (strcmp (attributes[i], "link_b") == 0)
  704     {
  705     link++;
  706  
  707     /*********
  708      * reset *
  709      *********/
  710 
  711     if (gamgi_io_token_check (attributes[i + 1]) == FALSE)
  712       gamgi_class->link[2] = gamgi->gamgi->link[2];
  713 
  714     /**********
  715      * update * 
  716      **********/
  717 
  718     else if (gamgi_io_token_float_scan (attributes[i + 1],
  719     &gamgi_class->link[2], 0.0, 1.0) == FALSE)
  720       return gamgi_io_error_value (attributes[i + 1],
  721       gml->ml.filename, fileline, gml->ml.window);
  722     }
  723 
  724   else return gamgi_io_error_attribute (attributes[i], 
  725   gml->ml.filename, fileline, gml->ml.window);
  726   }
  727 
  728 /******************************
  729  * global: missing attributes *
  730  ******************************/
  731 
  732 /*************************************************
  733  * button1, button2, button3 (coupled, optional) *
  734  *************************************************/
  735 
  736 if (button == 1 || button == 2)
  737   return gamgi_io_error_missing (gml->ml.filename, 
  738   fileline, gml->ml.window);
  739 
  740 /************************************************************************
  741  * background, foreground, title, bold, link colors (coupled, optional) *
  742  ************************************************************************/
  743 
  744 if (background == 1 || background == 2)
  745   return gamgi_io_error_missing (gml->ml.filename,
  746   fileline, gml->ml.window);
  747 
  748 if (foreground == 1 || foreground == 2) 
  749   return gamgi_io_error_missing (gml->ml.filename,
  750   fileline, gml->ml.window);
  751 
  752 if (title == 1 || title == 2)
  753   return gamgi_io_error_missing (gml->ml.filename,
  754   fileline, gml->ml.window); 
  755 
  756 if (bold == 1 || bold == 2) 
  757   return gamgi_io_error_missing (gml->ml.filename,
  758   fileline, gml->ml.window);
  759 
  760 if (link == 1 || link == 2)
  761   return gamgi_io_error_missing (gml->ml.filename,
  762   fileline, gml->ml.window);
  763 
  764 /***********************************
  765  * global: incompatible attributes *
  766  ***********************************/
  767 
  768 /*******************************************
  769  * (button1,button2,button3) must point to *
  770  * one of the six valid (1,2,3) sequences. *
  771  *******************************************/
  772 
  773 if (gamgi_class->button1 == gamgi_class->button2 ||
  774 gamgi_class->button1 == gamgi_class->button3 ||
  775 gamgi_class->button2 == gamgi_class->button3)
  776   return gamgi_io_error_incompatible (gml->ml.filename, 
  777   fileline, gml->ml.window);
  778 
  779 return gml->ml.valid;
  780 }
  781 
  782 static gamgi_bool static_window_end (const char *element, gamgi_gml *gml)
  783 {
  784 int fileline;
  785 
  786 /******************************************
  787  * used only in error messages: file line *
  788  * indicates where current element starts *
  789  ******************************************/
  790 
  791 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
  792 
  793 /*****************************************************
  794  * <window/> or <window></window> is not acceptable, *
  795  * windows must own at least an empty layer. This    *
  796  * error is not detected by parent checking, so we   *
  797  * need to compare the parent (which must be window) *
  798    with the last object (which must not be window).  *
  799  *****************************************************/
  800 
  801 if (gml->object_end->data == gml->ml.parent->data)
  802   return gamgi_io_error_empty (gml->ml.filename, 
  803   fileline, gml->ml.window);
  804 
  805 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
  806 
  807 return gml->ml.valid;
  808 }
  809 
  810 static gamgi_bool static_window_start (const char *element,
  811 const char **attributes, gamgi_gml *gml)
  812 {
  813 gamgi_window *window;
  814 gamgi_window_class *window_class;
  815 gamgi_object *object;
  816 char token[GAMGI_ENGINE_TOKEN];
  817 int fileline, i;
  818 int origin = 0, size = 0;
  819 unsigned int hash;
  820 
  821 /******************************************
  822  * used only in error messages: file line *
  823  * indicates where current element starts *
  824  ******************************************/
  825 
  826 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
  827 
  828 /***********************
  829  * parent stack: check *
  830  ***********************/
  831 
  832 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_WINDOW, gml) == FALSE)
  833   return gamgi_io_error_child (gml->ml.filename, 
  834   fileline, gml->ml.window);
  835 
  836 /**************************************************
  837  * window_class contains the current config info, *
  838  * which is gamgi->window unless new config info  *
  839  * has been previously stored in object->object.  *
  840  **************************************************/
  841 
  842 object = GAMGI_CAST_OBJECT gamgi->window;
  843 if (object->object == NULL) window_class = gamgi->window;
  844 else window_class = (gamgi_window_class *) object->object;
  845 
  846 /******************
  847  * object: create *
  848  ******************/
  849 
  850 window = gamgi_engine_create_window ();
  851 gamgi_engine_start_window (window);
  852 gamgi_mesa_start_window (window, window_class);
  853 
  854 window->object.object = GAMGI_CAST_OBJECT gml->ml.parent->data;
  855 
  856 /************************
  857  * object queue: update *
  858  ************************/
  859 
  860 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
  861 gml->object_end->data = window;
  862 
  863 /************************
  864  * parent stack: update *
  865  ************************/
  866 
  867 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
  868 gml->ml.parent->data = window;
  869 
  870 /**********************************
  871  * attribute array: check, update *
  872  **********************************/
  873 
  874 for (i = 0; attributes[i] != NULL; i += 2)
  875   {
  876   /********
  877    * name *
  878    ********/
  879 
  880   if (strcmp (attributes[i], "name") == 0)
  881     {
  882     if (gamgi_io_token_alpha_scan (attributes[i + 1], 
  883     window->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
  884       return gamgi_io_error_value (attributes[i + 1], 
  885       gml->ml.filename, fileline, gml->ml.window);
  886     }
  887 
  888   /*****************
  889    * id (optional) *
  890    *****************/
  891 
  892   else if (strcmp (attributes[i], "id") == 0)
  893     {
  894     if (gamgi_io_token_alpha_scan (attributes[i + 1],
  895     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
  896       return gamgi_io_error_value (attributes[i + 1], 
  897       gml->ml.filename, fileline, gml->ml.window);
  898     
  899     hash = gamgi_math_hash_value (token,
  900     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
  901 
  902     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
  903       return gamgi_io_error_multiple (gml->ml.filename, 
  904       fileline, gml->ml.window);
  905 
  906     gamgi_expat_import_id_add (token, gml, hash);
  907     }
  908 
  909   /****************************************************
  910    *                geometry parameters               *
  911    *                                                  *
  912    * origin: x, y must be in the ranges               *
  913    * [-screen_width, +screen_width]                   *
  914    * [-screen_height, +screen_height]                 *
  915    *                                                  *
  916    * size: w, h must be in the ranges                 *
  917    * [GAMGI_GTK_WINDOW_WIDTH_MIN, 2 * screen_width]   *
  918    * [GAMGI_GTK_WINDOW_HEIGHT_MIN, 2 * screen_height] *
  919    ****************************************************/
  920 
  921   else if (strcmp (attributes[i], "x") == 0)
  922     {
  923     origin++;
  924     if (gamgi_io_token_int_scan (attributes[i + 1],
  925     &window->origin_x, -gamgi->width, gamgi->width) == FALSE)
  926       return gamgi_io_error_value (attributes[i + 1], 
  927       gml->ml.filename, fileline, gml->ml.window);
  928     }
  929 
  930   else if (strcmp (attributes[i], "y") == 0)
  931     {
  932     origin++;
  933     if (gamgi_io_token_int_scan (attributes[i + 1], 
  934     &window->origin_y, -gamgi->height, gamgi->height) == FALSE)
  935       return gamgi_io_error_value (attributes[i + 1], 
  936       gml->ml.filename, fileline, gml->ml.window);
  937     }
  938 
  939   else if (strcmp (attributes[i], "width") == 0)
  940     {
  941     size++;
  942     if (gamgi_io_token_int_scan (attributes[i + 1], &window->width, 
  943     GAMGI_GTK_WINDOW_WIDTH_MIN, 2 * gamgi->width) == FALSE)
  944       return gamgi_io_error_value (attributes[i + 1], 
  945       gml->ml.filename, fileline, gml->ml.window);
  946     }
  947 
  948   else if (strcmp (attributes[i], "height") == 0)
  949     {
  950     size++;
  951     if (gamgi_io_token_int_scan (attributes[i + 1], &window->height, 
  952     GAMGI_GTK_WINDOW_HEIGHT_MIN, 2 * gamgi->height) == FALSE)
  953       return gamgi_io_error_value (attributes[i + 1], 
  954       gml->ml.filename, fileline, gml->ml.window);
  955     }
  956 
  957   /***********************
  958    * show parameters:    *
  959    * top, medium, bottom *
  960    ***********************/
  961 
  962   else if (strcmp (attributes[i], "top") == 0) 
  963     {
  964     if (gamgi_io_token_bool_scan (attributes[i + 1], 
  965     &window->top_flag) == FALSE)
  966       return gamgi_io_error_value (attributes[i + 1], 
  967       gml->ml.filename, fileline, gml->ml.window);
  968     }
  969 
  970   else if (strcmp (attributes[i], "medium") == 0)
  971     {
  972     if (gamgi_io_token_bool_scan (attributes[i + 1],
  973     &window->medium_flag) == FALSE)
  974       return gamgi_io_error_value (attributes[i + 1], 
  975       gml->ml.filename, fileline, gml->ml.window);
  976     }
  977 
  978   else if (strcmp (attributes[i], "bottom") == 0)
  979     {
  980     if (gamgi_io_token_bool_scan (attributes[i + 1],
  981     &window->bottom_flag) == FALSE)
  982       return gamgi_io_error_value (attributes[i + 1], 
  983       gml->ml.filename, fileline, gml->ml.window);
  984     }
  985 
  986   else return gamgi_io_error_attribute (attributes[i], 
  987   gml->ml.filename, fileline, gml->ml.window);
  988   }
  989 
  990 /*****************************************
  991  * global tests: incompatible attributes *
  992  *****************************************/
  993 
  994 if (origin == 1)
  995   return gamgi_io_error_incompatible (gml->ml.filename, 
  996   fileline, gml->ml.window);
  997 
  998 if (size == 1)
  999   return gamgi_io_error_incompatible (gml->ml.filename, 
 1000   fileline, gml->ml.window);
 1001 
 1002 return gml->ml.valid;
 1003 }
 1004 
 1005 static gamgi_bool static_layer_end (const char *element, gamgi_gml *gml)
 1006 {
 1007 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 1008 
 1009 return gml->ml.valid;
 1010 }
 1011 
 1012 static gamgi_bool static_layer_start (const char *element,
 1013 const char **attributes, gamgi_gml *gml)
 1014 {
 1015 gamgi_layer *layer;
 1016 gamgi_layer_class *layer_class;
 1017 gamgi_object *object;
 1018 char token[GAMGI_ENGINE_TOKEN];
 1019 double x[3], z[3], up_z[3];
 1020 double length;
 1021 int fileline, i;
 1022 int eye = 0, center = 0, up = 0, color = 0;
 1023 unsigned int hash;
 1024 
 1025 /******************************************
 1026  * used only in error messages: file line *
 1027  * indicates where current element starts *
 1028  ******************************************/
 1029 
 1030 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 1031 
 1032 /***********************
 1033  * parent stack: check *
 1034  ***********************/
 1035 
 1036 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_LAYER, gml) == FALSE)
 1037   return gamgi_io_error_child (gml->ml.filename, 
 1038   fileline, gml->ml.window);
 1039 
 1040 /*************************************************
 1041  * layer_class contains the current config info, *
 1042  * which is gamgi->layer unless new config info  *
 1043  * has been previously stored in object->object. *
 1044  *************************************************/
 1045 
 1046 object = GAMGI_CAST_OBJECT gamgi->layer;
 1047 if (object->object == NULL) layer_class = gamgi->layer;
 1048 else layer_class = (gamgi_layer_class *) object->object;
 1049 
 1050 /******************
 1051  * object: create *
 1052  ******************/
 1053 
 1054 layer = gamgi_engine_create_layer ();
 1055 gamgi_engine_start_layer (layer);
 1056 gamgi_mesa_start_layer (layer, layer_class);
 1057 
 1058 /************************************************************
 1059  * by default, each new layer belongs to the current window *
 1060  ************************************************************/
 1061 
 1062 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 1063 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window;
 1064 layer->object.object = object;
 1065 
 1066 /************************
 1067  * object queue: update *
 1068  ************************/
 1069 
 1070 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 1071 gml->object_end->data = layer;
 1072 
 1073 /************************
 1074  * parent stack: update *
 1075  ************************/
 1076 
 1077 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 1078 gml->ml.parent->data = layer;
 1079 
 1080 /**********************************
 1081  * start undo and save mechanisms *
 1082  **********************************/
 1083 
 1084 gamgi_gtk_history_start (layer);
 1085 
 1086 /*************************************************************
 1087  * new top layer: we cannot update gml->ml.window->layer     *
 1088  * untill we are sure that the file is good. For new windows *
 1089  * this problem does not exist, as its whole contents will   *
 1090  * be removed if the file is bad. When the import operation  *
 1091  * ends ok, the top layer becomes also the curret object     *
 1092  *************************************************************/
 1093 
 1094 if (object != GAMGI_CAST_OBJECT gml->ml.window)
 1095   (GAMGI_CAST_WINDOW object)->layer = layer;
 1096 
 1097 /**********************************
 1098  * attribute array: check, update *
 1099  **********************************/
 1100 
 1101 for (i = 0; attributes[i] != NULL; i += 2)
 1102   {
 1103   /*******************
 1104    * name (optional) *
 1105    *******************/
 1106 
 1107   if (strcmp (attributes[i], "name") == 0)
 1108     {
 1109     if (gamgi_io_token_alpha_scan (attributes[i + 1], 
 1110     layer->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1111       return gamgi_io_error_value (attributes[i + 1], 
 1112       gml->ml.filename, fileline, gml->ml.window);
 1113     }
 1114 
 1115   /*****************
 1116    * id (optional) *
 1117    *****************/
 1118 
 1119   else if (strcmp (attributes[i], "id") == 0)
 1120     {
 1121     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 1122     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1123       return gamgi_io_error_value (attributes[i + 1], 
 1124       gml->ml.filename, fileline, gml->ml.window);
 1125 
 1126     hash = gamgi_math_hash_value (token,
 1127     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 1128 
 1129     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 1130       return gamgi_io_error_multiple (gml->ml.filename, 
 1131       fileline, gml->ml.window);
 1132 
 1133     gamgi_expat_import_id_add (token, gml, hash);
 1134     }
 1135 
 1136   /*********************
 1137    * parent (optional) *
 1138    *********************/
 1139 
 1140   else if (strcmp (attributes[i], "parent") == 0)
 1141     {
 1142     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 1143     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1144       return gamgi_io_error_value (attributes[i + 1], 
 1145       gml->ml.filename, fileline, gml->ml.window);
 1146 
 1147     gamgi_expat_import_ref_add (token, gml);
 1148     }
 1149 
 1150   /*************************************************
 1151    * perspective, top, near, far planes (optional) *
 1152    *************************************************/
 1153 
 1154   else if (strcmp (attributes[i], "perspective") == 0)
 1155     {
 1156     if (gamgi_io_token_bool_scan (attributes[i + 1],
 1157     &layer->perspective) == FALSE)
 1158       return gamgi_io_error_value (attributes[i + 1], 
 1159       gml->ml.filename, fileline, gml->ml.window);
 1160     }
 1161 
 1162   else if (strcmp (attributes[i], "top") == 0)
 1163     {
 1164     if (gamgi_io_token_double_scan (attributes[i + 1], 
 1165     &layer->top, GAMGI_MATH_TOLERANCE_LENGTH, DBL_MAX) == FALSE)
 1166       return gamgi_io_error_value (attributes[i + 1], 
 1167       gml->ml.filename, fileline, gml->ml.window);
 1168     }
 1169 
 1170   else if (strcmp (attributes[i], "near") == 0)
 1171     {
 1172     if (gamgi_io_token_double_scan (attributes[i + 1],
 1173     &layer->near, GAMGI_MATH_TOLERANCE_LENGTH, DBL_MAX) == FALSE)
 1174       return gamgi_io_error_value (attributes[i + 1], 
 1175       gml->ml.filename, fileline, gml->ml.window);
 1176     }
 1177 
 1178   else if (strcmp (attributes[i], "far") == 0)
 1179     {
 1180     if (gamgi_io_token_double_scan (attributes[i + 1],
 1181     &layer->far, GAMGI_MATH_TOLERANCE_LENGTH, DBL_MAX) == FALSE)
 1182       return gamgi_io_error_value (attributes[i + 1], 
 1183       gml->ml.filename, fileline, gml->ml.window);
 1184     }
 1185 
 1186   /**********************************
 1187    * eye_x, eye_y, eye_z (optional) *
 1188    **********************************/
 1189 
 1190   else if (strcmp (attributes[i], "eye_x") == 0)
 1191     {
 1192     eye++;
 1193     if (gamgi_io_token_double_scan (attributes[i + 1],
 1194     &layer->eye[0], -DBL_MAX, DBL_MAX) == FALSE)
 1195       return gamgi_io_error_value (attributes[i + 1], 
 1196       gml->ml.filename, fileline, gml->ml.window);
 1197     }
 1198 
 1199   else if (strcmp (attributes[i], "eye_y") == 0)
 1200     {
 1201     eye++;
 1202     if (gamgi_io_token_double_scan (attributes[i + 1],
 1203     &layer->eye[1], -DBL_MAX, DBL_MAX) == FALSE)
 1204       return gamgi_io_error_value (attributes[i + 1], 
 1205       gml->ml.filename, fileline, gml->ml.window);
 1206     }
 1207 
 1208   else if (strcmp (attributes[i], "eye_z") == 0)
 1209     {
 1210     eye++;
 1211     if (gamgi_io_token_double_scan (attributes[i + 1],
 1212     &layer->eye[2], -DBL_MAX, DBL_MAX) == FALSE)
 1213       return gamgi_io_error_value (attributes[i + 1], 
 1214       gml->ml.filename, fileline, gml->ml.window);
 1215     }
 1216 
 1217   /*******************************************
 1218    * center_x, center_y, center_z (optional) *
 1219    *******************************************/
 1220 
 1221   else if (strcmp (attributes[i], "center_x") == 0)
 1222     {
 1223     center++;
 1224     if (gamgi_io_token_double_scan (attributes[i + 1],
 1225     &layer->center[0], -DBL_MAX, DBL_MAX) == FALSE)
 1226       return gamgi_io_error_value (attributes[i + 1], 
 1227       gml->ml.filename, fileline, gml->ml.window);
 1228     }
 1229 
 1230   else if (strcmp (attributes[i], "center_y") == 0)
 1231     {
 1232     center++;
 1233     if (gamgi_io_token_double_scan (attributes[i + 1],
 1234     &layer->center[1], -DBL_MAX, DBL_MAX) == FALSE)
 1235       return gamgi_io_error_value (attributes[i + 1], 
 1236       gml->ml.filename, fileline, gml->ml.window);
 1237     }
 1238 
 1239   else if (strcmp (attributes[i], "center_z") == 0)
 1240     {
 1241     center++;
 1242     if (gamgi_io_token_double_scan (attributes[i + 1],
 1243     &layer->center[2], -DBL_MAX, DBL_MAX) == FALSE)
 1244       return gamgi_io_error_value (attributes[i + 1], 
 1245       gml->ml.filename, fileline, gml->ml.window);
 1246     }
 1247 
 1248   /*******************************
 1249    * up_x, up_y, up_z (optional) *
 1250    *******************************/
 1251 
 1252   else if (strcmp (attributes[i], "up_x") == 0)
 1253     {
 1254     up++;
 1255     if (gamgi_io_token_double_scan (attributes[i + 1], 
 1256     &layer->up[0], -DBL_MAX, DBL_MAX) == FALSE)
 1257       return gamgi_io_error_value (attributes[i + 1], 
 1258       gml->ml.filename, fileline, gml->ml.window);
 1259     }
 1260 
 1261   else if (strcmp (attributes[i], "up_y") == 0)
 1262     {
 1263     up++;
 1264     if (gamgi_io_token_double_scan (attributes[i + 1], 
 1265     &layer->up[1], -DBL_MAX, DBL_MAX) == FALSE)
 1266       return gamgi_io_error_value (attributes[i + 1], 
 1267       gml->ml.filename, fileline, gml->ml.window);
 1268     }
 1269 
 1270   else if (strcmp (attributes[i], "up_z") == 0)
 1271     {
 1272     up++;
 1273     if (gamgi_io_token_double_scan (attributes[i + 1], 
 1274     &layer->up[2], -DBL_MAX, DBL_MAX) == FALSE)
 1275       return gamgi_io_error_value (attributes[i + 1], 
 1276       gml->ml.filename, fileline, gml->ml.window);
 1277     }
 1278 
 1279    /*******************************************
 1280     * visibility_in,visibility_out (optional) *
 1281     *******************************************/
 1282 
 1283    else if (strcmp (attributes[i], "visibility_in") == 0)
 1284     {
 1285     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 1286     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1287       return gamgi_io_error_value (attributes[i + 1],
 1288       gml->ml.filename, fileline, gml->ml.window);
 1289 
 1290     else if (strcmp (token, "all") == 0)
 1291        layer->visibility_in = GAMGI_GTK_ALL;
 1292     else if (strcmp (token, "partial") == 0)
 1293        layer->visibility_in = GAMGI_GTK_PARTIAL;
 1294     else if (strcmp (token, "none") == 0)
 1295        layer->visibility_in = GAMGI_GTK_NONE;
 1296     else return gamgi_io_error_value (attributes[i + 1],
 1297       gml->ml.filename, fileline, gml->ml.window);
 1298     }
 1299 
 1300   else if (strcmp (attributes[i], "visibility_out") == 0)
 1301     {
 1302     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 1303     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1304       return gamgi_io_error_value (attributes[i + 1],
 1305       gml->ml.filename, fileline, gml->ml.window);
 1306 
 1307     else if (strcmp (token, "all") == 0)
 1308        layer->visibility_out = GAMGI_GTK_ALL;
 1309     else if (strcmp (token, "partial") == 0)
 1310        layer->visibility_out = GAMGI_GTK_PARTIAL;
 1311     else if (strcmp (token, "none") == 0)
 1312        layer->visibility_out = GAMGI_GTK_NONE;
 1313     else return gamgi_io_error_value (attributes[i + 1],
 1314       gml->ml.filename, fileline, gml->ml.window);
 1315     }
 1316 
 1317   /*****************************
 1318    * red,green,blue (optional) *
 1319    *****************************/
 1320 
 1321   else if (strcmp (attributes[i], "red") == 0)
 1322     {
 1323     color++;
 1324     if (gamgi_io_token_float_scan (attributes[i + 1],
 1325     &layer->color[0], 0.0, 1.0) == FALSE)
 1326       return gamgi_io_error_value (attributes[i + 1], 
 1327       gml->ml.filename, fileline, gml->ml.window);
 1328     }
 1329 
 1330   else if (strcmp (attributes[i], "green") == 0)
 1331     {
 1332     color++;
 1333     if (gamgi_io_token_float_scan (attributes[i + 1],
 1334     &layer->color[1], 0.0, 1.0) == FALSE)
 1335       return gamgi_io_error_value (attributes[i + 1], 
 1336       gml->ml.filename, fileline, gml->ml.window);
 1337     }
 1338 
 1339   else if (strcmp (attributes[i], "blue") == 0)
 1340     {
 1341     color++;
 1342     if (gamgi_io_token_float_scan (attributes[i + 1],
 1343     &layer->color[2], 0.0, 1.0) == FALSE)
 1344       return gamgi_io_error_value (attributes[i + 1], 
 1345       gml->ml.filename, fileline, gml->ml.window);
 1346     }
 1347 
 1348   /*******************
 1349    * axes (optional) *
 1350    *******************/
 1351 
 1352   else if (strcmp (attributes[i], "axes") == 0)
 1353     {
 1354     if (gamgi_io_token_bool_scan (attributes[i + 1],
 1355     &layer->axes) == FALSE)
 1356       return gamgi_io_error_value (attributes[i + 1], 
 1357       gml->ml.filename, fileline, gml->ml.window);
 1358     }
 1359 
 1360   /********************
 1361    * scale (optional) *
 1362    ********************/
 1363 
 1364   else if (strcmp (attributes[i], "scale") == 0)
 1365     {
 1366     if (gamgi_io_token_double_scan (attributes[i + 1],
 1367     &layer->object.scale, GAMGI_MESA_SCALE_LOWER,
 1368     GAMGI_MESA_SCALE_UPPER) == FALSE)
 1369       return gamgi_io_error_value (attributes[i + 1], 
 1370       gml->ml.filename, fileline, gml->ml.window);
 1371     }
 1372 
 1373   else return gamgi_io_error_attribute (attributes[i], 
 1374   gml->ml.filename, fileline, gml->ml.window);
 1375   }
 1376 
 1377 /***********************************
 1378  * global: incompatible attributes *
 1379  ***********************************/
 1380 
 1381 if (layer->near + GAMGI_MATH_TOLERANCE_LENGTH > layer->far)
 1382   return gamgi_io_error_incompatible (gml->ml.filename,
 1383   fileline, gml->ml.window);
 1384 
 1385 if (eye == 1 || eye == 2)
 1386   return gamgi_io_error_incompatible (gml->ml.filename, 
 1387   fileline, gml->ml.window);
 1388 
 1389 if (center == 1 || center == 2) 
 1390   return gamgi_io_error_incompatible (gml->ml.filename, 
 1391   fileline, gml->ml.window);
 1392 
 1393 if (up == 1 || up == 2) 
 1394   return gamgi_io_error_incompatible (gml->ml.filename, 
 1395   fileline, gml->ml.window);
 1396 
 1397 if (color == 1 || color == 2)
 1398   return gamgi_io_error_incompatible (gml->ml.filename, 
 1399   fileline, gml->ml.window);
 1400 
 1401 /************************************************************
 1402  * check view parameters:                                   *
 1403  * 1) the distance eye-center must be in the near-far range *
 1404  * 2) the up component normal to eye-center cannot be zero  *
 1405  ************************************************************/
 1406 
 1407 gamgi_math_vector_sub (layer->eye, layer->center, z);
 1408 length = gamgi_math_vector_length (z);
 1409 if (length < layer->near || length > layer->far)
 1410   return gamgi_io_error_incompatible (gml->ml.filename,
 1411   fileline, gml->ml.window);
 1412 gamgi_math_vector_normal (z);
 1413 
 1414 length = gamgi_math_vector_dot (layer->up, z);
 1415 gamgi_math_vector_scale (z, up_z, length);
 1416 gamgi_math_vector_sub (layer->up, up_z, layer->up);
 1417 length = gamgi_math_vector_length (layer->up);
 1418 if (length < GAMGI_MATH_TOLERANCE_LENGTH)
 1419   return gamgi_io_error_incompatible (gml->ml.filename,
 1420   fileline, gml->ml.window);
 1421 gamgi_math_vector_normal (layer->up);
 1422 
 1423 gamgi_math_vector_cross (layer->up, z, x);
 1424 gamgi_math_matrix_absolute (layer->referential,
 1425 x[0], layer->up[0], z[0], x[1], layer->up[1], z[1], x[2], layer->up[2], z[2]);
 1426 
 1427 return gml->ml.valid;
 1428 }
 1429 
 1430 static gamgi_bool static_light_end (const char *element, gamgi_gml *gml)
 1431 {
 1432 /**********************
 1433  * handle parent list *
 1434  **********************/
 1435 
 1436 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 1437 
 1438 return gml->ml.valid;
 1439 }
 1440 
 1441 static gamgi_bool static_light_start (const char *element,
 1442 const char **attributes, gamgi_gml *gml)
 1443 {
 1444 gamgi_light *light;
 1445 gamgi_light_class *light_class;
 1446 gamgi_object *object;
 1447 char token[GAMGI_ENGINE_TOKEN];
 1448 int fileline, i;
 1449 int ref = 0;
 1450 int direction = 0, position = 0;
 1451 int ambient = 0, diffuse = 0, specular = 0;
 1452 int constant = 0, linear = 0, quadratic = 0, radial = 0;
 1453 unsigned int hash;
 1454 
 1455 /******************************************
 1456  * used only in error messages: file line *
 1457  * indicates where current element starts *
 1458  ******************************************/
 1459 
 1460 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 1461 
 1462 /***********************
 1463  * parent stack: check *
 1464  ***********************/
 1465 
 1466 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_LIGHT, gml) == FALSE)
 1467   return gamgi_io_error_child (gml->ml.filename, 
 1468   fileline, gml->ml.window);
 1469 
 1470 /*************************************************
 1471  * light_class contains the current config info, *
 1472  * which is gamgi->light unless new config info  *
 1473  * has been previously stored in object->object. *
 1474  *************************************************/
 1475 
 1476 object = GAMGI_CAST_OBJECT gamgi->light;
 1477 if (object->object == NULL) light_class = gamgi->light;
 1478 else light_class = (gamgi_light_class *) object->object;
 1479 
 1480 /******************
 1481  * object: create *
 1482  ******************/
 1483 
 1484 light = gamgi_engine_create_light ();
 1485 gamgi_engine_start_light (light);
 1486 gamgi_mesa_start_light (light, light_class);
 1487 
 1488 /***********************************************************
 1489  * by default, each new light belongs to the current layer *
 1490  ***********************************************************/
 1491 
 1492 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 1493 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 1494 light->object.object = object;
 1495 
 1496 /************************
 1497  * object queue: update *
 1498  ************************/
 1499 
 1500 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 1501 gml->object_end->data = light;
 1502 
 1503 /************************
 1504  * parent stack: update *
 1505  ************************/
 1506 
 1507 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 1508 gml->ml.parent->data = light;
 1509 
 1510 /**********************************
 1511  * attribute array: check, update *
 1512  **********************************/
 1513 
 1514 for (i = 0; attributes[i] != NULL; i += 2)
 1515   {
 1516   /*******************
 1517    * name (optional) *
 1518    *******************/
 1519 
 1520   if (strcmp (attributes[i], "name") == 0)
 1521     {
 1522     if (gamgi_io_token_alpha_scan (attributes[i + 1], 
 1523     light->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE) 
 1524       return gamgi_io_error_value (attributes[i + 1], 
 1525       gml->ml.filename, fileline, gml->ml.window);
 1526     }
 1527 
 1528   /*****************
 1529    * id (optional) *
 1530    *****************/
 1531 
 1532   else if (strcmp (attributes[i], "id") == 0)
 1533     {
 1534     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 1535     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1536       return gamgi_io_error_value (attributes[i + 1],
 1537       gml->ml.filename, fileline, gml->ml.window);
 1538 
 1539     hash = gamgi_math_hash_value (token,
 1540     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 1541 
 1542     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 1543       return gamgi_io_error_multiple (gml->ml.filename,
 1544       fileline, gml->ml.window);
 1545 
 1546     gamgi_expat_import_id_add (token, gml, hash);
 1547     }
 1548 
 1549   /*********************
 1550    * parent (optional) *
 1551    *********************/
 1552 
 1553   else if (strcmp (attributes[i], "parent") == 0)
 1554     {
 1555     ref++;
 1556     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 1557     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1558       return gamgi_io_error_value (attributes[i + 1], 
 1559       gml->ml.filename, fileline, gml->ml.window);
 1560 
 1561     gamgi_expat_import_ref_add (token, gml);
 1562     }
 1563 
 1564   /***************************************
 1565    * red,green,blue ambient (coupled,    *
 1566    * one type required, others optional) *
 1567    ***************************************/
 1568 
 1569   else if (strcmp (attributes[i], "ambient_r") == 0)
 1570     {
 1571     ambient++;
 1572     if (gamgi_io_token_float_scan (attributes[i + 1],
 1573     &light->ambient[0], 0.0, 1.0) == FALSE)
 1574       return gamgi_io_error_value (attributes[i + 1], 
 1575       gml->ml.filename, fileline, gml->ml.window);
 1576     }
 1577 
 1578   else if (strcmp (attributes[i], "ambient_g") == 0)
 1579     {
 1580     ambient++;
 1581     if (gamgi_io_token_float_scan (attributes[i + 1],
 1582     &light->ambient[1], 0.0, 1.0) == FALSE)
 1583       return gamgi_io_error_value (attributes[i + 1], 
 1584       gml->ml.filename, fileline, gml->ml.window);
 1585     }
 1586 
 1587   else if (strcmp (attributes[i], "ambient_b") == 0)
 1588     {
 1589     ambient++;
 1590     if (gamgi_io_token_float_scan (attributes[i + 1],
 1591     &light->ambient[2], 0.0, 1.0) == FALSE)
 1592       return gamgi_io_error_value (attributes[i + 1], 
 1593       gml->ml.filename, fileline, gml->ml.window);
 1594     }
 1595 
 1596   /***************************************
 1597    * red,green,blue diffuse (coupled,    *
 1598    * one type required, others optional) *
 1599    ***************************************/
 1600 
 1601   else if (strcmp (attributes[i], "diffuse_r") == 0)
 1602     {
 1603     diffuse++;
 1604     if (gamgi_io_token_float_scan (attributes[i + 1],
 1605     &light->diffuse[0], 0.0, 1.0) == FALSE)
 1606       return gamgi_io_error_value (attributes[i + 1], 
 1607       gml->ml.filename, fileline, gml->ml.window);
 1608     }
 1609 
 1610   else if (strcmp (attributes[i], "diffuse_g") == 0)
 1611     {
 1612     diffuse++;
 1613     if (gamgi_io_token_float_scan (attributes[i + 1],
 1614     &light->diffuse[1], 0.0, 1.0) == FALSE)
 1615       return gamgi_io_error_value (attributes[i + 1], 
 1616       gml->ml.filename, fileline, gml->ml.window);
 1617     }
 1618 
 1619   else if (strcmp (attributes[i], "diffuse_b") == 0)
 1620     {
 1621     diffuse++;
 1622     if (gamgi_io_token_float_scan (attributes[i + 1],
 1623     &light->diffuse[2], 0.0, 1.0) == FALSE)
 1624       return gamgi_io_error_value (attributes[i + 1], 
 1625       gml->ml.filename, fileline, gml->ml.window);
 1626     }
 1627 
 1628   /***************************************
 1629    * red,green,blue specular (coupled,   *
 1630    * one type required, others optional) *
 1631    ***************************************/
 1632 
 1633   else if (strcmp (attributes[i], "specular_r") == 0)
 1634     {
 1635     specular++;
 1636     if (gamgi_io_token_float_scan (attributes[i + 1],
 1637     &light->specular[0], 0.0, 1.0) == FALSE)
 1638       return gamgi_io_error_value (attributes[i + 1], 
 1639       gml->ml.filename, fileline, gml->ml.window);
 1640     }
 1641 
 1642   else if (strcmp (attributes[i], "specular_g") == 0)
 1643     {
 1644     specular++;
 1645     if (gamgi_io_token_float_scan (attributes[i + 1],
 1646     &light->specular[1], 0.0, 1.0) == FALSE)
 1647       return gamgi_io_error_value (attributes[i + 1], 
 1648       gml->ml.filename, fileline, gml->ml.window);
 1649     }
 1650 
 1651   else if (strcmp (attributes[i], "specular_b") == 0)
 1652     {
 1653     specular++;
 1654     if (gamgi_io_token_float_scan (attributes[i + 1],
 1655     &light->specular[2], 0.0, 1.0) == FALSE)
 1656       return gamgi_io_error_value (attributes[i + 1], 
 1657       gml->ml.filename, fileline, gml->ml.window);
 1658     }
 1659 
 1660    /*****************************************
 1661    * geometry: direction, position, angle   *
 1662    * (coupled, required, according to type) *
 1663    ******************************************/
 1664 
 1665   else if (strcmp (attributes[i], "direction_x") == 0)
 1666     {
 1667     direction++;
 1668     if (gamgi_io_token_float_scan (attributes[i + 1],
 1669     &light->direction[0], -FLT_MAX, FLT_MAX) == FALSE)
 1670       return gamgi_io_error_value (attributes[i + 1], 
 1671       gml->ml.filename, fileline, gml->ml.window);
 1672     }
 1673 
 1674   else if (strcmp (attributes[i], "direction_y") == 0)
 1675     {
 1676     direction++;
 1677     if (gamgi_io_token_float_scan (attributes[i + 1],
 1678     &light->direction[1], -FLT_MAX, FLT_MAX) == FALSE)
 1679       return gamgi_io_error_value (attributes[i + 1], 
 1680       gml->ml.filename, fileline, gml->ml.window);
 1681     }
 1682 
 1683   else if (strcmp (attributes[i], "direction_z") == 0)
 1684     {
 1685     direction++;
 1686     if (gamgi_io_token_float_scan (attributes[i + 1],
 1687     &light->direction[2], -FLT_MAX, FLT_MAX) == FALSE)
 1688       return gamgi_io_error_value (attributes[i + 1], 
 1689       gml->ml.filename, fileline, gml->ml.window);
 1690     }
 1691 
 1692   else if (strcmp (attributes[i], "position_x") == 0)
 1693     {
 1694     position++;
 1695     if (gamgi_io_token_float_scan (attributes[i + 1],
 1696     &light->position[0], -FLT_MAX, FLT_MAX) == FALSE)
 1697       return gamgi_io_error_value (attributes[i + 1], 
 1698       gml->ml.filename, fileline, gml->ml.window);
 1699     }
 1700 
 1701   else if (strcmp (attributes[i], "position_y") == 0)
 1702     {
 1703     position++;
 1704     if (gamgi_io_token_float_scan (attributes[i + 1],
 1705     &light->position[1], -FLT_MAX, FLT_MAX) == FALSE)
 1706       return gamgi_io_error_value (attributes[i + 1], 
 1707       gml->ml.filename, fileline, gml->ml.window);
 1708     }
 1709 
 1710   else if (strcmp (attributes[i], "position_z") == 0)
 1711     {
 1712     position++;
 1713     if (gamgi_io_token_float_scan (attributes[i + 1],
 1714     &light->position[2], -FLT_MAX, FLT_MAX) == FALSE)
 1715       return gamgi_io_error_value (attributes[i + 1], 
 1716       gml->ml.filename, fileline, gml->ml.window);
 1717     }
 1718 
 1719   else if (strcmp (attributes[i], "angle") == 0)
 1720     {
 1721     if (gamgi_io_token_float_scan (attributes[i + 1],
 1722     &light->angle, 0.0, 90.0) == FALSE)
 1723       return gamgi_io_error_value (attributes[i + 1], 
 1724       gml->ml.filename, fileline, gml->ml.window);
 1725     }
 1726 
 1727   /***************************************************************
 1728    * attenuation: constant, linear, quadratic, radial (optional) *
 1729    ***************************************************************/
 1730 
 1731   else if (strcmp (attributes[i], "constant") == 0)
 1732     {
 1733     constant++;
 1734     if (gamgi_io_token_float_scan (attributes[i + 1],
 1735     &light->constant, 0.0, FLT_MAX) == FALSE)
 1736       return gamgi_io_error_value (attributes[i + 1], 
 1737       gml->ml.filename, fileline, gml->ml.window);
 1738     }
 1739 
 1740   else if (strcmp (attributes[i], "linear") == 0)
 1741     {
 1742     linear++;
 1743     if (gamgi_io_token_float_scan (attributes[i + 1],
 1744     &light->linear, 0.0, FLT_MAX) == FALSE)
 1745       return gamgi_io_error_value (attributes[i + 1], 
 1746       gml->ml.filename, fileline, gml->ml.window);
 1747     }
 1748 
 1749   else if (strcmp (attributes[i], "quadratic") == 0)
 1750     {
 1751     quadratic++;
 1752     if (gamgi_io_token_float_scan (attributes[i + 1],
 1753     &light->quadratic, 0.0, FLT_MAX) == FALSE)
 1754       return gamgi_io_error_value (attributes[i + 1], 
 1755       gml->ml.filename, fileline, gml->ml.window);
 1756     }
 1757 
 1758   else if (strcmp (attributes[i], "radial") == 0)
 1759     {
 1760     radial++;
 1761     if (gamgi_io_token_float_scan (attributes[i + 1],
 1762     &light->radial, 0.0, FLT_MAX) == FALSE)
 1763       return gamgi_io_error_value (attributes[i + 1], 
 1764       gml->ml.filename, fileline, gml->ml.window);
 1765     }
 1766 
 1767   else return gamgi_io_error_attribute (attributes[i], 
 1768   gml->ml.filename, fileline, gml->ml.window);
 1769   }
 1770 
 1771 /******************************
 1772  * global: missing attributes *
 1773  ******************************/
 1774 
 1775 if (light->ambient[0] + light->ambient[1] + light->ambient[2] == 0
 1776 && light->diffuse[0] + light->diffuse[1] + light->diffuse[2] == 0
 1777 && light->specular[0] + light->specular[1] + light->specular[2] == 0)
 1778   return gamgi_io_error_missing (gml->ml.filename, 
 1779   fileline, gml->ml.window);
 1780 
 1781 if (ambient == 1 || diffuse == 1 || specular == 1 ||
 1782 ambient == 2 || diffuse == 2 || specular == 2)
 1783   return gamgi_io_error_missing (gml->ml.filename, 
 1784   fileline, gml->ml.window);
 1785 
 1786 if (position == 0 && direction == 0)
 1787   return gamgi_io_error_missing (gml->ml.filename, 
 1788   fileline, gml->ml.window);
 1789 
 1790 if (position == 1 || direction == 1 ||
 1791 position == 2 || direction == 2)
 1792   return gamgi_io_error_missing (gml->ml.filename, 
 1793   fileline, gml->ml.window);
 1794 
 1795 /******************************
 1796  * global: default attributes *
 1797  ******************************/
 1798 
 1799 if (position == 3) light->position[3] = 1.0;
 1800 else
 1801   {
 1802   /***************************************************
 1803    * OpenGL saves geometric data for directional and *
 1804    * positional lights in the same array (position), *
 1805    * using only a second array (direction) for spot  *
 1806    * lights. For directional lights, the direction   *
 1807    * data, collected in the direction array, must    *
 1808    * therefore be transfered to the position array   *
 1809    * and the direction array must be re-initialised. *
 1810    ***************************************************/
 1811 
 1812   /************************************************************
 1813    * invert the vector direction, so it has the same physical *
 1814    * meaning in directional and positional lights, always     *
 1815    * pointing from the light source to the light destination  *
 1816    ************************************************************/
 1817 
 1818   light->position[0] = -light->direction[0];
 1819   light->position[1] = -light->direction[1];
 1820   light->position[2] = -light->direction[2];
 1821 
 1822   light->position[3] = 0.0;
 1823 
 1824   light->direction[0] = GAMGI_MESA_LIGHT_DIRECTION_X;
 1825   light->direction[1] = GAMGI_MESA_LIGHT_DIRECTION_Y;
 1826   light->direction[2] = GAMGI_MESA_LIGHT_DIRECTION_Z;
 1827   }
 1828 
 1829 /***********************************
 1830  * global: incompatible attributes *
 1831  ***********************************/
 1832 
 1833 if (position == 0 &&
 1834 (light->angle != 180.0 || constant > 0
 1835 || linear > 0 || quadratic || radial > 0))
 1836   return gamgi_io_error_incompatible (gml->ml.filename, 
 1837   fileline, gml->ml.window);
 1838 
 1839 if (position == 3 && light->angle == 180.0 &&
 1840 (direction > 0 || radial > 0))
 1841   return gamgi_io_error_incompatible (gml->ml.filename, 
 1842   fileline, gml->ml.window);
 1843 
 1844 if (light->direction[0] == 0.0 && 
 1845 light->direction[1] == 0.0 && light->direction[2] == 0.0)
 1846   return gamgi_io_error_incompatible (gml->ml.filename, 
 1847   fileline, gml->ml.window);
 1848 
 1849  /***************************************************
 1850   * each layer cannot have more than gamgi->lights  *
 1851   * (at least 8, as defined by OpenGL).             *
 1852   *                                                 *
 1853   * layer->light is automatically updated everytime *
 1854   * layer is rendered, so even if an error is found *
 1855   * there is no danger of corrupting the layer data *
 1856   ***************************************************/
 1857 
 1858 if (gamgi->max_lights == (GAMGI_CAST_LAYER light->object.object)->light++)
 1859   return gamgi_io_error_resources (gml->ml.filename, 
 1860   fileline, gml->ml.window);
 1861 
 1862 return gml->ml.valid;
 1863 }
 1864 
 1865 static gamgi_bool static_assembly_end (const char *element, gamgi_gml *gml)
 1866 {
 1867 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 1868 
 1869 return gml->ml.valid;
 1870 }
 1871 
 1872 static gamgi_bool static_assembly_start (const char *element,
 1873 const char **attributes, gamgi_gml *gml)
 1874 {
 1875 gamgi_assembly *assembly;
 1876 gamgi_assembly_class *assembly_class;
 1877 gamgi_object *object;
 1878 char token[GAMGI_ENGINE_TOKEN];
 1879 int fileline, i;
 1880 unsigned int hash;
 1881 
 1882 /******************************************
 1883  * used only in error messages: file line *
 1884  * indicates where current element starts *
 1885  ******************************************/
 1886 
 1887 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 1888 
 1889 /******************************
 1890  * parent list: check, update *
 1891  ******************************/
 1892 
 1893 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_ASSEMBLY, gml) == FALSE)
 1894   return gamgi_io_error_child (gml->ml.filename, 
 1895   fileline, gml->ml.window);
 1896 
 1897 /****************************************************
 1898  * assembly_class contains the current config info, *
 1899  * which is gamgi->assembly unless new config info  *
 1900  * has been previously stored in object->object.    *
 1901  ****************************************************/
 1902 
 1903 object = GAMGI_CAST_OBJECT gamgi->assembly;
 1904 if (object->object == NULL) assembly_class = gamgi->assembly;
 1905 else assembly_class = (gamgi_assembly_class *) object->object;
 1906 
 1907 /*****************
 1908  * object create *
 1909  *****************/
 1910 
 1911 assembly = gamgi_engine_create_assembly ();
 1912 gamgi_engine_start_assembly (assembly);
 1913 gamgi_mesa_start_assembly (assembly, assembly_class);
 1914 
 1915 /**************************************************************
 1916  * by default, each new assembly belongs to the current layer *
 1917  **************************************************************/
 1918 
 1919 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 1920 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 1921 assembly->object.object = object;
 1922 
 1923 /************************
 1924  * object queue: update *
 1925  ************************/
 1926 
 1927 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 1928 gml->object_end->data = assembly;
 1929 
 1930 /************************
 1931  * parent stack: update *
 1932  ************************/
 1933 
 1934 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 1935 gml->ml.parent->data = assembly;
 1936 
 1937 /**********************************
 1938  * attribute array: check, update *
 1939  **********************************/
 1940 
 1941 for (i = 0; attributes[i] != NULL; i += 2)
 1942   {
 1943   /********
 1944    * name *
 1945    ********/
 1946 
 1947   if (strcmp (attributes[i], "name") == 0)
 1948     {
 1949     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 1950     assembly->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1951       return gamgi_io_error_value (attributes[i + 1], 
 1952       gml->ml.filename, fileline, gml->ml.window);
 1953     }
 1954 
 1955   /*****************
 1956    * id (optional) *
 1957    *****************/
 1958 
 1959   else if (strcmp (attributes[i], "id") == 0)
 1960     {
 1961     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 1962     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1963       return gamgi_io_error_value (attributes[i + 1], 
 1964       gml->ml.filename, fileline, gml->ml.window);
 1965 
 1966     hash = gamgi_math_hash_value (token,
 1967     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 1968 
 1969     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 1970       return gamgi_io_error_multiple (gml->ml.filename, 
 1971       fileline, gml->ml.window);
 1972 
 1973     gamgi_expat_import_id_add (token, gml, hash);
 1974     }
 1975 
 1976   /*********************
 1977    * parent (optional) *
 1978    *********************/
 1979 
 1980   else if (strcmp (attributes[i], "parent") == 0)
 1981     {
 1982     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 1983     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 1984       return gamgi_io_error_value (attributes[i + 1], 
 1985       gml->ml.filename, fileline, gml->ml.window);
 1986 
 1987     gamgi_expat_import_ref_add (token, gml);
 1988     }
 1989 
 1990   /********************
 1991    * scale (optional) *
 1992    ********************/
 1993 
 1994   else if (strcmp (attributes[i], "scale") == 0)
 1995     {
 1996     if (gamgi_io_token_double_scan (attributes[i + 1],
 1997     &assembly->object.scale, GAMGI_MESA_SCALE_LOWER,
 1998     GAMGI_MESA_SCALE_UPPER) == FALSE)
 1999       return gamgi_io_error_value (attributes[i + 1], 
 2000       gml->ml.filename, fileline, gml->ml.window);
 2001     }
 2002 
 2003   else return gamgi_io_error_attribute (attributes[i], 
 2004   gml->ml.filename, fileline, gml->ml.window);
 2005   }
 2006 
 2007 /************************************
 2008  * global tests: missing attributes *
 2009  ************************************/
 2010 
 2011 /*****************************************
 2012  * global tests: incompatible attributes *
 2013  *****************************************/
 2014 
 2015 return gml->ml.valid;
 2016 }
 2017 
 2018 static gamgi_bool static_graph_end (const char *element, gamgi_gml *gml)
 2019 {
 2020 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 2021 
 2022 return gml->ml.valid;
 2023 }
 2024 
 2025 static gamgi_bool static_graph_start (const char *element,
 2026 const char **attributes, gamgi_gml *gml)
 2027 {
 2028 gamgi_graph *graph;
 2029 gamgi_graph_class *graph_class;
 2030 gamgi_object *object;
 2031 char token[GAMGI_ENGINE_TOKEN];
 2032 int fileline, i;
 2033 unsigned int hash;
 2034 
 2035 /******************************************
 2036  * used only in error messages: file line *
 2037  * indicates where current element starts *
 2038  ******************************************/
 2039 
 2040 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 2041 
 2042 /******************************
 2043  * parent list: check, update *
 2044  ******************************/
 2045 
 2046 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_GRAPH, gml) == FALSE)
 2047   return gamgi_io_error_child (gml->ml.filename, 
 2048   fileline, gml->ml.window);
 2049 
 2050 /*************************************************
 2051  * graph_class contains the current config info, *
 2052  * which is gamgi->graph unless new config info  *
 2053  * has been previously stored in object->object. *
 2054  *************************************************/
 2055 
 2056 object = GAMGI_CAST_OBJECT gamgi->graph;
 2057 if (object->object == NULL) graph_class = gamgi->graph;
 2058 else graph_class = (gamgi_graph_class *) object->object;
 2059 
 2060 /*****************
 2061  * object create *
 2062  *****************/
 2063 
 2064 graph = gamgi_engine_create_graph ();
 2065 gamgi_engine_start_graph (graph);
 2066 gamgi_mesa_start_graph (graph, graph_class);
 2067 
 2068 /***********************************************************
 2069  * by default, each new graph belongs to the current layer *
 2070  ***********************************************************/
 2071 
 2072 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 2073 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 2074 graph->object.object = object;
 2075 
 2076 /************************
 2077  * object queue: update *
 2078  ************************/
 2079 
 2080 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 2081 gml->object_end->data = graph;
 2082 
 2083 /************************
 2084  * parent stack: update *
 2085  ************************/
 2086 
 2087 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 2088 gml->ml.parent->data = graph;
 2089 
 2090 /**********************************
 2091  * attribute array: check, update *
 2092  **********************************/
 2093 
 2094 for (i = 0; attributes[i] != NULL; i += 2)
 2095   {
 2096   /*******************
 2097    * name (optional) *
 2098    *******************/
 2099 
 2100   if (strcmp (attributes[i], "name") == 0)
 2101     {
 2102     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2103     graph->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2104       return gamgi_io_error_value (attributes[i + 1], 
 2105       gml->ml.filename, fileline, gml->ml.window);
 2106     }
 2107 
 2108   /*****************
 2109    * id (optional) *
 2110    *****************/
 2111 
 2112   else if (strcmp (attributes[i], "id") == 0)
 2113     {
 2114     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2115     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2116       return gamgi_io_error_value (attributes[i + 1], 
 2117       gml->ml.filename, fileline, gml->ml.window);
 2118 
 2119     hash = gamgi_math_hash_value (token,
 2120     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 2121 
 2122     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 2123       return gamgi_io_error_multiple (gml->ml.filename, 
 2124       fileline, gml->ml.window);
 2125 
 2126     gamgi_expat_import_id_add (token, gml, hash);
 2127     }
 2128 
 2129   /*********************
 2130    * parent (optional) *
 2131    *********************/
 2132 
 2133   else if (strcmp (attributes[i], "parent") == 0)
 2134     {
 2135     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2136     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2137       return gamgi_io_error_value (attributes[i + 1], 
 2138       gml->ml.filename, fileline, gml->ml.window);
 2139 
 2140     gamgi_expat_import_ref_add (token, gml);
 2141     }
 2142 
 2143   /********************
 2144    * scale (optional) *
 2145    ********************/
 2146 
 2147   else if (strcmp (attributes[i], "scale") == 0)
 2148     {
 2149     if (gamgi_io_token_double_scan (attributes[i + 1],
 2150     &graph->object.scale, GAMGI_MESA_SCALE_LOWER,
 2151     GAMGI_MESA_SCALE_UPPER) == FALSE)
 2152       return gamgi_io_error_value (attributes[i + 1], 
 2153       gml->ml.filename, fileline, gml->ml.window);
 2154     }
 2155 
 2156   else return gamgi_io_error_attribute (attributes[i], 
 2157   gml->ml.filename, fileline, gml->ml.window);
 2158   }
 2159 
 2160 /************************************
 2161  * global tests: missing attributes *
 2162  ************************************/
 2163 
 2164 /*****************************************
 2165  * global tests: incompatible attributes *
 2166  *****************************************/
 2167 
 2168 return gml->ml.valid;
 2169 }
 2170 
 2171 static gamgi_bool static_shape_end (const char *element, gamgi_gml *gml)
 2172 {
 2173 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 2174 
 2175 return gml->ml.valid;
 2176 }
 2177 
 2178 static gamgi_bool static_shape_start (const char *element,
 2179 const char **attributes, gamgi_gml *gml)
 2180 {
 2181 gamgi_shape *shape;
 2182 gamgi_shape_class *shape_class;
 2183 gamgi_object *object;
 2184 char token[GAMGI_ENGINE_TOKEN];
 2185 int fileline, i;
 2186 unsigned int hash;
 2187 
 2188 /******************************************
 2189  * used only in error messages: file line *
 2190  * indicates where current element starts *
 2191  ******************************************/
 2192 
 2193 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 2194 
 2195 /******************************
 2196  * parent list: check, update *
 2197  ******************************/
 2198 
 2199 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_SHAPE, gml) == FALSE)
 2200   return gamgi_io_error_child (gml->ml.filename, 
 2201   fileline, gml->ml.window);
 2202 
 2203 /*************************************************
 2204  * shape_class contains the current config info, *
 2205  * which is gamgi->shape unless new config info  *
 2206  * has been previously stored in object->object. *
 2207  *************************************************/
 2208 
 2209 object = GAMGI_CAST_OBJECT gamgi->shape;
 2210 if (object->object == NULL) shape_class = gamgi->shape;
 2211 else shape_class = (gamgi_shape_class *) object->object;
 2212 
 2213 /*****************
 2214  * object create *
 2215  *****************/
 2216 
 2217 shape = gamgi_engine_create_shape ();
 2218 gamgi_engine_start_shape (shape);
 2219 gamgi_mesa_start_shape (shape, shape_class);
 2220 
 2221 /***********************************************************
 2222  * by default, each new shape belongs to the current layer *
 2223  ***********************************************************/
 2224 
 2225 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 2226 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 2227 shape->object.object = object;
 2228 
 2229 /************************
 2230  * object queue: update *
 2231  ************************/
 2232 
 2233 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 2234 gml->object_end->data = shape;
 2235 
 2236 /************************
 2237  * parent stack: update *
 2238  ************************/
 2239 
 2240 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 2241 gml->ml.parent->data = shape;
 2242 
 2243 /**********************************
 2244  * attribute array: check, update *
 2245  **********************************/
 2246 
 2247 for (i = 0; attributes[i] != NULL; i += 2)
 2248   {
 2249   /*******************
 2250    * name (optional) *
 2251    *******************/
 2252 
 2253   if (strcmp (attributes[i], "name") == 0)
 2254     {
 2255     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2256     shape->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2257       return gamgi_io_error_value (attributes[i + 1], 
 2258       gml->ml.filename, fileline, gml->ml.window);
 2259     }
 2260 
 2261   /*****************
 2262    * id (optional) *
 2263    *****************/
 2264 
 2265   else if (strcmp (attributes[i], "id") == 0)
 2266     {
 2267     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2268     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2269       return gamgi_io_error_value (attributes[i + 1], 
 2270       gml->ml.filename, fileline, gml->ml.window);
 2271 
 2272     hash = gamgi_math_hash_value (token,
 2273     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 2274 
 2275     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 2276       return gamgi_io_error_multiple (gml->ml.filename, 
 2277       fileline, gml->ml.window);
 2278 
 2279     gamgi_expat_import_id_add (token, gml, hash);
 2280     }
 2281 
 2282   /*********************
 2283    * parent (optional) *
 2284    *********************/
 2285 
 2286   else if (strcmp (attributes[i], "parent") == 0)
 2287     {
 2288     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2289     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2290       return gamgi_io_error_value (attributes[i + 1], 
 2291       gml->ml.filename, fileline, gml->ml.window);
 2292 
 2293     gamgi_expat_import_ref_add (token, gml);
 2294     }
 2295 
 2296   /********************
 2297    * scale (optional) *
 2298    ********************/
 2299 
 2300   else if (strcmp (attributes[i], "scale") == 0)
 2301     {
 2302     if (gamgi_io_token_double_scan (attributes[i + 1],
 2303     &shape->object.scale, GAMGI_MESA_SCALE_LOWER,
 2304     GAMGI_MESA_SCALE_UPPER) == FALSE)
 2305       return gamgi_io_error_value (attributes[i + 1], 
 2306       gml->ml.filename, fileline, gml->ml.window);
 2307     }
 2308 
 2309   else return gamgi_io_error_attribute (attributes[i], 
 2310   gml->ml.filename, fileline, gml->ml.window);
 2311   }
 2312 
 2313 /************************************
 2314  * global tests: missing attributes *
 2315  ************************************/
 2316 
 2317 /*****************************************
 2318  * global tests: incompatible attributes *
 2319  *****************************************/
 2320 
 2321 return gml->ml.valid;
 2322 }
 2323 
 2324 static gamgi_bool static_arrow_end (const char *element, gamgi_gml *gml)
 2325 {
 2326 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 2327 
 2328 return gml->ml.valid;
 2329 }
 2330 
 2331 static gamgi_bool static_arrow_start (const char *element,
 2332 const char **attributes, gamgi_gml *gml)
 2333 {
 2334 gamgi_arrow *arrow;
 2335 gamgi_arrow_class *arrow_class;
 2336 gamgi_object *object;
 2337 char token[GAMGI_ENGINE_TOKEN];
 2338 int fileline, i;
 2339 unsigned int hash;
 2340 
 2341 /******************************************
 2342  * used only in error messages: file line *
 2343  * indicates where current element starts *
 2344  ******************************************/
 2345 
 2346 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 2347 
 2348 /******************************
 2349  * parent list: check, update *
 2350  ******************************/
 2351 
 2352 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_ARROW, gml) == FALSE)
 2353   return gamgi_io_error_child (gml->ml.filename, 
 2354   fileline, gml->ml.window);
 2355 
 2356 /*************************************************
 2357  * arrow_class contains the current config info, *
 2358  * which is gamgi->arrow unless new config info  *
 2359  * has been previously stored in object->object. *
 2360  *************************************************/
 2361 
 2362 object = GAMGI_CAST_OBJECT gamgi->arrow;
 2363 if (object->object == NULL) arrow_class = gamgi->arrow;
 2364 else arrow_class = (gamgi_arrow_class *) object->object;
 2365 
 2366 /*****************
 2367  * object create *
 2368  *****************/
 2369 
 2370 arrow = gamgi_engine_create_arrow ();
 2371 gamgi_engine_start_arrow (arrow);
 2372 gamgi_mesa_start_arrow (arrow, arrow_class);
 2373 
 2374 /***********************************************************
 2375  * by default, each new arrow belongs to the current layer *
 2376  ***********************************************************/
 2377 
 2378 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 2379 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 2380 arrow->object.object = object;
 2381 
 2382 /************************
 2383  * object queue: update *
 2384  ************************/
 2385 
 2386 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 2387 gml->object_end->data = arrow;
 2388 
 2389 /************************
 2390  * parent stack: update *
 2391  ************************/
 2392 
 2393 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 2394 gml->ml.parent->data = arrow;
 2395 
 2396 /**********************************
 2397  * attribute array: check, update *
 2398  **********************************/
 2399 
 2400 for (i = 0; attributes[i] != NULL; i += 2)
 2401   {
 2402   /*******************
 2403    * name (optional) *
 2404    *******************/
 2405 
 2406   if (strcmp (attributes[i], "name") == 0)
 2407     {
 2408     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2409     arrow->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2410       return gamgi_io_error_value (attributes[i + 1], 
 2411       gml->ml.filename, fileline, gml->ml.window);
 2412     }
 2413 
 2414   /*****************
 2415    * id (optional) *
 2416    *****************/
 2417 
 2418   else if (strcmp (attributes[i], "id") == 0)
 2419     {
 2420     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2421     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2422       return gamgi_io_error_value (attributes[i + 1], 
 2423       gml->ml.filename, fileline, gml->ml.window);
 2424 
 2425     hash = gamgi_math_hash_value (token,
 2426     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 2427 
 2428     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 2429       return gamgi_io_error_multiple (gml->ml.filename, 
 2430       fileline, gml->ml.window);
 2431 
 2432     gamgi_expat_import_id_add (token, gml, hash);
 2433     }
 2434 
 2435   /*********************
 2436    * parent (optional) *
 2437    *********************/
 2438 
 2439   else if (strcmp (attributes[i], "parent") == 0)
 2440     {
 2441     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2442     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2443       return gamgi_io_error_value (attributes[i + 1], 
 2444       gml->ml.filename, fileline, gml->ml.window);
 2445 
 2446     gamgi_expat_import_ref_add (token, gml);
 2447     }
 2448 
 2449   /********************
 2450    * scale (optional) *
 2451    ********************/
 2452 
 2453   else if (strcmp (attributes[i], "scale") == 0)
 2454     {
 2455     if (gamgi_io_token_double_scan (attributes[i + 1],
 2456     &arrow->object.scale, GAMGI_MESA_SCALE_LOWER,
 2457     GAMGI_MESA_SCALE_UPPER) == FALSE)
 2458       return gamgi_io_error_value (attributes[i + 1], 
 2459       gml->ml.filename, fileline, gml->ml.window);
 2460     }
 2461 
 2462   else return gamgi_io_error_attribute (attributes[i], 
 2463   gml->ml.filename, fileline, gml->ml.window);
 2464   }
 2465 
 2466 /************************************
 2467  * global tests: missing attributes *
 2468  ************************************/
 2469 
 2470 /*****************************************
 2471  * global tests: incompatible attributes *
 2472  *****************************************/
 2473 
 2474 return gml->ml.valid;
 2475 }
 2476 
 2477 static gamgi_bool static_cell_end (const char *element, gamgi_gml *gml)
 2478 {
 2479 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 2480 
 2481 return gml->ml.valid;
 2482 }
 2483 
 2484 static gamgi_bool static_cell_start (const char *element,
 2485 const char **attributes, gamgi_gml *gml)
 2486 {
 2487 gamgi_cell *cell;
 2488 gamgi_cell_class *cell_class;
 2489 gamgi_object *object;
 2490 char token[GAMGI_ENGINE_TOKEN];
 2491 char system[GAMGI_ENGINE_TOKEN] = "";
 2492 char lattice[GAMGI_ENGINE_TOKEN] = "";
 2493 double origin[3], euler[3];
 2494 int o1 = 0, o2 = 0, o3 = 0;
 2495 int o123 = 0;
 2496 gamgi_enum o4 = FALSE;
 2497 int fileline, i;
 2498 int number = 0, volume = 0;
 2499 int position = 0, angle = 0, color = 0;
 2500 int nodes = 0, faces = 0, borders = 0;
 2501 int origin_vectors = 0, axes_vectors = 0;
 2502 unsigned int hash;
 2503 
 2504 /******************************************
 2505  * used only in error messages: file line *
 2506  * indicates where current element starts *
 2507  ******************************************/
 2508 
 2509 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 2510 
 2511 /******************************
 2512  * parent list: check, update *
 2513  ******************************/
 2514 
 2515 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_CELL, gml) == FALSE)
 2516   return gamgi_io_error_child (gml->ml.filename, 
 2517   fileline, gml->ml.window);
 2518 
 2519 /*************************************************
 2520  * cell_class contains the current config info,  *
 2521  * which is gamgi->cell unless new config info   *
 2522  * has been previously stored in object->object. *
 2523  *************************************************/
 2524 
 2525 object = GAMGI_CAST_OBJECT gamgi->cell;
 2526 if (object->object == NULL) cell_class = gamgi->cell;
 2527 else cell_class = (gamgi_cell_class *) object->object;
 2528 
 2529 /*****************
 2530  * object create *
 2531  *****************/
 2532 
 2533 cell = gamgi_engine_create_cell ();
 2534 gamgi_engine_start_cell (cell);
 2535 gamgi_mesa_start_cell (cell, cell_class);
 2536 
 2537 /**********************************************************
 2538  * by default, each new cell belongs to the current layer *
 2539  **********************************************************/
 2540 
 2541 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 2542 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 2543 cell->object.object = object;
 2544 
 2545 /************************
 2546  * object queue: update *
 2547  ************************/
 2548 
 2549 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 2550 gml->object_end->data = cell;
 2551 
 2552 /************************
 2553  * parent stack: update *
 2554  ************************/
 2555 
 2556 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 2557 gml->ml.parent->data = cell;
 2558 
 2559 /**********************************
 2560  * attribute array: check, update *
 2561  **********************************/
 2562 
 2563 for (i = 0; attributes[i] != NULL; i += 2)
 2564   {
 2565   /*******************
 2566    * name (optional) *
 2567    *******************/
 2568 
 2569   if (strcmp (attributes[i], "name") == 0)
 2570     {
 2571     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2572     cell->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2573       return gamgi_io_error_value (attributes[i + 1], 
 2574       gml->ml.filename, fileline, gml->ml.window);
 2575     }
 2576 
 2577   /*****************
 2578    * id (optional) *
 2579    *****************/
 2580 
 2581   else if (strcmp (attributes[i], "id") == 0)
 2582     {
 2583     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2584     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2585       return gamgi_io_error_value (attributes[i + 1], 
 2586       gml->ml.filename, fileline, gml->ml.window);
 2587 
 2588     hash = gamgi_math_hash_value (token,
 2589     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 2590 
 2591     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 2592       return gamgi_io_error_multiple (gml->ml.filename, 
 2593       fileline, gml->ml.window);
 2594 
 2595     gamgi_expat_import_id_add (token, gml, hash);
 2596     }
 2597 
 2598   /*********************
 2599    * parent (optional) *
 2600    *********************/
 2601 
 2602   else if (strcmp (attributes[i], "parent") == 0)
 2603     {
 2604     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 2605     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2606       return gamgi_io_error_value (attributes[i + 1], 
 2607       gml->ml.filename, fileline, gml->ml.window);
 2608 
 2609     gamgi_expat_import_ref_add (token, gml);
 2610     }
 2611 
 2612   /********************
 2613    * Symmetry section *
 2614    ********************/
 2615 
 2616   /*********************
 2617    * system (optional) *
 2618    *********************/
 2619 
 2620   else if (strcmp (attributes[i], "system") == 0)
 2621     {
 2622     if (gamgi_io_token_alpha_scan (attributes[i + 1], system,
 2623     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2624       return gamgi_io_error_value (attributes[i + 1],
 2625       gml->ml.filename, fileline, gml->ml.window);
 2626     }
 2627 
 2628   /**********************
 2629    * lattice (optional) *
 2630    **********************/
 2631 
 2632   else if (strcmp (attributes[i], "lattice") == 0)
 2633     {
 2634     if (gamgi_io_token_alpha_scan (attributes[i + 1], lattice,
 2635     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2636       return gamgi_io_error_value (attributes[i + 1], 
 2637       gml->ml.filename, fileline, gml->ml.window);
 2638     }
 2639 
 2640   /********************
 2641    * group (optional) *
 2642    ********************/
 2643 
 2644   else if (strcmp (attributes[i], "group") == 0)
 2645     {
 2646     if (gamgi_io_token_int_scan (attributes[i + 1],
 2647     &cell->group, 0, 230) == FALSE)
 2648       return gamgi_io_error_value (attributes[i + 1],
 2649       gml->ml.filename, fileline, gml->ml.window);
 2650     }
 2651 
 2652   /***************************************************
 2653    * a,b,c,ab,ac,bc (required, depending on lattice) *
 2654    ***************************************************/
 2655 
 2656   else if (strcmp (attributes[i], "a") == 0)
 2657     {
 2658     if (gamgi_io_token_double_scan (attributes[i + 1],
 2659     &cell->a, GAMGI_MATH_TOLERANCE_LENGTH, DBL_MAX) == FALSE)
 2660       return gamgi_io_error_value (attributes[i + 1], 
 2661       gml->ml.filename, fileline, gml->ml.window);
 2662     }
 2663 
 2664   else if (strcmp (attributes[i], "b") == 0)
 2665     {
 2666     if (gamgi_io_token_double_scan (attributes[i + 1],
 2667     &cell->b, GAMGI_MATH_TOLERANCE_LENGTH, DBL_MAX) == FALSE)
 2668       return gamgi_io_error_value (attributes[i + 1], 
 2669       gml->ml.filename, fileline, gml->ml.window);
 2670     }
 2671 
 2672   else if (strcmp (attributes[i], "c") == 0)
 2673     {
 2674     if (gamgi_io_token_double_scan (attributes[i + 1],
 2675     &cell->c, GAMGI_MATH_TOLERANCE_LENGTH, DBL_MAX) == FALSE)
 2676       return gamgi_io_error_value (attributes[i + 1], 
 2677       gml->ml.filename, fileline, gml->ml.window);
 2678     }
 2679 
 2680   else if (strcmp (attributes[i], "ab") == 0)
 2681     {
 2682     if (gamgi_io_token_double_scan (attributes[i + 1],
 2683     &cell->ab, GAMGI_MATH_TOLERANCE_ANGLE, 180.0) == FALSE)
 2684       return gamgi_io_error_value (attributes[i + 1], 
 2685       gml->ml.filename, fileline, gml->ml.window);
 2686     }
 2687 
 2688   else if (strcmp (attributes[i], "ac") == 0)
 2689     {
 2690     if (gamgi_io_token_double_scan (attributes[i + 1],
 2691     &cell->ac, GAMGI_MATH_TOLERANCE_ANGLE, 180.0) == FALSE)
 2692       return gamgi_io_error_value (attributes[i + 1], 
 2693       gml->ml.filename, fileline, gml->ml.window);
 2694     }
 2695 
 2696   else if (strcmp (attributes[i], "bc") == 0)
 2697     {
 2698     if (gamgi_io_token_double_scan (attributes[i + 1],
 2699     &cell->bc, GAMGI_MATH_TOLERANCE_ANGLE, 180.0) == FALSE)
 2700       return gamgi_io_error_value (attributes[i + 1], 
 2701       gml->ml.filename, fileline, gml->ml.window);
 2702     }
 2703 
 2704   else if (strcmp (attributes[i], "reciprocal") == 0)
 2705     {
 2706     if (gamgi_io_token_double_scan (attributes[i + 1],
 2707     &cell->reciprocal, GAMGI_MATH_TOLERANCE_LENGTH,
 2708     DBL_MAX) == FALSE)
 2709       return gamgi_io_error_value (attributes[i + 1],
 2710       gml->ml.filename, fileline, gml->ml.window);
 2711     }
 2712 
 2713   /******************
 2714    * Volume section *
 2715    ******************/
 2716 
 2717   /*********
 2718    * model *
 2719    *********/
 2720 
 2721   else if (strcmp (attributes[i], "model") == 0)
 2722     {
 2723     if (gamgi_io_token_alpha_scan (attributes[i + 1], token,
 2724     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2725       return gamgi_io_error_value (attributes[i + 1],
 2726       gml->ml.filename, fileline, gml->ml.window);
 2727 
 2728     if (strcmp (token, "conventional") == 0)
 2729       cell->model = GAMGI_PHYS_CONVENTIONAL;
 2730     else if (strcmp (token, "primitive") == 0)
 2731       cell->model = GAMGI_PHYS_PRIMITIVE;
 2732     else if (strcmp (token, "wigner") == 0)
 2733       cell->model = GAMGI_PHYS_WIGNER;
 2734     else if (strcmp (token, "parallelepiped") == 0)
 2735       cell->model = GAMGI_PHYS_PARALLELEPIPED;
 2736     else if (strcmp (token, "sphere") == 0)
 2737       cell->model = GAMGI_PHYS_SPHERE;
 2738     else if (strcmp (token, "projection") == 0)
 2739       cell->model = GAMGI_PHYS_PROJECTION;
 2740 
 2741     else return gamgi_io_error_value (attributes[i + 1],
 2742     gml->ml.filename, fileline, gml->ml.window);
 2743     }
 2744 
 2745   /********************************
 2746    * n1,n2,n3 (optional, coupled) *
 2747    ********************************/
 2748 
 2749   else if (strcmp (attributes[i], "n1") == 0)
 2750     {
 2751     number++;
 2752     if (gamgi_io_token_int_scan (attributes[i + 1],
 2753     &cell->n1, 1, INT_MAX) == FALSE)
 2754       return gamgi_io_error_value (attributes[i + 1],
 2755       gml->ml.filename, fileline, gml->ml.window);
 2756     }
 2757 
 2758   else if (strcmp (attributes[i], "n2") == 0)
 2759     {
 2760     number++;
 2761     if (gamgi_io_token_int_scan (attributes[i + 1],
 2762     &cell->n2, 1, INT_MAX) == FALSE)
 2763       return gamgi_io_error_value (attributes[i + 1],
 2764       gml->ml.filename, fileline, gml->ml.window);
 2765     }
 2766 
 2767   else if (strcmp (attributes[i], "n3") == 0)
 2768     {
 2769     number++;
 2770     if (gamgi_io_token_int_scan (attributes[i + 1],
 2771     &cell->n3, 1, INT_MAX) == FALSE)
 2772       return gamgi_io_error_value (attributes[i + 1],
 2773       gml->ml.filename, fileline, gml->ml.window);
 2774     }
 2775 
 2776   /***************************************************************
 2777    * volume: v1,v2,v3,v12,13,v23 (required, depending of volume) *
 2778    ***************************************************************/
 2779 
 2780   else if (strcmp (attributes[i], "v1") == 0)
 2781     {
 2782     volume++;
 2783     if (gamgi_io_token_double_scan (attributes[i + 1],
 2784     &cell->v1, GAMGI_MATH_TOLERANCE_LENGTH, DBL_MAX) == FALSE)
 2785       return gamgi_io_error_value (attributes[i + 1],
 2786       gml->ml.filename, fileline, gml->ml.window);
 2787     }
 2788 
 2789   else if (strcmp (attributes[i], "v2") == 0)
 2790     {
 2791     volume++;
 2792     if (gamgi_io_token_double_scan (attributes[i + 1],
 2793     &cell->v2, GAMGI_MATH_TOLERANCE_LENGTH, DBL_MAX) == FALSE)
 2794       return gamgi_io_error_value (attributes[i + 1],
 2795       gml->ml.filename, fileline, gml->ml.window);
 2796     }
 2797 
 2798   else if (strcmp (attributes[i], "v3") == 0)
 2799     {
 2800     volume++;
 2801     if (gamgi_io_token_double_scan (attributes[i + 1],
 2802     &cell->v3, GAMGI_MATH_TOLERANCE_LENGTH, DBL_MAX) == FALSE)
 2803       return gamgi_io_error_value (attributes[i + 1],
 2804       gml->ml.filename, fileline, gml->ml.window);
 2805     }
 2806 
 2807   else if (strcmp (attributes[i], "v12") == 0)
 2808     {
 2809     volume++;
 2810     if (gamgi_io_token_double_scan (attributes[i + 1],
 2811     &cell->v12, GAMGI_MATH_TOLERANCE_ANGLE, 180.0) == FALSE)
 2812       return gamgi_io_error_value (attributes[i + 1],
 2813       gml->ml.filename, fileline, gml->ml.window);
 2814     }
 2815 
 2816   else if (strcmp (attributes[i], "v13") == 0)
 2817     {
 2818     volume++;
 2819     if (gamgi_io_token_double_scan (attributes[i + 1],
 2820     &cell->v13, GAMGI_MATH_TOLERANCE_ANGLE, 180.0) == FALSE)
 2821       return gamgi_io_error_value (attributes[i + 1],
 2822       gml->ml.filename, fileline, gml->ml.window);
 2823     }
 2824 
 2825   else if (strcmp (attributes[i], "v23") == 0)
 2826     {
 2827     volume++;
 2828     if (gamgi_io_token_double_scan (attributes[i + 1],
 2829     &cell->v23, GAMGI_MATH_TOLERANCE_ANGLE, 180.0) == FALSE)
 2830       return gamgi_io_error_value (attributes[i + 1],
 2831       gml->ml.filename, fileline, gml->ml.window);
 2832     }
 2833 
 2834   /*********************
 2835    * Positions section *
 2836    *********************/
 2837 
 2838   /*************************************
 2839    * origin: x,y,z (optional, coupled) *
 2840    *************************************/
 2841 
 2842   else if (strcmp (attributes[i], "x") == 0)
 2843     {
 2844     position++;
 2845     if (gamgi_io_token_double_scan (attributes[i + 1],
 2846     &origin[0], -DBL_MAX, DBL_MAX) == FALSE)
 2847       return gamgi_io_error_value (attributes[i + 1], 
 2848       gml->ml.filename, fileline, gml->ml.window);
 2849     }
 2850 
 2851   else if (strcmp (attributes[i], "y") == 0)
 2852     {
 2853     position++;
 2854     if (gamgi_io_token_double_scan (attributes[i + 1],
 2855     &origin[1], -DBL_MAX, DBL_MAX) == FALSE)
 2856       return gamgi_io_error_value (attributes[i + 1], 
 2857       gml->ml.filename, fileline, gml->ml.window);
 2858     }
 2859 
 2860   else if (strcmp (attributes[i], "z") == 0)
 2861     {
 2862     position++;
 2863     if (gamgi_io_token_double_scan (attributes[i + 1],
 2864     &origin[2], -DBL_MAX, DBL_MAX) == FALSE)
 2865       return gamgi_io_error_value (attributes[i + 1], 
 2866       gml->ml.filename, fileline, gml->ml.window);
 2867     }
 2868 
 2869   /**********************************************
 2870    * Euler angles: e1,e2,e3 (optional, coupled) *
 2871    **********************************************/
 2872 
 2873   else if (strcmp (attributes[i], "e1") == 0)
 2874     {
 2875     angle++;
 2876     if (gamgi_io_token_double_scan (attributes[i + 1],
 2877     &euler[0], 0.0, 180.0) == FALSE)
 2878       return gamgi_io_error_value (attributes[i + 1], 
 2879       gml->ml.filename, fileline, gml->ml.window);
 2880     }
 2881 
 2882   else if (strcmp (attributes[i], "e2") == 0)
 2883     {
 2884     angle++;
 2885     if (gamgi_io_token_double_scan (attributes[i + 1],
 2886     &euler[1], 0.0, 360.0) == FALSE)
 2887       return gamgi_io_error_value (attributes[i + 1], 
 2888       gml->ml.filename, fileline, gml->ml.window);
 2889     }
 2890 
 2891   else if (strcmp (attributes[i], "e3") == 0)
 2892     {
 2893     angle++;
 2894     if (gamgi_io_token_double_scan (attributes[i + 1],
 2895     &euler[2], 0.0, 360.0) == FALSE)
 2896       return gamgi_io_error_value (attributes[i + 1], 
 2897       gml->ml.filename, fileline, gml->ml.window);
 2898     }
 2899 
 2900   /******************
 2901    * Origin section *
 2902    ******************/
 2903 
 2904   /***************************************
 2905    * o1,o2,o3 offset (optional, coupled) *
 2906    ***************************************/
 2907 
 2908   else if (strcmp (attributes[i], "o1") == 0)
 2909     {
 2910     o123++;
 2911     if (gamgi_io_token_int_scan (attributes[i + 1],
 2912     &o1, -INT_MAX, INT_MAX) == FALSE)
 2913       return gamgi_io_error_value (attributes[i + 1],
 2914       gml->ml.filename, fileline, gml->ml.window);
 2915     }
 2916 
 2917   else if (strcmp (attributes[i], "o2") == 0)
 2918     {
 2919     o123++;
 2920     if (gamgi_io_token_int_scan (attributes[i + 1],
 2921     &o2, -INT_MAX, INT_MAX) == FALSE)
 2922       return gamgi_io_error_value (attributes[i + 1],
 2923       gml->ml.filename, fileline, gml->ml.window);
 2924     }
 2925 
 2926   else if (strcmp (attributes[i], "o3") == 0)
 2927     {
 2928     o123++;
 2929     if (gamgi_io_token_int_scan (attributes[i + 1],
 2930     &o3, -INT_MAX, INT_MAX) == FALSE)
 2931       return gamgi_io_error_value (attributes[i + 1],
 2932       gml->ml.filename, fileline, gml->ml.window);
 2933     }
 2934 
 2935   /************************
 2936    * o4 offset (optional) *
 2937    ************************/
 2938 
 2939   else if (strcmp (attributes[i], "o4") == 0)
 2940     {
 2941     if (gamgi_io_token_alpha_scan (attributes[i + 1], token,
 2942     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2943       return gamgi_io_error_value (attributes[i + 1],
 2944       gml->ml.filename, fileline, gml->ml.window);
 2945 
 2946     if (strcmp (token, "000") == 0)
 2947       o4 = FALSE;
 2948     else if (strcmp (token, "110") == 0)
 2949       o4 = GAMGI_PHYS_110;
 2950     else if (strcmp (token, "101") == 0)
 2951       o4 = GAMGI_PHYS_101;
 2952     else if (strcmp (token, "011") == 0)
 2953       o4 = GAMGI_PHYS_011;
 2954     else if (strcmp (token, "111") == 0)
 2955       o4 = GAMGI_PHYS_111;
 2956     else if (strcmp (token, "211") == 0)
 2957       o4 = GAMGI_PHYS_211;
 2958     else if (strcmp (token, "122") == 0)
 2959       o4 = GAMGI_PHYS_122;
 2960 
 2961     else return gamgi_io_error_value (attributes[i + 1],
 2962     gml->ml.filename, fileline, gml->ml.window);
 2963     }
 2964 
 2965   /*****************************
 2966    * origin vectors (optional) *
 2967    *****************************/
 2968 
 2969   else if (strcmp (attributes[i], "origin_vectors") == 0)
 2970     {
 2971     origin_vectors++;
 2972     if (gamgi_io_token_alpha_scan (attributes[i + 1], token,
 2973     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 2974       return gamgi_io_error_value (attributes[i + 1],
 2975       gml->ml.filename, fileline, gml->ml.window);
 2976 
 2977     if (strcmp (token, "conventional") == 0)
 2978       cell->origin_vectors = GAMGI_PHYS_CONVENTIONAL;
 2979     else if (strcmp (token, "primitive") == 0)
 2980       cell->origin_vectors = GAMGI_PHYS_PRIMITIVE;
 2981 
 2982     else return gamgi_io_error_value (attributes[i + 1],
 2983     gml->ml.filename, fileline, gml->ml.window);
 2984     }
 2985 
 2986   /*******************
 2987    * axes (optional) *
 2988    *******************/
 2989 
 2990   else if (strcmp (attributes[i], "axes") == 0)
 2991     {
 2992     if (gamgi_io_token_bool_scan (attributes[i + 1],
 2993     &cell->axes) == FALSE)
 2994       return gamgi_io_error_value (attributes[i + 1],
 2995       gml->ml.filename, fileline, gml->ml.window);
 2996     }
 2997 
 2998   /***************************
 2999    * axes vectors (optional) *
 3000    ***************************/
 3001 
 3002   else if (strcmp (attributes[i], "axes_vectors") == 0)
 3003     {
 3004     axes_vectors++;
 3005     if (gamgi_io_token_alpha_scan (attributes[i + 1], token,
 3006     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3007       return gamgi_io_error_value (attributes[i + 1],
 3008       gml->ml.filename, fileline, gml->ml.window);
 3009 
 3010     if (strcmp (token, "conventional") == 0)
 3011       cell->axes_vectors = GAMGI_PHYS_CONVENTIONAL;
 3012     else if (strcmp (token, "primitive") == 0)
 3013       cell->axes_vectors = GAMGI_PHYS_PRIMITIVE;
 3014 
 3015     else return gamgi_io_error_value (attributes[i + 1],
 3016     gml->ml.filename, fileline, gml->ml.window);
 3017     }
 3018 
 3019   /****************
 3020    * View section *
 3021    ****************/
 3022 
 3023   /**********************
 3024    * borders (optional) *
 3025    **********************/
 3026 
 3027   else if (strcmp (attributes[i], "borders") == 0)
 3028     {
 3029     borders++;
 3030     if (gamgi_io_token_alpha_scan (attributes[i + 1], token,
 3031     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3032       return gamgi_io_error_value (attributes[i + 1],
 3033       gml->ml.filename, fileline, gml->ml.window);
 3034 
 3035     if (strcmp (token, "all") == 0)
 3036       cell->borders = GAMGI_PHYS_ALL;
 3037     else if (strcmp (token, "faces") == 0)
 3038       cell->borders = GAMGI_PHYS_FACES;
 3039     else if (strcmp (token, "edges") == 0)
 3040       cell->borders = GAMGI_PHYS_EDGES;
 3041     else if (strcmp (token, "none") == 0)
 3042       cell->borders = GAMGI_PHYS_NONE;
 3043 
 3044     else return gamgi_io_error_value (attributes[i + 1],
 3045     gml->ml.filename, fileline, gml->ml.window);
 3046     }
 3047 
 3048   /***************************
 3049    * nodes, faces (optional) *
 3050    ***************************/
 3051 
 3052   else if (strcmp (attributes[i], "nodes") == 0)
 3053     {
 3054     nodes++;
 3055     if (gamgi_io_token_bool_scan (attributes[i + 1], &cell->nodes) == FALSE)
 3056       return gamgi_io_error_value (attributes[i + 1],
 3057       gml->ml.filename, fileline, gml->ml.window);
 3058     }
 3059 
 3060   else if (strcmp (attributes[i], "faces") == 0)
 3061     {
 3062     faces++;
 3063     if (gamgi_io_token_bool_scan (attributes[i + 1], &cell->faces) == FALSE)
 3064       return gamgi_io_error_value (attributes[i + 1],
 3065       gml->ml.filename, fileline, gml->ml.window);
 3066     }
 3067 
 3068   /*****************************
 3069    * red,green,blue (optional) *
 3070    *****************************/
 3071 
 3072   else if (strcmp (attributes[i], "red") == 0)
 3073     {
 3074     color++;
 3075     if (gamgi_io_token_float_scan (attributes[i + 1],
 3076     &cell->red, 0.0, 1.0) == FALSE)
 3077       return gamgi_io_error_value (attributes[i + 1],
 3078       gml->ml.filename, fileline, gml->ml.window);
 3079     }
 3080 
 3081   else if (strcmp (attributes[i], "green") == 0)
 3082     {
 3083     color++;
 3084     if (gamgi_io_token_float_scan (attributes[i + 1],
 3085     &cell->green, 0.0, 1.0) == FALSE)
 3086       return gamgi_io_error_value (attributes[i + 1],
 3087       gml->ml.filename, fileline, gml->ml.window);
 3088     }
 3089 
 3090   else if (strcmp (attributes[i], "blue") == 0)
 3091     {
 3092     color++;
 3093     if (gamgi_io_token_float_scan (attributes[i + 1],
 3094     &cell->blue, 0.0, 1.0) == FALSE)
 3095       return gamgi_io_error_value (attributes[i + 1],
 3096       gml->ml.filename, fileline, gml->ml.window);
 3097     }
 3098 
 3099   /********************
 3100    * scale (optional) *
 3101    ********************/
 3102 
 3103   else if (strcmp (attributes[i], "scale") == 0)
 3104     {
 3105     if (gamgi_io_token_double_scan (attributes[i + 1],
 3106     &cell->object.scale, GAMGI_MESA_SCALE_LOWER,
 3107     GAMGI_MESA_SCALE_UPPER) == FALSE)
 3108       return gamgi_io_error_value (attributes[i + 1], 
 3109       gml->ml.filename, fileline, gml->ml.window);
 3110     }
 3111 
 3112   else return gamgi_io_error_attribute (attributes[i], 
 3113   gml->ml.filename, fileline, gml->ml.window);
 3114   }
 3115 
 3116 /******************************
 3117  * global: missing attributes *
 3118  ******************************/
 3119 
 3120 if (cell->model == GAMGI_PHYS_PARALLELEPIPED && volume != 6)
 3121   return gamgi_io_error_missing (gml->ml.filename,
 3122   fileline, gml->ml.window);
 3123 
 3124 if (cell->model == GAMGI_PHYS_SPHERE && cell->v1 == 0.0)
 3125   return gamgi_io_error_missing (gml->ml.filename,
 3126   fileline, gml->ml.window);
 3127 
 3128 /***********************************
 3129  * global: incompatible attributes *
 3130  ***********************************/
 3131 
 3132 if (gamgi_gtk_cell_symmetry_import (system,
 3133 lattice, &cell->group, &cell->lattice) == FALSE)
 3134   return gamgi_io_error_incompatible (gml->ml.filename,
 3135   fileline, gml->ml.window);
 3136 
 3137 if (gamgi_gtk_cell_symmetry_parameters (cell, cell->lattice, 
 3138 &cell->a, &cell->b, &cell->c, &cell->ab, &cell->ac, &cell->bc) == FALSE)
 3139   return gamgi_io_error_incompatible (gml->ml.filename,
 3140   fileline, gml->ml.window);
 3141 
 3142 if (position == 1 || position == 2)
 3143   return gamgi_io_error_incompatible (gml->ml.filename, 
 3144   fileline, gml->ml.window);
 3145 
 3146 if (angle == 1 || angle == 2)
 3147   return gamgi_io_error_incompatible (gml->ml.filename, 
 3148   fileline, gml->ml.window);
 3149 
 3150 if (number == 1 || number == 2)
 3151   return gamgi_io_error_incompatible (gml->ml.filename, 
 3152   fileline, gml->ml.window);
 3153 
 3154 if (o123 == 1 || o123 == 2)
 3155   return gamgi_io_error_incompatible (gml->ml.filename,
 3156   fileline, gml->ml.window);
 3157 
 3158 if (o4 != FALSE && cell->origin_vectors == GAMGI_PHYS_PRIMITIVE)
 3159   return gamgi_io_error_incompatible (gml->ml.filename,
 3160   fileline, gml->ml.window);
 3161 
 3162 if (o4 != FALSE && gamgi_math_node_check (cell->lattice, o4) == FALSE)
 3163   return gamgi_io_error_incompatible (gml->ml.filename,
 3164   fileline, gml->ml.window);
 3165   
 3166 /****************************************
 3167  * Wigner-Seitz cells are not available *
 3168  * yet for these more complex lattices  *
 3169  ****************************************/
 3170 
 3171 if (cell->model == GAMGI_PHYS_WIGNER)
 3172   {
 3173   if (cell->lattice == GAMGI_PHYS_TRICLINIC_P ||
 3174   cell->lattice == GAMGI_PHYS_MONOCLINIC_P ||
 3175   cell->lattice == GAMGI_PHYS_MONOCLINIC_C)
 3176     return gamgi_io_error_incompatible (gml->ml.filename,
 3177     fileline, gml->ml.window);
 3178   }
 3179 
 3180 if (cell->model == GAMGI_PHYS_PARALLELEPIPED)
 3181   {
 3182   /*********************************************************
 3183    * the 3 angles together cannot be too close to 0 or 360 *
 3184    *                                                       *
 3185    * each angle cannot be larger than the sum of the other *
 3186    * two, or smaller than the difference of the other two  *
 3187    *********************************************************/
 3188 
 3189   if (cell->v12 + cell->v13 + cell->v23 > 360.0 - GAMGI_MATH_TOLERANCE_ANGLE)
 3190     return gamgi_io_error_incompatible (gml->ml.filename,
 3191     fileline, gml->ml.window);
 3192 
 3193   if (cell->v12 + GAMGI_MATH_TOLERANCE_ANGLE > cell->v13 + cell->v23
 3194   || cell->v13 + GAMGI_MATH_TOLERANCE_ANGLE > cell->v12 + cell->v23
 3195   || cell->v23 + GAMGI_MATH_TOLERANCE_ANGLE > cell->v12 + cell->v13)
 3196     return gamgi_io_error_incompatible (gml->ml.filename, 
 3197     fileline, gml->ml.window);
 3198 
 3199   if (cell->v12 - GAMGI_MATH_TOLERANCE_ANGLE < abs (cell->v13 - cell->v23)
 3200   || cell->v13 - GAMGI_MATH_TOLERANCE_ANGLE < abs (cell->v12 - cell->v23)
 3201   || cell->v23 - GAMGI_MATH_TOLERANCE_ANGLE < abs (cell->v12 - cell->v13))
 3202     return gamgi_io_error_incompatible (gml->ml.filename, 
 3203     fileline, gml->ml.window);
 3204   }
 3205 
 3206 if (cell->model != GAMGI_PHYS_PARALLELEPIPED && 
 3207 cell->model != GAMGI_PHYS_SPHERE && volume != 0)
 3208   return gamgi_io_error_incompatible (gml->ml.filename, 
 3209   fileline, gml->ml.window);
 3210 
 3211 if (cell->model == GAMGI_PHYS_SPHERE && volume != 1)
 3212   return gamgi_io_error_incompatible (gml->ml.filename,
 3213   fileline, gml->ml.window);
 3214 
 3215 if (cell->model == GAMGI_PHYS_PARALLELEPIPED
 3216 || cell->model == GAMGI_PHYS_SPHERE)
 3217   {
 3218   if (number != 0)
 3219     return gamgi_io_error_incompatible (gml->ml.filename,
 3220     fileline, gml->ml.window);
 3221 
 3222   if (cell->borders == GAMGI_PHYS_ALL 
 3223   || cell->borders == GAMGI_PHYS_FACES)
 3224     return gamgi_io_error_incompatible (gml->ml.filename, 
 3225     fileline, gml->ml.window);
 3226 
 3227   if (cell->faces == TRUE)
 3228     return gamgi_io_error_incompatible (gml->ml.filename, 
 3229     fileline, gml->ml.window);
 3230   }
 3231 
 3232 if (cell->model == GAMGI_PHYS_PROJECTION)
 3233   {
 3234   if (number != 0)
 3235     return gamgi_io_error_incompatible (gml->ml.filename,
 3236     fileline, gml->ml.window);
 3237 
 3238   if (cell->reciprocal != 0.0)
 3239     return gamgi_io_error_incompatible (gml->ml.filename,
 3240     fileline, gml->ml.window);
 3241 
 3242   if (cell->axes == TRUE)
 3243     return gamgi_io_error_incompatible (gml->ml.filename,
 3244     fileline, gml->ml.window);
 3245 
 3246   if (o123 != 0 || o4 != FALSE)
 3247     return gamgi_io_error_incompatible (gml->ml.filename,
 3248     fileline, gml->ml.window);
 3249 
 3250   if (borders == 1 && cell->borders != GAMGI_PHYS_EDGES)
 3251     return gamgi_io_error_incompatible (gml->ml.filename,
 3252     fileline, gml->ml.window);
 3253 
 3254   cell->borders = GAMGI_PHYS_EDGES;
 3255 
 3256   if (cell->faces == TRUE)
 3257     return gamgi_io_error_incompatible (gml->ml.filename,
 3258     fileline, gml->ml.window);
 3259 
 3260   if (nodes == 1 && cell->nodes != FALSE)
 3261     return gamgi_io_error_incompatible (gml->ml.filename,
 3262     fileline, gml->ml.window);
 3263 
 3264   cell->nodes = FALSE;
 3265   }
 3266 
 3267 if (cell->faces == TRUE && cell->borders == GAMGI_PHYS_ALL)
 3268   return gamgi_io_error_incompatible (gml->ml.filename, 
 3269   fileline, gml->ml.window);
 3270 
 3271 /*****************************************
 3272  * Something in the cell must be visible *
 3273  *****************************************/
 3274 
 3275 if (faces + nodes + borders == 3 && cell->faces == FALSE &&
 3276 cell->nodes == FALSE && cell->borders == GAMGI_PHYS_NONE)
 3277   return gamgi_io_error_incompatible (gml->ml.filename, 
 3278   fileline, gml->ml.window);
 3279 
 3280 if (color == 1 || color == 2)
 3281    return gamgi_io_error_incompatible (gml->ml.filename, 
 3282    fileline, gml->ml.window);
 3283 
 3284 /******************************
 3285  * global: default attributes *
 3286  ******************************/
 3287 
 3288 if (position == 0) gamgi_math_vector_zero (origin);
 3289 if (angle == 0) gamgi_math_vector_zero (euler);
 3290 
 3291 if (origin_vectors == 0)
 3292   {
 3293   if (cell->model == GAMGI_PHYS_CONVENTIONAL ||
 3294   cell->model == GAMGI_PHYS_PARALLELEPIPED ||
 3295   cell->model == GAMGI_PHYS_SPHERE ||
 3296   cell->model == GAMGI_PHYS_PROJECTION || o4 != FALSE)
 3297     cell->origin_vectors = GAMGI_PHYS_CONVENTIONAL;
 3298 
 3299   else cell->origin_vectors = GAMGI_PHYS_PRIMITIVE;
 3300   }
 3301 
 3302 if (axes_vectors == 0)
 3303   {
 3304   if (cell->model == GAMGI_PHYS_CONVENTIONAL ||
 3305   cell->model == GAMGI_PHYS_PARALLELEPIPED ||
 3306   cell->model == GAMGI_PHYS_SPHERE ||
 3307   cell->model == GAMGI_PHYS_PROJECTION)
 3308     cell->axes_vectors = GAMGI_PHYS_CONVENTIONAL;
 3309 
 3310   else cell->axes_vectors = GAMGI_PHYS_PRIMITIVE;
 3311   }
 3312 
 3313 if (cell->model == GAMGI_PHYS_CONVENTIONAL ||
 3314 cell->model == GAMGI_PHYS_PRIMITIVE ||
 3315 cell->model == GAMGI_PHYS_WIGNER)
 3316   {
 3317   if (borders == 0 && cell->faces == TRUE && cell->borders == GAMGI_PHYS_ALL)
 3318     cell->borders = GAMGI_PHYS_FACES;
 3319   }
 3320 
 3321 if (cell->model == GAMGI_PHYS_PARALLELEPIPED ||
 3322 cell->model == GAMGI_PHYS_SPHERE)
 3323   {
 3324   if (faces == 0) cell->faces = FALSE;
 3325   if (borders == 0 && cell->borders != GAMGI_PHYS_NONE)
 3326     cell->borders = GAMGI_PHYS_EDGES;
 3327   }
 3328 
 3329 if (cell->model == GAMGI_PHYS_PROJECTION)
 3330   {
 3331   if (nodes == 0) cell->nodes = FALSE;
 3332   if (faces == 0) cell->faces = FALSE;
 3333   if (borders == 0) cell->borders = GAMGI_PHYS_EDGES;
 3334   }
 3335 
 3336 /**************
 3337  * build cell *
 3338  **************/
 3339 
 3340 if (cell->origin_vectors == GAMGI_PHYS_PRIMITIVE)
 3341   { cell->o1 = o1; cell->o2 = o2; cell->o3 = o3; }
 3342 if (cell->origin_vectors == GAMGI_PHYS_CONVENTIONAL)
 3343   gamgi_math_node_primitive (cell->lattice, 
 3344   o1, o2, o3, o4, &cell->o1, &cell->o2, &cell->o3);
 3345 
 3346 gamgi_phys_cell_create (cell);
 3347 gamgi_math_position_cell_rotation_set (cell, euler);
 3348 gamgi_math_position_cell_translation_set (cell, origin);
 3349 
 3350 return gml->ml.valid;
 3351 }
 3352 
 3353 static gamgi_bool static_cluster_end (const char *element, gamgi_gml *gml)
 3354 {
 3355 gamgi_cluster *cluster;
 3356 int fileline;
 3357 
 3358 cluster = GAMGI_CAST_CLUSTER gml->ml.parent->data;
 3359 if (cluster->reference == GAMGI_CHEM_POLYTOPE)
 3360   {
 3361   /******************************************
 3362    * used only in error messages: file line *
 3363    * indicates where current element starts *
 3364    ******************************************/
 3365 
 3366   fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 3367 
 3368   /****************************************
 3369    * check arrays built from cdata blocks *
 3370    ****************************************/
 3371 
 3372   if (gamgi_math_polygon_check_loops (cluster->loops,
 3373   cluster->n_loops, cluster->points, cluster->n_points) == FALSE)
 3374     return gamgi_io_error_incompatible (gml->ml.filename,
 3375     fileline, gml->ml.window);
 3376 
 3377   if (gamgi_math_polygon_check_colors (cluster->loops,
 3378   cluster->n_loops, cluster->colors, cluster->n_colors,
 3379   cluster->paints, cluster->n_paints) == FALSE)
 3380     return gamgi_io_error_incompatible (gml->ml.filename,
 3381     fileline, gml->ml.window);
 3382   }
 3383 
 3384 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 3385 
 3386 return gml->ml.valid;
 3387 }
 3388 
 3389 static gamgi_bool static_cluster_start (const char *element,
 3390 const char **attributes, gamgi_gml *gml)
 3391 {
 3392 gamgi_cluster *cluster;
 3393 gamgi_cluster_class *cluster_class;
 3394 gamgi_object *object;
 3395 char token[GAMGI_ENGINE_TOKEN];
 3396 double rotation[9];
 3397 double euler[3];
 3398 int position = 0, center = 0, angle = 0;
 3399 int fileline, i;
 3400 unsigned int hash;
 3401 
 3402 /******************************************
 3403  * used only in error messages: file line *
 3404  * indicates where current element starts *
 3405  ******************************************/
 3406 
 3407 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 3408 
 3409 /******************************
 3410  * parent list: check, update *
 3411  ******************************/
 3412 
 3413 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_CLUSTER, gml) == FALSE)
 3414   return gamgi_io_error_child (gml->ml.filename, 
 3415   fileline, gml->ml.window);
 3416 
 3417 /***************************************************
 3418  * cluster_class contains the current config info, *
 3419  * which is gamgi->cluster unless new config info  *
 3420  * has been previously stored in object->object.   *
 3421  ***************************************************/
 3422 
 3423 object = GAMGI_CAST_OBJECT gamgi->cluster;
 3424 if (object->object == NULL) cluster_class = gamgi->cluster;
 3425 else cluster_class = (gamgi_cluster_class *) object->object;
 3426 
 3427 /*****************
 3428  * object create *
 3429  *****************/
 3430 
 3431 cluster = gamgi_engine_create_cluster ();
 3432 gamgi_engine_start_cluster (cluster);
 3433 gamgi_mesa_start_cluster (cluster, cluster_class);
 3434 
 3435 /*************************************************************
 3436  * by default, each new cluster belongs to the current layer *
 3437  *************************************************************/
 3438 
 3439 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 3440 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 3441 cluster->object.object = object;
 3442 
 3443 /************************
 3444  * object queue: update *
 3445  ************************/
 3446 
 3447 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 3448 gml->object_end->data = cluster;
 3449 
 3450 /************************
 3451  * parent stack: update *
 3452  ************************/
 3453 
 3454 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 3455 gml->ml.parent->data = cluster;
 3456 
 3457 /**********************************
 3458  * attribute array: check, update *
 3459  **********************************/
 3460 
 3461 for (i = 0; attributes[i] != NULL; i += 2)
 3462   {
 3463   /*******************
 3464    * name (optional) *
 3465    *******************/
 3466 
 3467   if (strcmp (attributes[i], "name") == 0)
 3468     {
 3469     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3470     cluster->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3471       return gamgi_io_error_value (attributes[i + 1], 
 3472       gml->ml.filename, fileline, gml->ml.window);
 3473     }
 3474 
 3475   /*****************
 3476    * id (optional) *
 3477    *****************/
 3478 
 3479   else if (strcmp (attributes[i], "id") == 0)
 3480     {
 3481     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3482     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3483       return gamgi_io_error_value (attributes[i + 1], 
 3484       gml->ml.filename, fileline, gml->ml.window);
 3485 
 3486     hash = gamgi_math_hash_value (token,
 3487     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 3488 
 3489     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 3490       return gamgi_io_error_multiple (gml->ml.filename, 
 3491       fileline, gml->ml.window);
 3492 
 3493     gamgi_expat_import_id_add (token, gml, hash);
 3494     }
 3495 
 3496   /*********************
 3497    * parent (optional) *
 3498    *********************/
 3499 
 3500   else if (strcmp (attributes[i], "parent") == 0)
 3501     {
 3502     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3503     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3504       return gamgi_io_error_value (attributes[i + 1], 
 3505       gml->ml.filename, fileline, gml->ml.window);
 3506 
 3507     gamgi_expat_import_ref_add (token, gml);
 3508     }
 3509 
 3510   /************************
 3511    * reference (optional) *
 3512    ************************/
 3513 
 3514   else if (strcmp (attributes[i], "reference") == 0)
 3515     {
 3516     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3517     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3518       return gamgi_io_error_value (attributes[i + 1],
 3519       gml->ml.filename, fileline, gml->ml.window);
 3520 
 3521     else if (strcmp (token, "container") == 0)
 3522        cluster->reference = GAMGI_CHEM_CONTAINER;
 3523     else if (strcmp (token, "polytope") == 0)
 3524        cluster->reference = GAMGI_CHEM_POLYTOPE;
 3525     else return gamgi_io_error_value (attributes[i + 1],
 3526       gml->ml.filename, fileline, gml->ml.window);
 3527     }
 3528 
 3529   /*************************************
 3530    * origin: x,y,z (optional, coupled) *
 3531    *************************************/
 3532 
 3533   else if (strcmp (attributes[i], "x") == 0)
 3534     {
 3535     position++;
 3536     if (gamgi_io_token_double_scan (attributes[i + 1],
 3537     &cluster->origin[0], -DBL_MAX, DBL_MAX) == FALSE)
 3538       return gamgi_io_error_value (attributes[i + 1],
 3539       gml->ml.filename, fileline, gml->ml.window);
 3540     }
 3541 
 3542   else if (strcmp (attributes[i], "y") == 0)
 3543     {
 3544     position++;
 3545     if (gamgi_io_token_double_scan (attributes[i + 1],
 3546     &cluster->origin[1], -DBL_MAX, DBL_MAX) == FALSE)
 3547       return gamgi_io_error_value (attributes[i + 1],
 3548       gml->ml.filename, fileline, gml->ml.window);
 3549     }
 3550 
 3551   else if (strcmp (attributes[i], "z") == 0)
 3552     {
 3553     position++;
 3554     if (gamgi_io_token_double_scan (attributes[i + 1],
 3555     &cluster->origin[2], -DBL_MAX, DBL_MAX) == FALSE)
 3556       return gamgi_io_error_value (attributes[i + 1],
 3557       gml->ml.filename, fileline, gml->ml.window);
 3558     }
 3559 
 3560   /*************************************
 3561    * center: x,y,z (optional, coupled) *
 3562    *************************************/
 3563 
 3564   else if (strcmp (attributes[i], "center_x") == 0)
 3565     {
 3566     center++;
 3567     if (gamgi_io_token_double_scan (attributes[i + 1],
 3568     &cluster->center[0], -DBL_MAX, DBL_MAX) == FALSE)
 3569       return gamgi_io_error_value (attributes[i + 1],
 3570       gml->ml.filename, fileline, gml->ml.window);
 3571     }
 3572 
 3573   else if (strcmp (attributes[i], "center_y") == 0)
 3574     {
 3575     center++;
 3576     if (gamgi_io_token_double_scan (attributes[i + 1],
 3577     &cluster->center[1], -DBL_MAX, DBL_MAX) == FALSE)
 3578       return gamgi_io_error_value (attributes[i + 1],
 3579       gml->ml.filename, fileline, gml->ml.window);
 3580     }
 3581 
 3582   else if (strcmp (attributes[i], "center_z") == 0)
 3583     {
 3584     center++;
 3585     if (gamgi_io_token_double_scan (attributes[i + 1],
 3586     &cluster->center[2], -DBL_MAX, DBL_MAX) == FALSE)
 3587       return gamgi_io_error_value (attributes[i + 1],
 3588       gml->ml.filename, fileline, gml->ml.window);
 3589     }
 3590 
 3591   /**********************************************
 3592    * Euler angles: e1,e2,e3 (optional, coupled) *
 3593    **********************************************/
 3594 
 3595   else if (strcmp (attributes[i], "e1") == 0)
 3596     {
 3597     angle++;
 3598     if (gamgi_io_token_double_scan (attributes[i + 1],
 3599     &euler[0], 0.0, 180.0) == FALSE)
 3600       return gamgi_io_error_value (attributes[i + 1],
 3601       gml->ml.filename, fileline, gml->ml.window);
 3602     }
 3603 
 3604   else if (strcmp (attributes[i], "e2") == 0)
 3605     {
 3606     angle++;
 3607     if (gamgi_io_token_double_scan (attributes[i + 1],
 3608     &euler[1], 0.0, 360.0) == FALSE)
 3609       return gamgi_io_error_value (attributes[i + 1],
 3610       gml->ml.filename, fileline, gml->ml.window);
 3611     }
 3612 
 3613   else if (strcmp (attributes[i], "e3") == 0)
 3614     {
 3615     angle++;
 3616     if (gamgi_io_token_double_scan (attributes[i + 1],
 3617     &euler[2], 0.0, 360.0) == FALSE)
 3618       return gamgi_io_error_value (attributes[i + 1],
 3619       gml->ml.filename, fileline, gml->ml.window);
 3620     }
 3621 
 3622   /***************************
 3623    * atoms, faces (optional) *
 3624    ***************************/
 3625 
 3626   else if (strcmp (attributes[i], "atoms") == 0)
 3627     {
 3628     if (gamgi_io_token_int_scan (attributes[i + 1],
 3629     &cluster->atoms, 0, INT_MAX) == FALSE)
 3630       return gamgi_io_error_value (attributes[i + 1],
 3631       gml->ml.filename, fileline, gml->ml.window);
 3632     }
 3633 
 3634   else if (strcmp (attributes[i], "faces") == 0)
 3635     {
 3636     if (gamgi_io_token_bool_scan (attributes[i + 1], &cluster->faces) == FALSE)
 3637       return gamgi_io_error_value (attributes[i + 1],
 3638       gml->ml.filename, fileline, gml->ml.window);
 3639     }
 3640 
 3641   /********************
 3642    * scale (optional) *
 3643    ********************/
 3644 
 3645   else if (strcmp (attributes[i], "scale") == 0)
 3646     {
 3647     if (gamgi_io_token_double_scan (attributes[i + 1],
 3648     &cluster->object.scale, GAMGI_MESA_SCALE_LOWER,
 3649     GAMGI_MESA_SCALE_UPPER) == FALSE)
 3650       return gamgi_io_error_value (attributes[i + 1], 
 3651       gml->ml.filename, fileline, gml->ml.window);
 3652     }
 3653 
 3654   else return gamgi_io_error_attribute (attributes[i], 
 3655   gml->ml.filename, fileline, gml->ml.window);
 3656   }
 3657 
 3658 /************************************
 3659  * global tests: missing attributes *
 3660  ************************************/
 3661 
 3662 /*****************************************
 3663  * global tests: incompatible attributes *
 3664  *****************************************/
 3665 
 3666 /*****************************************
 3667  * coupled parameters: enter all or none *
 3668  *****************************************/
 3669 
 3670 if (position == 1 || position == 2)
 3671   return gamgi_io_error_incompatible (gml->ml.filename,
 3672   fileline, gml->ml.window);
 3673 
 3674 if (center == 1 || center == 2)
 3675   return gamgi_io_error_incompatible (gml->ml.filename,
 3676   fileline, gml->ml.window);
 3677 
 3678 if (angle == 1 || angle == 2)
 3679   return gamgi_io_error_incompatible (gml->ml.filename,
 3680   fileline, gml->ml.window);
 3681 
 3682 /******************************
 3683  * global: default attributes *
 3684  ******************************/
 3685 
 3686 if (angle == 0)
 3687   { euler[0] = 0.0; euler[1] = 0.0; euler[2] = 0.0; }
 3688 
 3689 /*****************************
 3690  * set quaternion from euler *
 3691  *****************************/
 3692 
 3693 gamgi_math_euler_to_matrix (euler, rotation);
 3694 gamgi_math_quaternion_from_matrix (rotation, cluster->quaternion);
 3695 
 3696 return gml->ml.valid;
 3697 }
 3698 
 3699 static gamgi_bool static_molecule_end (const char *element, gamgi_gml *gml)
 3700 {
 3701 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 3702 
 3703 return gml->ml.valid;
 3704 }
 3705 
 3706 static gamgi_bool static_molecule_start (const char *element,
 3707 const char **attributes, gamgi_gml *gml)
 3708 {
 3709 gamgi_molecule *molecule;
 3710 gamgi_molecule_class *molecule_class;
 3711 gamgi_object *object;
 3712 char token[GAMGI_ENGINE_TOKEN];
 3713 int fileline, i;
 3714 unsigned int hash;
 3715 
 3716 /******************************************
 3717  * used only in error messages: file line *
 3718  * indicates where current element starts *
 3719  ******************************************/
 3720 
 3721 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 3722 
 3723 /******************************
 3724  * parent list: check, update *
 3725  ******************************/
 3726 
 3727 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_MOLECULE, gml) == FALSE)
 3728   return gamgi_io_error_child (gml->ml.filename, 
 3729   fileline, gml->ml.window);
 3730 
 3731 /****************************************************
 3732  * molecule_class contains the current config info, *
 3733  * which is gamgi->molecule unless new config info  *
 3734  * has been previously stored in object->object.    *
 3735  ****************************************************/
 3736 
 3737 object = GAMGI_CAST_OBJECT gamgi->molecule;
 3738 if (object->object == NULL) molecule_class = gamgi->molecule;
 3739 else molecule_class = (gamgi_molecule_class *) object->object;
 3740 
 3741 /*****************
 3742  * object create *
 3743  *****************/
 3744 
 3745 molecule = gamgi_engine_create_molecule ();
 3746 gamgi_engine_start_molecule (molecule);
 3747 gamgi_mesa_start_molecule (molecule, molecule_class);
 3748 
 3749 /**************************************************************
 3750  * by default, each new molecule belongs to the current layer *
 3751  **************************************************************/
 3752 
 3753 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 3754 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 3755 molecule->object.object = object;
 3756 
 3757 /************************
 3758  * object queue: update *
 3759  ************************/
 3760 
 3761 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 3762 gml->object_end->data = molecule;
 3763 
 3764 /************************
 3765  * parent stack: update *
 3766  ************************/
 3767 
 3768 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 3769 gml->ml.parent->data = molecule;
 3770 
 3771 /**********************************
 3772  * attribute array: check, update *
 3773  **********************************/
 3774 
 3775 for (i = 0; attributes[i] != NULL; i += 2)
 3776   {
 3777   /*******************
 3778    * name (optional) *
 3779    *******************/
 3780 
 3781   if (strcmp (attributes[i], "name") == 0)
 3782     {
 3783     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3784     molecule->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3785       return gamgi_io_error_value (attributes[i + 1], 
 3786       gml->ml.filename, fileline, gml->ml.window);
 3787     }
 3788 
 3789   /*****************
 3790    * id (optional) *
 3791    *****************/
 3792 
 3793   else if (strcmp (attributes[i], "id") == 0)
 3794     {
 3795     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3796     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3797       return gamgi_io_error_value (attributes[i + 1], 
 3798       gml->ml.filename, fileline, gml->ml.window);
 3799 
 3800     hash = gamgi_math_hash_value (token,
 3801     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 3802 
 3803     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 3804       return gamgi_io_error_multiple (gml->ml.filename, 
 3805       fileline, gml->ml.window);
 3806 
 3807     gamgi_expat_import_id_add (token, gml, hash);
 3808     }
 3809 
 3810   /*********************
 3811    * parent (optional) *
 3812    *********************/
 3813 
 3814   else if (strcmp (attributes[i], "parent") == 0)
 3815     {
 3816     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3817     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3818       return gamgi_io_error_value (attributes[i + 1], 
 3819       gml->ml.filename, fileline, gml->ml.window);
 3820 
 3821     gamgi_expat_import_ref_add (token, gml);
 3822     }
 3823 
 3824   /********************
 3825    * scale (optional) *
 3826    ********************/
 3827 
 3828   else if (strcmp (attributes[i], "scale") == 0)
 3829     {
 3830     if (gamgi_io_token_double_scan (attributes[i + 1],
 3831     &molecule->object.scale, GAMGI_MESA_SCALE_LOWER,
 3832     GAMGI_MESA_SCALE_UPPER) == FALSE)
 3833       return gamgi_io_error_value (attributes[i + 1], 
 3834       gml->ml.filename, fileline, gml->ml.window);
 3835     }
 3836 
 3837   else return gamgi_io_error_attribute (attributes[i], 
 3838   gml->ml.filename, fileline, gml->ml.window);
 3839   }
 3840 
 3841 /************************************
 3842  * global tests: missing attributes *
 3843  ************************************/
 3844 
 3845 /*****************************************
 3846  * global tests: incompatible attributes *
 3847  *****************************************/
 3848 
 3849 return gml->ml.valid;
 3850 }
 3851 
 3852 static gamgi_bool static_group_end (const char *element, gamgi_gml *gml)
 3853 {
 3854 gamgi_group *group;
 3855 int fileline;
 3856 
 3857 group = GAMGI_CAST_GROUP gml->ml.parent->data;
 3858 if (group->reference == GAMGI_CHEM_POLYTOPE)
 3859   {
 3860   /******************************************
 3861    * used only in error messages: file line *
 3862    * indicates where current element starts *
 3863    ******************************************/
 3864 
 3865   fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 3866 
 3867   /****************************************
 3868    * check arrays built from cdata blocks *
 3869    ****************************************/
 3870 
 3871   if (gamgi_math_polygon_check_loops (group->loops,
 3872   group->n_loops, group->points, group->n_points) == FALSE)
 3873     return gamgi_io_error_incompatible (gml->ml.filename,
 3874     fileline, gml->ml.window);
 3875   }
 3876 
 3877 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 3878 
 3879 return gml->ml.valid;
 3880 }
 3881 
 3882 static gamgi_bool static_group_start (const char *element,
 3883 const char **attributes, gamgi_gml *gml)
 3884 {
 3885 gamgi_group *group;
 3886 gamgi_group_class *group_class;
 3887 gamgi_object *object;
 3888 char token[GAMGI_ENGINE_TOKEN];
 3889 double rotation[9];
 3890 double euler[3];
 3891 int position = 0, center = 0, angle = 0, color = 0;
 3892 int fileline, i;
 3893 unsigned int hash;
 3894 
 3895 /******************************************
 3896  * used only in error messages: file line *
 3897  * indicates where current element starts *
 3898  ******************************************/
 3899 
 3900 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 3901 
 3902 /******************************
 3903  * parent list: check, update *
 3904  ******************************/
 3905 
 3906 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_GROUP, gml) == FALSE)
 3907   return gamgi_io_error_child (gml->ml.filename, 
 3908   fileline, gml->ml.window);
 3909 
 3910 /*************************************************
 3911  * group_class contains the current config info, *
 3912  * which is gamgi->group unless new config info  *
 3913  * has been previously stored in object->object. *
 3914  *************************************************/
 3915 
 3916 object = GAMGI_CAST_OBJECT gamgi->group;
 3917 if (object->object == NULL) group_class = gamgi->group;
 3918 else group_class = (gamgi_group_class *) object->object;
 3919 
 3920 /*****************
 3921  * object create *
 3922  *****************/
 3923 
 3924 group = gamgi_engine_create_group ();
 3925 gamgi_engine_start_group (group);
 3926 gamgi_mesa_start_group (group, group_class);
 3927 
 3928 /***********************************************************
 3929  * by default, each new group belongs to the current layer *
 3930  ***********************************************************/
 3931 
 3932 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 3933 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 3934 group->object.object = object;
 3935 
 3936 /************************
 3937  * object queue: update *
 3938  ************************/
 3939 
 3940 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 3941 gml->object_end->data = group;
 3942 
 3943 /************************
 3944  * parent stack: update *
 3945  ************************/
 3946 
 3947 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 3948 gml->ml.parent->data = group;
 3949 
 3950 /**********************************
 3951  * attribute array: check, update *
 3952  **********************************/
 3953 
 3954 for (i = 0; attributes[i] != NULL; i += 2)
 3955   {
 3956   /*******************
 3957    * name (optional) *
 3958    *******************/
 3959 
 3960   if (strcmp (attributes[i], "name") == 0)
 3961     {
 3962     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3963     group->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3964       return gamgi_io_error_value (attributes[i + 1], 
 3965       gml->ml.filename, fileline, gml->ml.window);
 3966     }
 3967 
 3968   /*****************
 3969    * id (optional) *
 3970    *****************/
 3971 
 3972   else if (strcmp (attributes[i], "id") == 0)
 3973     {
 3974     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3975     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3976       return gamgi_io_error_value (attributes[i + 1], 
 3977       gml->ml.filename, fileline, gml->ml.window);
 3978 
 3979     hash = gamgi_math_hash_value (token,
 3980     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 3981 
 3982     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 3983       return gamgi_io_error_multiple (gml->ml.filename, 
 3984       fileline, gml->ml.window);
 3985 
 3986     gamgi_expat_import_id_add (token, gml, hash);
 3987     }
 3988 
 3989   /*********************
 3990    * parent (optional) *
 3991    *********************/
 3992 
 3993   else if (strcmp (attributes[i], "parent") == 0)
 3994     {
 3995     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 3996     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 3997       return gamgi_io_error_value (attributes[i + 1], 
 3998       gml->ml.filename, fileline, gml->ml.window);
 3999 
 4000     gamgi_expat_import_ref_add (token, gml);
 4001     }
 4002 
 4003   /************************
 4004    * reference (optional) *
 4005    ************************/
 4006 
 4007   else if (strcmp (attributes[i], "reference") == 0)
 4008     {
 4009     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4010     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4011       return gamgi_io_error_value (attributes[i + 1],
 4012       gml->ml.filename, fileline, gml->ml.window);
 4013 
 4014     else if (strcmp (token, "container") == 0)
 4015        group->reference = GAMGI_CHEM_CONTAINER;
 4016     else if (strcmp (token, "polytope") == 0)
 4017        group->reference = GAMGI_CHEM_POLYTOPE;
 4018     else return gamgi_io_error_value (attributes[i + 1],
 4019       gml->ml.filename, fileline, gml->ml.window);
 4020     }
 4021 
 4022   /*************************************
 4023    * origin: x,y,z (optional, coupled) *
 4024    *************************************/
 4025 
 4026   else if (strcmp (attributes[i], "x") == 0)
 4027     {
 4028     position++;
 4029     if (gamgi_io_token_double_scan (attributes[i + 1],
 4030     &group->origin[0], -DBL_MAX, DBL_MAX) == FALSE)
 4031       return gamgi_io_error_value (attributes[i + 1],
 4032       gml->ml.filename, fileline, gml->ml.window);
 4033     }
 4034 
 4035   else if (strcmp (attributes[i], "y") == 0)
 4036     {
 4037     position++;
 4038     if (gamgi_io_token_double_scan (attributes[i + 1],
 4039     &group->origin[1], -DBL_MAX, DBL_MAX) == FALSE)
 4040       return gamgi_io_error_value (attributes[i + 1],
 4041       gml->ml.filename, fileline, gml->ml.window);
 4042     }
 4043 
 4044   else if (strcmp (attributes[i], "z") == 0)
 4045     {
 4046     position++;
 4047     if (gamgi_io_token_double_scan (attributes[i + 1],
 4048     &group->origin[2], -DBL_MAX, DBL_MAX) == FALSE)
 4049       return gamgi_io_error_value (attributes[i + 1],
 4050       gml->ml.filename, fileline, gml->ml.window);
 4051     }
 4052 
 4053   /*************************************
 4054    * center: x,y,z (optional, coupled) *
 4055    *************************************/
 4056 
 4057   else if (strcmp (attributes[i], "center_x") == 0)
 4058     {
 4059     center++;
 4060     if (gamgi_io_token_double_scan (attributes[i + 1],
 4061     &group->center[0], -DBL_MAX, DBL_MAX) == FALSE)
 4062       return gamgi_io_error_value (attributes[i + 1],
 4063       gml->ml.filename, fileline, gml->ml.window);
 4064     }
 4065 
 4066   else if (strcmp (attributes[i], "center_y") == 0)
 4067     {
 4068     center++;
 4069     if (gamgi_io_token_double_scan (attributes[i + 1],
 4070     &group->center[1], -DBL_MAX, DBL_MAX) == FALSE)
 4071       return gamgi_io_error_value (attributes[i + 1],
 4072       gml->ml.filename, fileline, gml->ml.window);
 4073     }
 4074 
 4075   else if (strcmp (attributes[i], "center_z") == 0)
 4076     {
 4077     center++;
 4078     if (gamgi_io_token_double_scan (attributes[i + 1],
 4079     &group->center[2], -DBL_MAX, DBL_MAX) == FALSE)
 4080       return gamgi_io_error_value (attributes[i + 1],
 4081       gml->ml.filename, fileline, gml->ml.window);
 4082     }
 4083 
 4084   /**********************************************
 4085    * Euler angles: e1,e2,e3 (optional, coupled) *
 4086    **********************************************/
 4087 
 4088   else if (strcmp (attributes[i], "e1") == 0)
 4089     {
 4090     angle++;
 4091     if (gamgi_io_token_double_scan (attributes[i + 1],
 4092     &euler[0], 0.0, 180.0) == FALSE)
 4093       return gamgi_io_error_value (attributes[i + 1],
 4094       gml->ml.filename, fileline, gml->ml.window);
 4095     }
 4096 
 4097   else if (strcmp (attributes[i], "e2") == 0)
 4098     {
 4099     angle++;
 4100     if (gamgi_io_token_double_scan (attributes[i + 1],
 4101     &euler[1], 0.0, 360.0) == FALSE)
 4102       return gamgi_io_error_value (attributes[i + 1],
 4103       gml->ml.filename, fileline, gml->ml.window);
 4104     }
 4105 
 4106   else if (strcmp (attributes[i], "e3") == 0)
 4107     {
 4108     angle++;
 4109     if (gamgi_io_token_double_scan (attributes[i + 1],
 4110     &euler[2], 0.0, 360.0) == FALSE)
 4111       return gamgi_io_error_value (attributes[i + 1],
 4112       gml->ml.filename, fileline, gml->ml.window);
 4113     }
 4114 
 4115   /********************
 4116    * style (optional) *
 4117    ********************/
 4118 
 4119   else if (strcmp (attributes[i], "faces") == 0)
 4120     {
 4121     if (gamgi_io_token_bool_scan (attributes[i + 1], &group->faces) == FALSE)
 4122       return gamgi_io_error_value (attributes[i + 1],
 4123       gml->ml.filename, fileline, gml->ml.window);
 4124     }
 4125 
 4126   /*****************************
 4127    * red,green,blue (optional) *
 4128    *****************************/
 4129 
 4130   else if (strcmp (attributes[i], "red") == 0)
 4131     {
 4132     color++;
 4133     if (gamgi_io_token_float_scan (attributes[i + 1],
 4134     &group->red, 0.0, 1.0) == FALSE)
 4135       return gamgi_io_error_value (attributes[i + 1],
 4136       gml->ml.filename, fileline, gml->ml.window);
 4137     }
 4138 
 4139   else if (strcmp (attributes[i], "green") == 0)
 4140     {
 4141     color++;
 4142     if (gamgi_io_token_float_scan (attributes[i + 1],
 4143     &group->green, 0.0, 1.0) == FALSE)
 4144       return gamgi_io_error_value (attributes[i + 1],
 4145       gml->ml.filename, fileline, gml->ml.window);
 4146     }
 4147 
 4148   else if (strcmp (attributes[i], "blue") == 0)
 4149     {
 4150     color++;
 4151     if (gamgi_io_token_float_scan (attributes[i + 1],
 4152     &group->blue, 0.0, 1.0) == FALSE)
 4153       return gamgi_io_error_value (attributes[i + 1],
 4154       gml->ml.filename, fileline, gml->ml.window);
 4155     }
 4156 
 4157   /********************
 4158    * scale (optional) *
 4159    ********************/
 4160 
 4161   else if (strcmp (attributes[i], "scale") == 0)
 4162     {
 4163     if (gamgi_io_token_double_scan (attributes[i + 1],
 4164     &group->object.scale, GAMGI_MESA_SCALE_LOWER,
 4165     GAMGI_MESA_SCALE_UPPER) == FALSE)
 4166       return gamgi_io_error_value (attributes[i + 1], 
 4167       gml->ml.filename, fileline, gml->ml.window);
 4168     }
 4169 
 4170   else return gamgi_io_error_attribute (attributes[i], 
 4171   gml->ml.filename, fileline, gml->ml.window);
 4172   }
 4173 
 4174 /************************************
 4175  * global tests: missing attributes *
 4176  ************************************/
 4177 
 4178 /*****************************************
 4179  * global tests: incompatible attributes *
 4180  *****************************************/
 4181 
 4182 /*****************************************
 4183  * coupled parameters: enter all or none *
 4184  *****************************************/
 4185 
 4186 if (position == 1 || position == 2)
 4187   return gamgi_io_error_incompatible (gml->ml.filename,
 4188   fileline, gml->ml.window);
 4189 
 4190 if (center == 1 || center == 2)
 4191   return gamgi_io_error_incompatible (gml->ml.filename,
 4192   fileline, gml->ml.window);
 4193 
 4194 if (angle == 1 || angle == 2)
 4195   return gamgi_io_error_incompatible (gml->ml.filename,
 4196   fileline, gml->ml.window);
 4197 
 4198 if (color == 1 || color == 2)
 4199   return gamgi_io_error_incompatible (gml->ml.filename,
 4200   fileline, gml->ml.window);
 4201 
 4202 /******************************
 4203  * global: default attributes *
 4204  ******************************/
 4205 
 4206 if (angle == 0)
 4207   { euler[0] = 0.0; euler[1] = 0.0; euler[2] = 0.0; }
 4208 
 4209 /*****************************
 4210  * set quaternion from euler *
 4211  *****************************/
 4212 
 4213 gamgi_math_euler_to_matrix (euler, rotation);
 4214 gamgi_math_quaternion_from_matrix (rotation, group->quaternion);
 4215 
 4216 return gml->ml.valid;
 4217 }
 4218 
 4219 static gamgi_bool static_plane_end (const char *element, gamgi_gml *gml)
 4220 {
 4221 gamgi_plane *plane;
 4222 int fileline;
 4223 
 4224 plane = GAMGI_CAST_PLANE gml->ml.parent->data;
 4225 if (plane->model == GAMGI_PHYS_POLYGON)
 4226   {
 4227   /******************************************
 4228    * used only in error messages: file line *
 4229    * indicates where current element starts *
 4230    ******************************************/
 4231 
 4232   fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 4233 
 4234   /****************************************
 4235    * check arrays built from cdata blocks *
 4236    ****************************************/
 4237 
 4238   if (plane->loops == NULL || plane->loops[0] > 1)
 4239     return gamgi_io_error_incompatible (gml->ml.filename,
 4240     fileline, gml->ml.window);
 4241 
 4242   if (gamgi_math_polygon_check_loops (plane->loops,
 4243   plane->n_loops, plane->points, plane->n_points) == FALSE)
 4244     return gamgi_io_error_incompatible (gml->ml.filename,
 4245     fileline, gml->ml.window);
 4246   }
 4247 
 4248 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 4249 
 4250 return gml->ml.valid;
 4251 }
 4252 
 4253 static gamgi_bool static_plane_start (const char *element,
 4254 const char **attributes, gamgi_gml *gml)
 4255 {
 4256 gamgi_plane *plane;
 4257 gamgi_plane_class *plane_class;
 4258 gamgi_object *object;
 4259 char token[GAMGI_ENGINE_TOKEN];
 4260 double rotation[9], euler[3];
 4261 int fileline, i;
 4262 int parent = 0;
 4263 int hkl = 0, color = 0, order = 0;
 4264 int vectors = 0, net = 0;
 4265 int position = 0, center = 0, angle = 0;
 4266 unsigned int hash;
 4267 
 4268 /******************************************
 4269  * used only in error messages: file line *
 4270  * indicates where current element starts *
 4271  ******************************************/
 4272 
 4273 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 4274 
 4275 /******************************
 4276  * parent list: check, update *
 4277  ******************************/
 4278 
 4279 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_PLANE, gml) == FALSE)
 4280   return gamgi_io_error_child (gml->ml.filename, 
 4281   fileline, gml->ml.window);
 4282 
 4283 /*************************************************
 4284  * plane_class contains the current config info, *
 4285  * which is gamgi->plane unless new config info  *
 4286  * has been previously stored in object->object. *
 4287  *************************************************/
 4288 
 4289 object = GAMGI_CAST_OBJECT gamgi->plane;
 4290 if (object->object == NULL) plane_class = gamgi->plane;
 4291 else plane_class = (gamgi_plane_class *) object->object;
 4292 
 4293 /*****************
 4294  * object create *
 4295  *****************/
 4296 
 4297 plane = gamgi_engine_create_plane ();
 4298 gamgi_engine_start_plane (plane);
 4299 gamgi_mesa_start_plane (plane, plane_class);
 4300 
 4301 /***********************************************************
 4302  * by default, each new plane belongs to the current layer *
 4303  ***********************************************************/
 4304 
 4305 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 4306 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 4307 plane->object.object = object;
 4308 
 4309 /************************
 4310  * object queue: update *
 4311  ************************/
 4312 
 4313 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 4314 gml->object_end->data = plane;
 4315 
 4316 /************************
 4317  * parent stack: update *
 4318  ************************/
 4319 
 4320 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 4321 gml->ml.parent->data = plane;
 4322 
 4323 /**********************************
 4324  * attribute array: check, update *
 4325  **********************************/
 4326 
 4327 for (i = 0; attributes[i] != NULL; i += 2)
 4328   {
 4329   /*******************
 4330    * name (optional) *
 4331    *******************/
 4332 
 4333   if (strcmp (attributes[i], "name") == 0)
 4334     {
 4335     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4336     plane->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4337       return gamgi_io_error_value (attributes[i + 1], 
 4338       gml->ml.filename, fileline, gml->ml.window);
 4339     }
 4340 
 4341   /*****************
 4342    * id (optional) *
 4343    *****************/
 4344 
 4345   else if (strcmp (attributes[i], "id") == 0)
 4346     {
 4347     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4348     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4349       return gamgi_io_error_value (attributes[i + 1], 
 4350       gml->ml.filename, fileline, gml->ml.window);
 4351 
 4352      hash = gamgi_math_hash_value (token,
 4353     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 4354 
 4355     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 4356       return gamgi_io_error_multiple (gml->ml.filename, 
 4357       fileline, gml->ml.window);
 4358 
 4359     gamgi_expat_import_id_add (token, gml, hash);
 4360     }
 4361 
 4362   /*********************
 4363    * parent (optional) *
 4364    *********************/
 4365 
 4366   else if (strcmp (attributes[i], "parent") == 0)
 4367     {
 4368     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4369     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4370       return gamgi_io_error_value (attributes[i + 1], 
 4371       gml->ml.filename, fileline, gml->ml.window);
 4372 
 4373     gamgi_expat_import_ref_add (token, gml);
 4374     }
 4375 
 4376   /************************
 4377    * reference (optional) *
 4378    ***********************/
 4379 
 4380   else if (strcmp (attributes[i], "reference") == 0)
 4381     {
 4382     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4383     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4384       return gamgi_io_error_value (attributes[i + 1],
 4385       gml->ml.filename, fileline, gml->ml.window);
 4386 
 4387     if (strcmp (token, "cell") == 0)
 4388       plane->reference = GAMGI_ENGINE_CELL;
 4389     else if (strcmp (token, "atoms") == 0)
 4390       plane->reference = GAMGI_ENGINE_ATOM;
 4391 
 4392     else return gamgi_io_error_value (attributes[i + 1],
 4393     gml->ml.filename, fileline, gml->ml.window);
 4394     }
 4395 
 4396   /**********************************************
 4397    * hkl (required for crystallographic planes) *
 4398    **********************************************/
 4399 
 4400   else if (strcmp (attributes[i], "h") == 0)
 4401     {
 4402     hkl++;
 4403     if (gamgi_io_token_int_scan (attributes[i + 1],
 4404     &plane->hkl[0], -INT_MAX, INT_MAX) == FALSE)
 4405       return gamgi_io_error_value (attributes[i + 1],
 4406       gml->ml.filename, fileline, gml->ml.window);
 4407     }
 4408 
 4409   else if (strcmp (attributes[i], "k") == 0)
 4410     {
 4411     hkl++;
 4412     if (gamgi_io_token_int_scan (attributes[i + 1],
 4413     &plane->hkl[1], -INT_MAX, INT_MAX) == FALSE)
 4414       return gamgi_io_error_value (attributes[i + 1],
 4415       gml->ml.filename, fileline, gml->ml.window);
 4416     }
 4417 
 4418   else if (strcmp (attributes[i], "l") == 0)
 4419     {
 4420     hkl++;
 4421     if (gamgi_io_token_int_scan (attributes[i + 1],
 4422     &plane->hkl[2], -INT_MAX, INT_MAX) == FALSE)
 4423       return gamgi_io_error_value (attributes[i + 1],
 4424       gml->ml.filename, fileline, gml->ml.window);
 4425     }
 4426 
 4427   /**********************
 4428    * vectors (optional) *
 4429    **********************/
 4430 
 4431   else if (strcmp (attributes[i], "vectors") == 0)
 4432     {
 4433     vectors++;
 4434     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4435     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4436       return gamgi_io_error_value (attributes[i + 1],
 4437       gml->ml.filename, fileline, gml->ml.window);
 4438 
 4439     if (strcmp (token, "conventional") == 0)
 4440       plane->vectors = GAMGI_PHYS_CONVENTIONAL;
 4441     else if (strcmp (token, "primitive") == 0)
 4442       plane->vectors = GAMGI_PHYS_PRIMITIVE;
 4443 
 4444     else return gamgi_io_error_value (attributes[i + 1],
 4445     gml->ml.filename, fileline, gml->ml.window);
 4446     }
 4447 
 4448   /********************
 4449    * model (optional) *
 4450    ********************/
 4451 
 4452   else if (strcmp (attributes[i], "model") == 0)
 4453     {
 4454     if (gamgi_io_token_alpha_scan (attributes[i + 1], token,
 4455     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4456       return gamgi_io_error_value (attributes[i + 1],
 4457       gml->ml.filename, fileline, gml->ml.window);
 4458 
 4459     if (strcmp (token, "polygon") == 0)
 4460       plane->model = GAMGI_PHYS_POLYGON;
 4461     else if (strcmp (token, "trace") == 0)
 4462       plane->model = GAMGI_PHYS_TRACE;
 4463     else if (strcmp (token, "pole") == 0)
 4464       plane->model = GAMGI_PHYS_POLE;
 4465     else if (strcmp (token, "vector") == 0)
 4466       plane->model = GAMGI_PHYS_VECTOR;
 4467 
 4468     else return gamgi_io_error_value (attributes[i + 1],
 4469     gml->ml.filename, fileline, gml->ml.window);
 4470     }
 4471 
 4472   /*****************************
 4473    * projection net (optional) *
 4474    *****************************/
 4475 
 4476    else if (strcmp (attributes[i], "net") == 0)
 4477     {
 4478     net++;
 4479     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4480     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4481       return gamgi_io_error_value (attributes[i + 1],
 4482       gml->ml.filename, fileline, gml->ml.window);
 4483 
 4484     if (strcmp (token, "wulff") == 0)
 4485       plane->net = gamgi_phys_projection_wulff;
 4486     else if (strcmp (token, "schmidt") == 0)
 4487       plane->net = gamgi_phys_projection_schmidt;
 4488 
 4489     else return gamgi_io_error_value (attributes[i + 1],
 4490     gml->ml.filename, fileline, gml->ml.window);
 4491     }
 4492 
 4493   /********************
 4494    * order (optional) *
 4495    ********************/
 4496 
 4497   else if (strcmp (attributes[i], "order") == 0)
 4498     {
 4499     order++;
 4500     if (gamgi_io_token_int_scan (attributes[i + 1],
 4501     &plane->order, -INT_MAX, INT_MAX) == FALSE)
 4502       return gamgi_io_error_value (attributes[i + 1],
 4503       gml->ml.filename, fileline, gml->ml.window);
 4504     }
 4505 
 4506   /*************************************
 4507    * origin: x,y,z (optional, coupled) *
 4508    *************************************/
 4509 
 4510   else if (strcmp (attributes[i], "x") == 0)
 4511     {
 4512     position++;
 4513     if (gamgi_io_token_double_scan (attributes[i + 1],
 4514     &plane->origin[0], -DBL_MAX, DBL_MAX) == FALSE)
 4515       return gamgi_io_error_value (attributes[i + 1],
 4516       gml->ml.filename, fileline, gml->ml.window);
 4517     }
 4518 
 4519   else if (strcmp (attributes[i], "y") == 0)
 4520     {
 4521     position++;
 4522     if (gamgi_io_token_double_scan (attributes[i + 1],
 4523     &plane->origin[1], -DBL_MAX, DBL_MAX) == FALSE)
 4524       return gamgi_io_error_value (attributes[i + 1],
 4525       gml->ml.filename, fileline, gml->ml.window);
 4526     }
 4527 
 4528   else if (strcmp (attributes[i], "z") == 0)
 4529     {
 4530     position++;
 4531     if (gamgi_io_token_double_scan (attributes[i + 1],
 4532     &plane->origin[2], -DBL_MAX, DBL_MAX) == FALSE)
 4533       return gamgi_io_error_value (attributes[i + 1],
 4534       gml->ml.filename, fileline, gml->ml.window);
 4535     }
 4536 
 4537   /*************************************
 4538    * center: x,y,z (optional, coupled) *
 4539    *************************************/
 4540 
 4541   else if (strcmp (attributes[i], "center_x") == 0)
 4542     {
 4543     center++;
 4544     if (gamgi_io_token_double_scan (attributes[i + 1],
 4545     &plane->center[0], -DBL_MAX, DBL_MAX) == FALSE)
 4546       return gamgi_io_error_value (attributes[i + 1],
 4547       gml->ml.filename, fileline, gml->ml.window);
 4548     }
 4549 
 4550   else if (strcmp (attributes[i], "center_y") == 0)
 4551     {
 4552     center++;
 4553     if (gamgi_io_token_double_scan (attributes[i + 1],
 4554     &plane->center[1], -DBL_MAX, DBL_MAX) == FALSE)
 4555       return gamgi_io_error_value (attributes[i + 1],
 4556       gml->ml.filename, fileline, gml->ml.window);
 4557     }
 4558 
 4559   else if (strcmp (attributes[i], "center_z") == 0)
 4560     {
 4561     center++;
 4562     if (gamgi_io_token_double_scan (attributes[i + 1],
 4563     &plane->center[2], -DBL_MAX, DBL_MAX) == FALSE)
 4564       return gamgi_io_error_value (attributes[i + 1],
 4565       gml->ml.filename, fileline, gml->ml.window);
 4566     }
 4567 
 4568   /**********************************************
 4569    * Euler angles: e1,e2,e3 (optional, coupled) *
 4570    **********************************************/
 4571 
 4572   else if (strcmp (attributes[i], "e1") == 0)
 4573     {
 4574     angle++;
 4575     if (gamgi_io_token_double_scan (attributes[i + 1],
 4576     &euler[0], 0.0, 180.0) == FALSE)
 4577       return gamgi_io_error_value (attributes[i + 1],
 4578       gml->ml.filename, fileline, gml->ml.window);
 4579     }
 4580 
 4581   else if (strcmp (attributes[i], "e2") == 0)
 4582     {
 4583     angle++;
 4584     if (gamgi_io_token_double_scan (attributes[i + 1],
 4585     &euler[1], 0.0, 360.0) == FALSE)
 4586       return gamgi_io_error_value (attributes[i + 1],
 4587       gml->ml.filename, fileline, gml->ml.window);
 4588     }
 4589 
 4590   else if (strcmp (attributes[i], "e3") == 0)
 4591     {
 4592     angle++;
 4593     if (gamgi_io_token_double_scan (attributes[i + 1],
 4594     &euler[2], 0.0, 360.0) == FALSE)
 4595       return gamgi_io_error_value (attributes[i + 1],
 4596       gml->ml.filename, fileline, gml->ml.window);
 4597     }
 4598 
 4599   /*****************************
 4600    * red,green,blue (optional) *
 4601    *****************************/
 4602 
 4603   else if (strcmp (attributes[i], "red") == 0)
 4604     {
 4605     color++;
 4606     if (gamgi_io_token_float_scan (attributes[i + 1],
 4607     &plane->red, 0.0, 1.0) == FALSE)
 4608       return gamgi_io_error_value (attributes[i + 1],
 4609       gml->ml.filename, fileline, gml->ml.window);
 4610     }
 4611 
 4612   else if (strcmp (attributes[i], "green") == 0)
 4613     {
 4614     color++;
 4615     if (gamgi_io_token_float_scan (attributes[i + 1],
 4616     &plane->green, 0.0, 1.0) == FALSE)
 4617       return gamgi_io_error_value (attributes[i + 1],
 4618       gml->ml.filename, fileline, gml->ml.window);
 4619     }
 4620 
 4621   else if (strcmp (attributes[i], "blue") == 0)
 4622     {
 4623     color++;
 4624     if (gamgi_io_token_float_scan (attributes[i + 1],
 4625     &plane->blue, 0.0, 1.0) == FALSE)
 4626       return gamgi_io_error_value (attributes[i + 1],
 4627       gml->ml.filename, fileline, gml->ml.window);
 4628     }
 4629 
 4630   /********************
 4631    * scale (optional) *
 4632    ********************/
 4633 
 4634   else if (strcmp (attributes[i], "scale") == 0)
 4635     {
 4636     if (gamgi_io_token_double_scan (attributes[i + 1],
 4637     &plane->object.scale, GAMGI_MESA_SCALE_LOWER,
 4638     GAMGI_MESA_SCALE_UPPER) == FALSE)
 4639       return gamgi_io_error_value (attributes[i + 1], 
 4640       gml->ml.filename, fileline, gml->ml.window);
 4641     }
 4642 
 4643   else return gamgi_io_error_attribute (attributes[i], 
 4644   gml->ml.filename, fileline, gml->ml.window);
 4645   }
 4646 
 4647 /************************************
 4648  * global tests: missing attributes *
 4649  ************************************/
 4650 
 4651 /*****************************************
 4652  * global tests: incompatible attributes *
 4653  *****************************************/
 4654 
 4655 if (plane->reference == GAMGI_ENGINE_CELL)
 4656   {
 4657   if (hkl != 3)
 4658     return gamgi_io_error_incompatible (gml->ml.filename,
 4659     fileline, gml->ml.window);
 4660 
 4661   if (gamgi_phys_direction_zero (plane->hkl) == TRUE)
 4662     return gamgi_io_error_incompatible (gml->ml.filename,
 4663     fileline, gml->ml.window);
 4664   }
 4665 
 4666 if (plane->reference == GAMGI_ENGINE_ATOM)
 4667   {
 4668   if (hkl != 0 || vectors != 0 || net != 0 || order != 0)
 4669     return gamgi_io_error_incompatible (gml->ml.filename,
 4670     fileline, gml->ml.window);
 4671 
 4672   if (plane->model == GAMGI_PHYS_POLE || plane->model == GAMGI_PHYS_TRACE
 4673   || plane->model == GAMGI_PHYS_VECTOR)
 4674     return gamgi_io_error_incompatible (gml->ml.filename,
 4675     fileline, gml->ml.window);
 4676 
 4677   if (plane->net != NULL)
 4678     return gamgi_io_error_incompatible (gml->ml.filename,
 4679     fileline, gml->ml.window);
 4680 
 4681   if (plane->model == FALSE) plane->model = GAMGI_PHYS_POLYGON;
 4682   }
 4683 
 4684 if (parent == 0 && gamgi_expat_import_plane (plane,
 4685 object, gml->ml.filename, fileline, gml) == FALSE) return FALSE;
 4686 
 4687 /*****************************
 4688  * check position parameters *
 4689  *****************************/
 4690 
 4691 if (position == 1 || position == 2)
 4692   return gamgi_io_error_incompatible (gml->ml.filename,
 4693   fileline, gml->ml.window);
 4694 
 4695 if (center == 1 || center == 2)
 4696   return gamgi_io_error_incompatible (gml->ml.filename,
 4697   fileline, gml->ml.window);
 4698 
 4699 if (angle == 1 || angle == 2)
 4700   return gamgi_io_error_incompatible (gml->ml.filename,
 4701   fileline, gml->ml.window);
 4702 
 4703 /********************************************************
 4704  * by default origin is 0,0,0 and quaternion is 0,0,0,1 *
 4705  * so it is not needed to initialize both before using  *
 4706  * them to temporarily store the origin and euler data. *
 4707  ********************************************************/
 4708 
 4709 /******************************
 4710  * global: default attributes *
 4711  ******************************/
 4712 
 4713 if (strcmp (plane->object.name, "") == 0)
 4714   {
 4715   if (plane->reference == GAMGI_ENGINE_CELL)
 4716     sprintf (token, "(%d.%d.%d)", plane->hkl[0], plane->hkl[1], plane->hkl[2]);
 4717   else
 4718     sprintf (token, "Plane");
 4719 
 4720   strcpy (plane->object.name, token);
 4721   }
 4722 
 4723 /************************************************
 4724  * the plane (vector, pole, trace, polygon) can *
 4725  * be built only when the cell parent is known  *
 4726  ************************************************/
 4727 
 4728 /*********************************************************************
 4729  * rotation/translation data is now set (origin, center, quaternion) *
 4730  *********************************************************************/
 4731 
 4732 gamgi_math_euler_to_matrix (euler, rotation);
 4733 gamgi_math_quaternion_from_matrix (rotation, plane->quaternion);
 4734 
 4735 return gml->ml.valid;
 4736 }
 4737 
 4738 static gamgi_bool static_direction_end (const char *element, gamgi_gml *gml)
 4739 {
 4740 gml->ml.parent = gamgi_engine_slist_remove_start (gml->ml.parent);
 4741 
 4742 return gml->ml.valid;
 4743 }
 4744 
 4745 static gamgi_bool static_direction_start (const char *element,
 4746 const char **attributes, gamgi_gml *gml)
 4747 {
 4748 gamgi_direction *direction;
 4749 gamgi_direction_class *direction_class;
 4750 gamgi_object *object;
 4751 double rotation[9], euler[3];
 4752 double points[6];
 4753 char token[GAMGI_ENGINE_TOKEN];
 4754 int fileline, i;
 4755 int parent = 0;
 4756 int xyz = 0;
 4757 int uvw = 0, color = 0;
 4758 int vectors = 0, net = 0;
 4759 int o123 = 0, o4 = 0;
 4760 int position = 0, center = 0, angle = 0;
 4761 unsigned int hash;
 4762 
 4763 /******************************************
 4764  * used only in error messages: file line *
 4765  * indicates where current element starts *
 4766  ******************************************/
 4767 
 4768 fileline = XML_GetCurrentLineNumber (gml->ml.parser);
 4769 
 4770 /******************************
 4771  * parent list: check, update *
 4772  ******************************/
 4773 
 4774 if (gamgi_expat_import_parent_gml (GAMGI_ENGINE_DIRECTION, gml) == FALSE)
 4775   return gamgi_io_error_child (gml->ml.filename, 
 4776   fileline, gml->ml.window);
 4777 
 4778 /*****************************************************
 4779  * direction_class contains the current config info, *
 4780  * which is gamgi->direction unless new config info  *
 4781  * has been previously stored in object->object.     *
 4782  *****************************************************/
 4783 
 4784 object = GAMGI_CAST_OBJECT gamgi->direction;
 4785 if (object->object == NULL) direction_class = gamgi->direction;
 4786 else direction_class = (gamgi_direction_class *) object->object;
 4787 
 4788 /*****************
 4789  * object create *
 4790  *****************/
 4791 
 4792 direction = gamgi_engine_create_direction ();
 4793 gamgi_engine_start_direction (direction);
 4794 gamgi_mesa_start_direction (direction, direction_class);
 4795 
 4796 /***************************************************************
 4797  * by default, each new direction belongs to the current layer *
 4798  ***************************************************************/
 4799 
 4800 object = GAMGI_CAST_OBJECT gml->ml.parent->data;
 4801 if (object == NULL) object = GAMGI_CAST_OBJECT gml->ml.window->layer;
 4802 direction->object.object = object;
 4803 
 4804 /************************
 4805  * object queue: update *
 4806  ************************/
 4807 
 4808 gml->object_end = gamgi_engine_dlist_add_end (gml->object_end);
 4809 gml->object_end->data = direction;
 4810 
 4811 /************************
 4812  * parent stack: update *
 4813  ************************/
 4814 
 4815 gml->ml.parent = gamgi_engine_slist_add_start (gml->ml.parent);
 4816 gml->ml.parent->data = direction;
 4817 
 4818 /**********************************
 4819  * attribute array: check, update *
 4820  **********************************/
 4821 
 4822 for (i = 0; attributes[i] != NULL; i += 2)
 4823   {
 4824   /*******************
 4825    * name (optional) *
 4826    *******************/
 4827 
 4828   if (strcmp (attributes[i], "name") == 0)
 4829     {
 4830     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4831     direction->object.name, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4832       return gamgi_io_error_value (attributes[i + 1], 
 4833       gml->ml.filename, fileline, gml->ml.window);
 4834     }
 4835 
 4836   /*****************
 4837    * id (optional) *
 4838    *****************/
 4839 
 4840   else if (strcmp (attributes[i], "id") == 0)
 4841     {
 4842     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4843     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4844       return gamgi_io_error_value (attributes[i + 1], 
 4845       gml->ml.filename, fileline, gml->ml.window);
 4846 
 4847     hash = gamgi_math_hash_value (token,
 4848     GAMGI_EXPAT_HASH_MULTIPLIER, GAMGI_EXPAT_HASH_SIZE);
 4849 
 4850     if (gamgi_expat_import_id_get (token, gml, hash) != NULL)
 4851       return gamgi_io_error_multiple (gml->ml.filename, 
 4852       fileline, gml->ml.window);
 4853 
 4854     gamgi_expat_import_id_add (token, gml, hash);
 4855     }
 4856 
 4857   /*********************
 4858    * parent (optional) *
 4859    *********************/
 4860 
 4861   else if (strcmp (attributes[i], "parent") == 0)
 4862     {
 4863     parent++;
 4864     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4865     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4866       return gamgi_io_error_value (attributes[i + 1],
 4867       gml->ml.filename, fileline, gml->ml.window);
 4868 
 4869     gamgi_expat_import_ref_add (token, gml);
 4870     }
 4871 
 4872   /************************
 4873    * reference (optional) *
 4874    ***********************/
 4875 
 4876   else if (strcmp (attributes[i], "reference") == 0)
 4877     {
 4878     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4879     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4880       return gamgi_io_error_value (attributes[i + 1],
 4881       gml->ml.filename, fileline, gml->ml.window);
 4882 
 4883     if (strcmp (token, "cell") == 0)
 4884       direction->reference = GAMGI_ENGINE_CELL;
 4885     else if (strcmp (token, "atoms") == 0)
 4886       direction->reference = GAMGI_ENGINE_ATOM;
 4887 
 4888     else return gamgi_io_error_value (attributes[i + 1],
 4889     gml->ml.filename, fileline, gml->ml.window);
 4890     }
 4891 
 4892   /******************
 4893    * uvw (optional) *
 4894    ******************/
 4895 
 4896   else if (strcmp (attributes[i], "u") == 0)
 4897     {
 4898     uvw++;
 4899     if (gamgi_io_token_int_scan (attributes[i + 1],
 4900     &direction->uvw[0], -INT_MAX, INT_MAX) == FALSE)
 4901       return gamgi_io_error_value (attributes[i + 1],
 4902       gml->ml.filename, fileline, gml->ml.window);
 4903     }
 4904 
 4905   else if (strcmp (attributes[i], "v") == 0)
 4906     {
 4907     uvw++;
 4908     if (gamgi_io_token_int_scan (attributes[i + 1],
 4909     &direction->uvw[1], -INT_MAX, INT_MAX) == FALSE)
 4910       return gamgi_io_error_value (attributes[i + 1],
 4911       gml->ml.filename, fileline, gml->ml.window);
 4912     }
 4913 
 4914   else if (strcmp (attributes[i], "w") == 0)
 4915     {
 4916     uvw++;
 4917     if (gamgi_io_token_int_scan (attributes[i + 1],
 4918     &direction->uvw[2], -INT_MAX, INT_MAX) == FALSE)
 4919       return gamgi_io_error_value (attributes[i + 1],
 4920       gml->ml.filename, fileline, gml->ml.window);
 4921     }
 4922 
 4923   /**********************
 4924    * vectors (optional) *
 4925    **********************/
 4926 
 4927   else if (strcmp (attributes[i], "vectors") == 0)
 4928     {
 4929     vectors++;
 4930     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4931     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4932       return gamgi_io_error_value (attributes[i + 1],
 4933       gml->ml.filename, fileline, gml->ml.window);
 4934 
 4935     if (strcmp (token, "conventional") == 0)
 4936       direction->vectors = GAMGI_PHYS_CONVENTIONAL;
 4937     else if (strcmp (token, "primitive") == 0)
 4938       direction->vectors = GAMGI_PHYS_PRIMITIVE;
 4939 
 4940     else return gamgi_io_error_value (attributes[i + 1],
 4941     gml->ml.filename, fileline, gml->ml.window);
 4942     }
 4943 
 4944   /********************
 4945    * model (optional) *
 4946    ********************/
 4947 
 4948   else if (strcmp (attributes[i], "model") == 0)
 4949     {
 4950     if (gamgi_io_token_alpha_scan (attributes[i + 1], token,
 4951     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4952       return gamgi_io_error_value (attributes[i + 1],
 4953       gml->ml.filename, fileline, gml->ml.window);
 4954 
 4955     if (strcmp (token, "line") == 0)
 4956       direction->model = GAMGI_PHYS_LINE;
 4957     else if (strcmp (token, "trace") == 0)
 4958       direction->model = GAMGI_PHYS_TRACE;
 4959     else if (strcmp (token, "pole") == 0)
 4960       direction->model = GAMGI_PHYS_POLE;
 4961 
 4962     else return gamgi_io_error_value (attributes[i + 1],
 4963     gml->ml.filename, fileline, gml->ml.window);
 4964     }
 4965 
 4966   /*****************************
 4967    * projection net (optional) *
 4968    *****************************/
 4969 
 4970    else if (strcmp (attributes[i], "net") == 0)
 4971     {
 4972     net++;
 4973     if (gamgi_io_token_alpha_scan (attributes[i + 1],
 4974     token, GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 4975       return gamgi_io_error_value (attributes[i + 1],
 4976       gml->ml.filename, fileline, gml->ml.window);
 4977 
 4978     if (strcmp (token, "wulff") == 0)
 4979       direction->net = gamgi_phys_projection_wulff;
 4980     else if (strcmp (token, "schmidt") == 0)
 4981       direction->net = gamgi_phys_projection_schmidt;
 4982 
 4983     else return gamgi_io_error_value (attributes[i + 1],
 4984     gml->ml.filename, fileline, gml->ml.window);
 4985     }
 4986 
 4987   /***************************************
 4988    * o1,o2,o3 offset (optional, coupled) *
 4989    ***************************************/
 4990 
 4991   else if (strcmp (attributes[i], "o1") == 0)
 4992     {
 4993     o123++;
 4994     if (gamgi_io_token_int_scan (attributes[i + 1],
 4995     &direction->node[0], -INT_MAX, INT_MAX) == FALSE)
 4996       return gamgi_io_error_value (attributes[i + 1],
 4997       gml->ml.filename, fileline, gml->ml.window);
 4998     }
 4999 
 5000   else if (strcmp (attributes[i], "o2") == 0)
 5001     {
 5002     o123++;
 5003     if (gamgi_io_token_int_scan (attributes[i + 1],
 5004     &direction->node[1], -INT_MAX, INT_MAX) == FALSE)
 5005       return gamgi_io_error_value (attributes[i + 1],
 5006       gml->ml.filename, fileline, gml->ml.window);
 5007     }
 5008 
 5009   else if (strcmp (attributes[i], "o3") == 0)
 5010     {
 5011     o123++;
 5012     if (gamgi_io_token_int_scan (attributes[i + 1],
 5013     &direction->node[2], -INT_MAX, INT_MAX) == FALSE)
 5014       return gamgi_io_error_value (attributes[i + 1],
 5015       gml->ml.filename, fileline, gml->ml.window);
 5016     }
 5017 
 5018   /************************
 5019    * o4 offset (optional) *
 5020    ************************/
 5021 
 5022   else if (strcmp (attributes[i], "o4") == 0)
 5023     {
 5024     o4++;
 5025     if (gamgi_io_token_alpha_scan (attributes[i + 1], token,
 5026     GAMGI_IO_TEXT, GAMGI_ENGINE_TOKEN) == FALSE)
 5027       return gamgi_io_error_value (attributes[i + 1],
 5028       gml->ml.filename, fileline, gml->ml.window);
 5029 
 5030     if (strcmp (token, "000") == 0)
 5031       direction->node[3] = FALSE;
 5032     else if (strcmp (token, "011") == 0)
 5033       direction->node[3] = GAMGI_PHYS_011;
 5034     else if (strcmp (token, "101") == 0)
 5035       direction->node[3] = GAMGI_PHYS_101;
 5036     else if (strcmp (token, "110") == 0)
 5037       direction->node[3] = GAMGI_PHYS_110;
 5038     else if (strcmp (token, "111") == 0)
 5039       direction->node[3] = GAMGI_PHYS_111;
 5040     else if (strcmp (token, "122") == 0)
 5041       direction->node[3] = GAMGI_PHYS_122;
 5042     else if (strcmp (token, "211") == 0)
 5043       direction->node[3] = GAMGI_PHYS_211;
 5044    
 5045     else return gamgi_io_error_value (attributes[i + 1],
 5046     gml->ml.filename, fileline, gml->ml.window);
 5047     }
 5048 
 5049   /**************************************************
 5050    * segment: x0,y0,z0,x1,y1,z1 (required, coupled) *
 5051    **************************************************/
 5052 
 5053   else if (strcmp (attributes[i], "begin_x") == 0)
 5054     {
 5055     xyz++;
 5056     if (gamgi_io_token_double_scan (attributes[i + 1],
 5057     &points[0], -DBL_MAX, DBL_MAX) == FALSE)
 5058       return gamgi_io_error_value (attributes[i + 1],
 5059       gml->ml.filename, fileline, gml->ml.window);
 5060     }
 5061 
 5062   else if (strcmp (attributes[i], "begin_y") == 0)
 5063     {
 5064     xyz++;
 5065     if (gamgi_io_token_double_scan (attributes[i + 1],
 5066     &points[1], -DBL_MAX, DBL_MAX) == FALSE)
 5067       return gamgi_io_error_value (attributes[i + 1],
 5068       gml->ml.filename, fileline, gml->ml.window);
 5069     }
 5070 
 5071   else if (strcmp (attributes[i], "begin_z") == 0)
 5072     {
 5073     xyz++;
 5074     if (gamgi_io_token_double_scan (attributes[i + 1],
 5075     &points[2], -DBL_MAX, DBL_MAX) == FALSE)
 5076       return gamgi_io_error_value (attributes[i + 1],
 5077       gml->ml.filename, fileline, gml->ml.window);
 5078     }
 5079 
 5080   else if (strcmp (attributes[i], "end_x") == 0)
 5081     {
 5082     xyz++;
 5083     if (gamgi_io_token_double_scan (attributes[i + 1],
 5084     &points[3], -DBL_MAX, DBL_MAX) == FALSE)
 5085       return gamgi_io_error_value (attributes[i + 1],
 5086       gml->ml.filename, fileline, gml->ml.window);
 5087     }
 5088 
 5089   else if (strcmp (attributes[i], "end_y") == 0)
 5090     {
 5091     xyz++;
 5092     if (gamgi_io_token_double_scan (attributes[i + 1],
 5093     &points[4], -DBL_MAX, DBL_MAX) == FALSE)
 5094       return gamgi_io_error_value (attributes[i + 1],
 5095       gml->ml.filename, fileline, gml->ml.window);
 5096     }
 5097 
 5098   else if (strcmp (attributes[i], "end_z") == 0)
 5099     {
 5100     xyz++;
 5101     if (gamgi_io_token_double_scan (attributes[i + 1],
 5102     &points[5], -DBL_MAX, DBL_MAX) == FALSE)
 5103       return gamgi_io_error_value (attributes[i + 1],
 5104       gml->ml.filename, fileline, gml->ml.window);
 5105     }
 5106 
 5107    /*************************************
 5108    * origin: x,y,z (optional, coupled) *
 5109    *************************************/
 5110 
 5111   else if (strcmp (attributes[i], "x") == 0)
 5112     {
 5113     position++;
 5114     if (gamgi_io_token_double_scan (attributes[i + 1],
 5115     &direction->origin[0], -DBL_MAX, DBL_MAX) == FALSE)
 5116       return gamgi_io_error_value (attributes[i + 1],
 5117       gml->ml.filename, fileline, gml->ml.window);
 5118     }
 5119 
 5120   else if (strcmp (attributes[i], "y") == 0)
 5121     {
 5122     position++;
 5123     if (gamgi_io_token_double_scan (attributes[i + 1],
 5124     &direction->origin[1], -DBL_MAX, DBL_MAX) == FALSE)
 5125       return gamgi_io_error_value (attributes[i + 1],
 5126       gml->ml.filename, fileline, gml->ml.window);
 5127     }
 5128 
 5129   else if (strcmp (attributes[i], "z") == 0)
 5130     {
 5131     position++;
 5132     if (gamgi_io_token_double_scan (attributes[i + 1],
 5133     &direction->origin[2], -DBL_MAX, DBL_MAX) == FALSE)
 5134       return gamgi_io_error_value (attributes[i + 1],
 5135       gml->ml.filename, fileline, gml->ml.window);
 5136     }
 5137 
 5138   /*************************************
 5139    * center: x,y,z (optional, coupled) *
 5140    *************************************/
 5141 
 5142   else if (strcmp (attributes[i], "center_x") == 0)
 5143     {
 5144     center++;
 5145     if (gamgi_io_token_double_scan (attributes[i + 1],
 5146     &direction->center[0], -DBL_MAX, DBL_MAX) == FALSE)
 5147       return gamgi_io_error_value (attributes[i + 1],
 5148       gml->ml.filename, fileline, gml->ml.window);
 5149     }
 5150 
 5151   else if (strcmp (attributes[i], "center_y") == 0)
 5152     {
 5153     center++;
 5154     if (gamgi_io_token_double_scan (attributes[i +