home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1996 February / PCWK0296.iso / sharewar / dos / program / gs300sr1 / gs300sr1.exe / ZMISC2.C < prev    next >
C/C++ Source or Header  |  1994-07-27  |  19KB  |  647 lines

  1. /* Copyright (C) 1992, 1993, 1994 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of Aladdin Ghostscript.
  4.   
  5.   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  6.   or distributor accepts any responsibility for the consequences of using it,
  7.   or for whether it serves any particular purpose or works at all, unless he
  8.   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  9.   License (the "License") for full details.
  10.   
  11.   Every copy of Aladdin Ghostscript must include a copy of the License,
  12.   normally in a plain ASCII text file named PUBLIC.  The License grants you
  13.   the right to copy, modify and redistribute Aladdin Ghostscript, but only
  14.   under certain conditions described in the License.  Among other things, the
  15.   License requires that the copyright notice and this notice be preserved on
  16.   all copies.
  17. */
  18.  
  19. /* zmisc2.c */
  20. /* Miscellaneous Level 2 operators */
  21. #include "memory_.h"
  22. #include "string_.h"
  23. #include "ghost.h"
  24. #include "errors.h"
  25. #include "oper.h"
  26. #include "gsfont.h"
  27. #include "gsutil.h"
  28. #include "estack.h"
  29. #include "ialloc.h"        /* for imemory for status */
  30. #include "idict.h"
  31. #include "idparam.h"
  32. #include "iparam.h"
  33. #include "dstack.h"
  34. #include "ilevel.h"
  35. #include "iname.h"
  36. #include "iutil2.h"
  37. #include "store.h"
  38.  
  39. /* The (global) font directory */
  40. extern gs_font_dir *ifont_dir;        /* in zfont.c */
  41.  
  42. /* Import the GC parameters from zvmem2.c. */
  43. extern int set_vm_reclaim(P1(long));
  44. extern int set_vm_threshold(P1(long));
  45.  
  46. /* Import built-in parameter values from iinit.c. */
  47. extern const long gs_buildtime;
  48.  
  49. /* Import the Level 1 'where' operator from zdict.c. */
  50. extern int zwhere(P1(os_ptr));
  51.  
  52. /* The system passwords. */
  53. private password StartJobPassword = NULL_PASSWORD;
  54. password SystemParamsPassword = NULL_PASSWORD;    /* exported for ziodev2.c. */
  55.  
  56. /* Define an individual user or system parameter. */
  57. /* Eventually this will be made public. */
  58. #define param_def_common\
  59.     const char _ds *pname
  60. typedef struct param_def_s {
  61.     param_def_common;
  62. } param_def;
  63. typedef struct long_param_def_s {
  64.     param_def_common;
  65.     long min_value, max_value;
  66.     long (*current)(P0());
  67.     int (*set)(P1(long));
  68. } long_param_def;
  69. #if arch_sizeof_long > arch_sizeof_int
  70. #  define max_uint_param max_uint
  71. #else
  72. #  define max_uint_param max_long
  73. #endif
  74. typedef struct bool_param_def_s {
  75.     param_def_common;
  76.     bool (*current)(P0());
  77.     int (*set)(P1(bool));
  78. } bool_param_def;
  79. typedef struct string_param_def_s {
  80.     param_def_common;
  81.     void (*current)(P1(gs_param_string *));
  82.     int (*set)(P1(gs_param_string *));
  83. } string_param_def;
  84. /* Define a parameter set (user or system). */
  85. typedef struct param_set_s {
  86.     const long_param_def *long_defs;
  87.       uint long_count;
  88.     const bool_param_def *bool_defs;
  89.       uint bool_count;
  90.     const string_param_def *string_defs;
  91.       uint string_count;
  92. } param_set;
  93.  
  94. /* Forward references */
  95. private int set_language_level(P1(int));
  96. private int currentparams(P2(os_ptr, const param_set _ds *));
  97.  
  98. /* ------ Language level operators ------ */
  99.  
  100. /* - .languagelevel <1 or 2> */
  101. private int
  102. zlanguagelevel(register os_ptr op)
  103. {    push(1);
  104.     ref_assign(op, &ref_language_level);
  105.     return 0;
  106. }
  107.  
  108. /* <1 or 2> .setlanguagelevel - */
  109. private int
  110. zsetlanguagelevel(register os_ptr op)
  111. {    int code = 0;
  112.     check_type(*op, t_integer);
  113.     if ( op->value.intval < 1 || op->value.intval > 2 )
  114.         return_error(e_rangecheck);
  115.     if ( op->value.intval != ref_language_level.value.intval )
  116.     {    code = set_language_level((int)op->value.intval);
  117.         if ( code < 0 ) return code;
  118.     }
  119.     pop(1);
  120.     ref_assign_old(NULL, &ref_language_level, op, "setlanguagelevel");
  121.     return code;
  122. }
  123.  
  124. /* ------ Passwords ------ */
  125.  
  126. #define plist ((gs_param_list *)&list)
  127.  
  128. /* <string|int> .checkpassword <0|1|2> */
  129. private int
  130. zcheckpassword(register os_ptr op)
  131. {    ref params[2];
  132.     array_param_list list;
  133.     int result = 0;
  134.     int code = name_ref((const byte *)"Password", 8, ¶ms[0], 0);
  135.     if ( code < 0 )
  136.       return code;
  137.     params[1] = *op;
  138.     array_param_list_read(&list, params, 2, NULL);
  139.     if ( param_check_password(plist, &StartJobPassword) == 0 )
  140.         result = 1;
  141.     if ( param_check_password(plist, &SystemParamsPassword) == 0 )
  142.         result = 2;
  143.     make_int(op, result);
  144.     return 0;
  145. }
  146.  
  147. /* ------ System parameters ------ */
  148.  
  149. /* Integer values */
  150. private long
  151. current_BuildTime(void)
  152. {    return gs_buildtime;
  153. }
  154. private long
  155. current_MaxFontCache(void)
  156. {    uint cstat[7];
  157.     gs_cachestatus(ifont_dir, cstat);
  158.     return cstat[1];
  159. }
  160. private long
  161. current_CurFontCache(void)
  162. {    uint cstat[7];
  163.     gs_cachestatus(ifont_dir, cstat);
  164.     return cstat[0];
  165. }
  166. private const long_param_def system_long_params[] = {
  167.     {"BuildTime", 0, max_uint_param, current_BuildTime, NULL},
  168.     {"MaxFontCache", 0, max_uint_param, current_MaxFontCache, NULL},
  169.     {"CurFontCache", 0, max_uint_param, current_CurFontCache, NULL}
  170. };
  171. /* Boolean values */
  172. private bool
  173. current_ByteOrder(void)
  174. {    return !arch_is_big_endian;
  175. }
  176. private const bool_param_def system_bool_params[] = {
  177.     {"ByteOrder", current_ByteOrder, NULL}
  178. };
  179. /* String values */
  180. private void
  181. current_RealFormat(gs_param_string *pval)
  182. {
  183. #if arch_floats_are_IEEE
  184.     static const char *rfs = "IEEE";
  185. #else
  186.     static const char *rfs = "not IEEE";
  187. #endif
  188.     pval->data = (const byte *)rfs;
  189.     pval->size = strlen(rfs);
  190.     pval->persistent = true;
  191. }
  192. private const string_param_def system_string_params[] = {
  193.     {"RealFormat", current_RealFormat, NULL}
  194. };
  195.  
  196. /* The system parameter set */
  197. private const param_set system_param_set = {
  198.     system_long_params, countof(system_long_params),
  199.     system_bool_params, countof(system_bool_params),
  200.     system_string_params, countof(system_string_params)
  201. };
  202.  
  203.  
  204. /* <dict> setsystemparams - */
  205. private int
  206. zsetsystemparams(register os_ptr op)
  207. {    int code;
  208.     dict_param_list list;
  209.     int ival;
  210.     password pass;
  211.     code = dict_param_list_read(&list, op, NULL);
  212.     if ( code < 0 )
  213.       return code;
  214.     code = param_check_password(plist, &SystemParamsPassword);
  215.     if ( code != 0 )
  216.       return_error(code < 0 ? code : e_invalidaccess);
  217.     code = param_read_password(plist, "StartJobPassword", &pass);
  218.     switch ( code )
  219.     {
  220.     default:            /* invalid */
  221.         return code;
  222.     case 1:                /* missing */
  223.         break;
  224.     case 0:
  225.         StartJobPassword = pass;
  226.     }
  227.     code = param_read_password(plist, "SystemParamsPassword", &pass);
  228.     switch ( code )
  229.     {
  230.     default:            /* invalid */
  231.         return code;
  232.     case 1:                /* missing */
  233.         break;
  234.     case 0:
  235.         SystemParamsPassword = pass;
  236.     }
  237.     code = dict_int_param(op, "MaxFontCache", 0, max_int, 0, &ival);
  238.     switch ( code )
  239.     {
  240.     default:            /* invalid */
  241.         return code;
  242.     case 1:                /* missing */
  243.         break;
  244.     case 0:
  245.         /****** NOT IMPLEMENTED YET ******/
  246.         ;
  247.     }
  248.     pop(1);
  249.     return 0;
  250. }
  251.  
  252. /* - .currentsystemparams <name1> <value1> ... */
  253. private int
  254. zcurrentsystemparams(os_ptr op)
  255. {    return currentparams(op, &system_param_set);
  256. }
  257.  
  258. /* ------ User parameters ------ */
  259.  
  260. /* Integer values */
  261. private long
  262. current_JobTimeout(void)
  263. {    return 0;
  264. }
  265. private int
  266. set_JobTimeout(long val)
  267. {    return 0;
  268. }
  269. private long
  270. current_MaxFontItem(void)
  271. {    return gs_currentcacheupper(ifont_dir);
  272. }
  273. private int
  274. set_MaxFontItem(long val)
  275. {    return gs_setcacheupper(ifont_dir, val);
  276. }
  277. private long
  278. current_MinFontCompress(void)
  279. {    return gs_currentcachelower(ifont_dir);
  280. }
  281. private int
  282. set_MinFontCompress(long val)
  283. {    return gs_setcachelower(ifont_dir, val);
  284. }
  285. private long
  286. current_MaxOpStack(void)
  287. {    return ref_stack_max_count(&o_stack);
  288. }
  289. private int
  290. set_MaxOpStack(long val)
  291. {    return ref_stack_set_max_count(&o_stack, val);
  292. }
  293. private long
  294. current_MaxDictStack(void)
  295. {    return ref_stack_max_count(&d_stack);
  296. }
  297. private int
  298. set_MaxDictStack(long val)
  299. {    return ref_stack_set_max_count(&d_stack, val);
  300. }
  301. private long
  302. current_MaxExecStack(void)
  303. {    return ref_stack_max_count(&e_stack);
  304. }
  305. private int
  306. set_MaxExecStack(long val)
  307. {    return ref_stack_set_max_count(&e_stack, val);
  308. }
  309. private long
  310. current_MaxLocalVM(void)
  311. {    gs_memory_gc_status_t stat;
  312.     gs_memory_gc_status(iimemory_local, &stat);
  313.     return stat.max_vm;
  314. }
  315. private int
  316. set_MaxLocalVM(long val)
  317. {    gs_memory_gc_status_t stat;
  318.     gs_memory_gc_status(iimemory_local, &stat);
  319.     stat.max_vm = max(val, 0);
  320.     gs_memory_set_gc_status(iimemory_local, &stat);
  321.     return 0;
  322. }
  323. private long
  324. current_VMReclaim(void)
  325. {    gs_memory_gc_status_t gstat, lstat;
  326.     gs_memory_gc_status(iimemory_global, &gstat);
  327.     gs_memory_gc_status(iimemory_local, &lstat);
  328.     return (!gstat.enabled ? -2 : !lstat.enabled ? -1 : 0);
  329. }
  330. private long
  331. current_VMThreshold(void)
  332. {    gs_memory_gc_status_t stat;
  333.     gs_memory_gc_status(iimemory_local, &stat);
  334.     return stat.vm_threshold;
  335. }
  336. #define current_WaitTimeout current_JobTimeout
  337. #define set_WaitTimeout set_JobTimeout
  338. private const long_param_def user_long_params[] = {
  339.     {"JobTimeout", 0, max_uint_param,
  340.        current_JobTimeout, set_JobTimeout},
  341.     {"MaxFontItem", 0, max_uint_param,
  342.        current_MaxFontItem, set_MaxFontItem},
  343.     {"MinFontCompress", 0, max_uint_param,
  344.        current_MinFontCompress, set_MinFontCompress},
  345.     {"MaxOpStack", 0, max_uint_param,
  346.        current_MaxOpStack, set_MaxOpStack},
  347.     {"MaxDictStack", 0, max_uint_param,
  348.        current_MaxDictStack, set_MaxDictStack},
  349.     {"MaxExecStack", 0, max_uint_param,
  350.        current_MaxExecStack, set_MaxExecStack},
  351.     {"MaxLocalVM", 0, max_uint_param,
  352.        current_MaxLocalVM, set_MaxLocalVM},
  353.     {"VMReclaim", -2, 0,
  354.        current_VMReclaim, set_vm_reclaim},
  355.     {"VMThreshold", 0, max_uint_param,
  356.        current_VMThreshold, set_vm_threshold},
  357.     {"WaitTimeout", 0, max_uint_param,
  358.        current_WaitTimeout, set_WaitTimeout}
  359. };
  360. /* Boolean values */
  361. private bool
  362. current_AccurateScreens(void)
  363. {    return false;
  364. }
  365. private int
  366. set_AccurateScreens(bool val)
  367. {    return 0;
  368. }
  369. private const bool_param_def user_bool_params[] = {
  370.     {"AccurateScreens", current_AccurateScreens, set_AccurateScreens}
  371. };
  372. /* String values */
  373. private void
  374. current_JobName(gs_param_string *pval)
  375. {    pval->data = 0;
  376.     pval->size = 0;
  377.     pval->persistent = true;
  378. }
  379. private int
  380. set_JobName(gs_param_string *val)
  381. {    return 0;
  382. }
  383. private const string_param_def user_string_params[] = {
  384.     {"JobName", current_JobName, set_JobName}
  385. };
  386.  
  387. /* The user parameter set */
  388. private const param_set user_param_set = {
  389.     user_long_params, countof(user_long_params),
  390.     user_bool_params, countof(user_bool_params),
  391.     user_string_params, countof(user_string_params)
  392. };
  393.  
  394. /* <dict> setuserparams - */
  395. private int
  396. zsetuserparams(register os_ptr op)
  397. {    dict_param_list list;
  398.     int code = dict_param_list_read(&list, op, NULL);
  399.     int i;
  400.     if ( code < 0 )
  401.       return code;
  402.     for ( i = 0; i < countof(user_long_params); i++ )
  403.       {    const long_param_def *pdef = &user_long_params[i];
  404.         if ( pdef->set != NULL )
  405.           {    long val;
  406.             int code = param_read_long((gs_param_list *)&list,
  407.                            pdef->pname, &val);
  408.             switch ( code )
  409.               {
  410.               default:            /* invalid */
  411.                 return code;
  412.               case 1:            /* missing */
  413.                 break;
  414.               case 0:
  415.                 if ( val < pdef->min_value ||
  416.                  val > pdef->max_value
  417.                    )
  418.                   return_error(e_rangecheck);
  419.                 code = (*pdef->set)(val);
  420.                 if ( code < 0 )
  421.                   return code;
  422.               }
  423.           }
  424.       }
  425.     pop(1);
  426.     return 0;
  427. }
  428.  
  429. /* - .currentuserparams <name1> <value1> ... */
  430. private int
  431. zcurrentuserparams(os_ptr op)
  432. {    return currentparams(op, &user_param_set);
  433. }
  434.  
  435. /* ------ The 'where' hack ------ */
  436.  
  437. private int
  438. z2where(register os_ptr op)
  439. {    /*
  440.      * Aldus Freehand versions 2.x check for the presence of the
  441.      * setcolor operator, and if it is missing, substitute a procedure.
  442.      * Unfortunately, the procedure takes different parameters from
  443.      * the operator.  As a result, files produced by this application
  444.      * cause an error if the setcolor operator is actually defined
  445.      * and 'bind' is ever used.  Aldus fixed this bug in Freehand 3.0,
  446.      * but there are a lot of files created by the older versions
  447.      * still floating around.  Therefore, at Adobe's suggestion,
  448.      * we implement the following dreadful hack in the 'where' operator:
  449.      *    If the key is /setcolor, and
  450.      *      there is a dictionary named FreeHandDict, and
  451.      *      currentdict is that dictionary,
  452.      *    then "where" consults only that dictionary and not any other
  453.      *      dictionaries on the dictionary stack.
  454.      */
  455.     ref rkns, rfh;
  456.     const ref *pdref = dsp;
  457.     ref *pvalue;
  458.     if ( !r_has_type(op, t_name) ||
  459.          (name_string_ref(op, &rkns), r_size(&rkns)) != 8 ||
  460.          memcmp(rkns.value.bytes, "setcolor", 8) != 0 ||
  461.          name_ref((const byte *)"FreeHandDict", 12, &rfh, -1) < 0 ||
  462.          (pvalue = dict_find_name(&rfh)) == 0 ||
  463.          !obj_eq(pvalue, pdref)
  464.        )
  465.         return zwhere(op);
  466.     check_dict_read(*pdref);
  467.     if ( dict_find(pdref, op, &pvalue) > 0 )
  468.     {    ref_assign(op, pdref);
  469.         push(1);
  470.         make_true(op);
  471.     }
  472.     else
  473.         make_false(op);
  474.     return 0;
  475. }
  476.  
  477. /* ------ Initialization procedure ------ */
  478.  
  479. /* The level setting ops are recognized even in Level 1 mode. */
  480. op_def zmisc2_op_defs[] = {
  481.     {"0.languagelevel", zlanguagelevel},
  482.     {"1.setlanguagelevel", zsetlanguagelevel},
  483.         /* The rest of the operators are defined only in Level 2. */
  484.         op_def_begin_level2(),
  485.     {"1.checkpassword", zcheckpassword},
  486.     {"0.currentsystemparams", zcurrentsystemparams},
  487.     {"0.currentuserparams", zcurrentuserparams},
  488.     {"1setsystemparams", zsetsystemparams},
  489.     {"1setuserparams", zsetuserparams},
  490. /* Note that this overrides the definition in zdict.c. */
  491.     {"1where", z2where},
  492.     op_def_end(0)
  493. };
  494.  
  495. /* ------ Internal procedures ------ */
  496.  
  497. /* Get the current values of a parameter set to the stack. */
  498. private int
  499. currentparams(os_ptr op, const param_set _ds *pset)
  500. {    stack_param_list list;
  501.     int i;
  502.     stack_param_list_write(&list, &o_stack, NULL);
  503.     for ( i = 0; i < pset->long_count; i++ )
  504.     {    long val = (*pset->long_defs[i].current)();
  505.         int code = param_write_long((gs_param_list *)&list,
  506.                         pset->long_defs[i].pname, &val);
  507.         if ( code < 0 )
  508.           return code;
  509.     }
  510.     for ( i = 0; i < pset->bool_count; i++ )
  511.     {    bool val = (*pset->bool_defs[i].current)();
  512.         int code = param_write_bool((gs_param_list *)&list,
  513.                         pset->bool_defs[i].pname, &val);
  514.         if ( code < 0 )
  515.           return code;
  516.     }
  517.     for ( i = 0; i < pset->string_count; i++ )
  518.     {    gs_param_string val;
  519.         int code;
  520.         (*pset->string_defs[i].current)(&val);
  521.         code = param_write_string((gs_param_list *)&list,
  522.                       pset->string_defs[i].pname, &val);
  523.         if ( code < 0 )
  524.           return code;
  525.     }
  526.     return 0;
  527. }
  528.  
  529. /* Adjust the interpreter for a change in language level. */
  530. /* This is used for the .setlanguagelevel operator, */
  531. /* and after a restore. */
  532. private int swap_entry(P3(ref elt[2], ref *dict, ref *dict2));
  533. private int
  534. set_language_level(int level)
  535. {    ref *pgdict =        /* globaldict, if present */
  536.       ref_stack_index(&d_stack, ref_stack_count(&d_stack) - 2);
  537.     ref *level2dict;
  538.     if (dict_find_string(systemdict, "level2dict", &level2dict) <= 0)
  539.           return_error(e_undefined);          
  540.     /* As noted in dstack.h, we allocate the extra d-stack entry */
  541.     /* for globaldict even in Level 1 mode; in Level 1 mode, */
  542.     /* this entry holds an extra copy of systemdict, and */
  543.     /* [count]dictstack omit the very bottommost entry. */
  544.     if ( level == 2 )        /* from Level 1 to Level 2 */
  545.     {    /* Put globaldict in the dictionary stack. */
  546.         ref *pdict;
  547.         int code = dict_find_string(level2dict, "globaldict", &pdict);
  548.         if ( code <= 0 )
  549.             return_error(e_undefined);
  550.         if ( !r_has_type(pdict, t_dictionary) )
  551.             return_error(e_typecheck);
  552.         *pgdict = *pdict;
  553.         /* Set other flags for Level 2 operation. */
  554.         dict_auto_expand = true;
  555.     }
  556.     else                /* from Level 2 to Level 1 */
  557.     {    /* Clear the cached definition pointers of all names */
  558.         /* defined in globaldict.  This will slow down */
  559.         /* future lookups, but we don't care. */
  560.         int index = dict_first(pgdict);
  561.         ref elt[2];
  562.         while ( (index = dict_next(pgdict, index, &elt[0])) >= 0 )
  563.           if ( r_has_type(&elt[0], t_name) )
  564.             elt[0].value.pname->pvalue = pv_other;
  565.         /* Overwrite globaldict in the dictionary stack. */
  566.         *pgdict = *systemdict;
  567.         /* Set other flags for Level 1 operation. */
  568.         dict_auto_expand = false;
  569.     }
  570.     /* Swap the contents of level2dict and systemdict. */
  571.     /* If a value in level2dict is a dictionary, and it contains */
  572.     /* a key/value pair referring to itself, swap its contents */
  573.     /* with the contents of the same dictionary in systemdict. */
  574.     /* (This is a hack to swap the contents of statusdict.) */
  575.     {    int index = dict_first(level2dict);
  576.         ref elt[2];        /* key, value */
  577.         ref *subdict;
  578.         while ( (index = dict_next(level2dict, index, &elt[0])) >= 0 )
  579.           if ( r_has_type(&elt[1], t_dictionary) &&
  580.                dict_find(&elt[1], &elt[0], &subdict) > 0 &&
  581.                obj_eq(&elt[1], subdict)
  582.              )
  583.         {
  584. #define sub2dict &elt[1]
  585.             int isub = dict_first(&elt[1]);
  586.             ref subelt[2];
  587.             int found = dict_find(systemdict, &elt[0], &subdict);
  588.             if ( found <= 0 )
  589.               continue;
  590.             while ( (isub = dict_next(sub2dict, isub, &subelt[0])) >= 0 )
  591.             {    int code = swap_entry(subelt, subdict, sub2dict);
  592.                 if ( code < 0 )
  593.                   return code;
  594.             }
  595. #undef sub2dict
  596.         }
  597.           else
  598.         {    int code = swap_entry(elt, systemdict, level2dict);
  599.             if ( code < 0 )
  600.               return code;
  601.         }
  602.     }
  603.     dict_set_top();        /* reload dict stack cache */
  604.     return 0;
  605. }
  606.  
  607. /* Swap an entry from a Level 2 dictionary into a base dictionary. */
  608. private int
  609. swap_entry(ref elt[2], ref *pdict, ref *pdict2)
  610. {    ref *pvalue;
  611.     ref old_value;
  612.     int found = dict_find(pdict, &elt[0], &pvalue);
  613.     switch ( found )
  614.     {
  615.     default:        /* <0, error */
  616.         return found;
  617.     case 0:            /* missing */
  618.         make_null(&old_value);
  619.         break;
  620.     case 1:            /* present */
  621.         old_value = *pvalue;
  622.     }
  623.     /*
  624.      * Temporarily flag the dictionaries as local, so that we don't
  625.      * get invalidaccess errors.  (We know that they are both
  626.      * referenced from systemdict, so they are allowed to reference
  627.      * local objects even if they are global.)
  628.      */
  629.     {    bool local2 = r_has_attr(pdict2, a_local);
  630.         int code;
  631.         r_set_attrs(pdict2, a_local);
  632.         dict_put(pdict2, &elt[0], &old_value);
  633.         if ( r_has_type(&elt[1], t_null) )
  634.             code = dict_undef(pdict, &elt[0]);
  635.         else
  636.         {    bool local = r_has_attr(pdict, a_local);
  637.             r_set_attrs(pdict, a_local);
  638.             code = dict_put(pdict, &elt[0], &elt[1]);
  639.             if ( !local )
  640.                 r_clear_attrs(pdict, a_local);
  641.         }
  642.         if ( !local2 )
  643.             r_clear_attrs(pdict2, a_local);
  644.         return code;
  645.     }
  646. }
  647.