+
+OP *
+do_kv(ARGS)
+dARGS
+{
+ dSP;
+ HV *hv = (HV*)POPs;
+ register AV *ary = stack;
+ I32 i;
+ register HE *entry;
+ char *tmps;
+ SV *tmpstr;
+ I32 dokeys = (op->op_type == OP_KEYS || op->op_type == OP_RV2HV);
+ I32 dovalues = (op->op_type == OP_VALUES || op->op_type == OP_RV2HV);
+
+ if (!hv)
+ RETURN;
+ if (GIMME != G_ARRAY) {
+ dTARGET;
+
+ if (!SvMAGICAL(hv) || !mg_find((SV*)hv,'P'))
+ i = HvKEYS(hv);
+ else {
+ i = 0;
+ (void)hv_iterinit(hv);
+ /*SUPPRESS 560*/
+ while (entry = hv_iternext(hv)) {
+ i++;
+ }
+ }
+ PUSHi( i );
+ RETURN;
+ }
+
+ /* Guess how much room we need. hv_max may be a few too many. Oh well. */
+ EXTEND(sp, HvMAX(hv) * (dokeys + dovalues));
+
+ (void)hv_iterinit(hv);
+
+ PUTBACK; /* hv_iternext and hv_iterval might clobber stack_sp */
+ while (entry = hv_iternext(hv)) {
+ SPAGAIN;
+ if (dokeys) {
+ tmps = hv_iterkey(entry,&i); /* won't clobber stack_sp */
+ if (!i)
+ tmps = "";
+ XPUSHs(sv_2mortal(newSVpv(tmps,i)));
+ }
+ if (dovalues) {
+ tmpstr = NEWSV(45,0);
+ PUTBACK;
+ sv_setsv(tmpstr,hv_iterval(hv,entry));
+ SPAGAIN;
+ DEBUG_H( {
+ sprintf(buf,"%d%%%d=%d\n",entry->hent_hash,
+ HvMAX(hv)+1,entry->hent_hash & HvMAX(hv));
+ sv_setpv(tmpstr,buf);
+ } )
+ XPUSHs(sv_2mortal(tmpstr));
+ }
+ PUTBACK;
+ }
+ return NORMAL;
+}
+