int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint8_t mlen, uint32_t value) { struct radix_node_head *rnh; struct table_entry *ent; struct radix_node *rn; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); rnh = ch->tables[tbl]; ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); if (ent == NULL) return (ENOMEM); ent->value = value; KEY_LEN(ent->addr) = KEY_LEN(ent->mask) = 8; ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; IPFW_WLOCK(ch); rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); if (rn == NULL) { IPFW_WUNLOCK(ch); free(ent, M_IPFW_TBL); return (EEXIST); } IPFW_WUNLOCK(ch); return (0); }
/*! \brief Check for equality. */ static bool hhelem_isequal(hhelem_t *elm, const char *key, uint16_t len) { uint16_t klen; memcpy(&klen, KEY_LEN(elm->d), sizeof(klen)); if (klen != len) { return false; } return memcmp(KEY_STR(elm->d), key, len) == 0; }
int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, uint32_t *val, int type) { struct radix_node_head *rnh; struct table_xentry *xent; struct sockaddr_in6 sa6; struct xaddr_iface iface; if (tbl >= V_fw_tables_max) return (0); if ((rnh = ch->xtables[tbl]) == NULL) return (0); switch (type) { case IPFW_TABLE_CIDR: KEY_LEN(sa6) = KEY_LEN_INET6; memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh)); break; case IPFW_TABLE_INTERFACE: KEY_LEN(iface) = KEY_LEN_IFACE + strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1; /* Assume direct match */ /* FIXME: Add interface pattern matching */ xent = (struct table_xentry *)(rnh->rnh_matchaddr(&iface, rnh)); break; default: return (0); } if (xent != NULL) { *val = xent->value; return (1); } return (0); }
int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint8_t mlen) { struct radix_node_head *rnh; struct table_entry *ent; struct sockaddr_in sa, mask; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); rnh = ch->tables[tbl]; KEY_LEN(sa) = KEY_LEN(mask) = 8; mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; IPFW_WLOCK(ch); ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); if (ent == NULL) { IPFW_WUNLOCK(ch); return (ESRCH); } IPFW_WUNLOCK(ch); free(ent, M_IPFW_TBL); return (0); }
/* * ex_prchars -- * Local routine to dump characters to the screen. */ static int ex_prchars(SCR *sp, const CHAR_T *p, size_t *colp, size_t len, u_int flags, int repeatc) { CHAR_T ch; const char *kp; size_t col, tlen, ts; if (O_ISSET(sp, O_LIST)) LF_SET(E_C_LIST); ts = O_VAL(sp, O_TABSTOP); for (col = *colp; len--;) if ((ch = *p++) == L('\t') && !LF_ISSET(E_C_LIST)) for (tlen = ts - col % ts; col < sp->cols && tlen--; ++col) { (void)ex_printf(sp, "%c", repeatc ? repeatc : ' '); if (INTERRUPTED(sp)) goto intr; } else { /* XXXX */ if (INTISWIDE(ch)) { CHAR_T str[2] = {0, 0}; str[0] = ch; INT2CHAR(sp, str, 2, kp, tlen); } else { kp = (char *)KEY_NAME(sp, ch); tlen = KEY_LEN(sp, ch); } if (!repeatc && col + tlen < sp->cols) { (void)ex_puts(sp, kp); col += tlen; } else for (; tlen--; ++kp, ++col) { if (col == sp->cols) { col = 0; (void)ex_puts(sp, "\n"); } (void)ex_printf(sp, "%c", repeatc ? repeatc : *kp); if (INTERRUPTED(sp)) goto intr; } } intr: *colp = col; return (0); }
int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint32_t *val) { struct radix_node_head *rnh; struct table_entry *ent; struct sockaddr_in sa; if (tbl >= IPFW_TABLES_MAX) return (0); rnh = ch->tables[tbl]; KEY_LEN(sa) = 8; sa.sin_addr.s_addr = addr; ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); if (ent != NULL) { *val = ent->value; return (1); } return (0); }
int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint32_t *val) { struct radix_node_head *rnh; struct table_entry *ent; struct sockaddr_in sa; if (tbl >= V_fw_tables_max) return (0); if ((rnh = ch->tables[tbl]) == NULL) return (0); KEY_LEN(sa) = KEY_LEN_INET; sa.sin_addr.s_addr = addr; ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh)); if (ent != NULL) { *val = ent->value; return (1); } return (0); }
/* * msgq_status -- * Report on the file's status. * * PUBLIC: void msgq_status(SCR *, recno_t, u_int); */ void msgq_status( SCR *sp, recno_t lno, u_int flags) { recno_t last; size_t blen, len; int cnt, needsep; const char *t; char **ap, *bp, *np, *p, *s, *ep; CHAR_T *wp; size_t wlen; /* Get sufficient memory. */ len = strlen(sp->frp->name); GET_SPACE_GOTOC(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128); p = bp; ep = bp + blen; /* Convert the filename. */ CHAR2INT(sp, sp->frp->name, len + 1, wp, wlen); /* Copy in the filename. */ for (; *wp != '\0'; ++wp) { len = KEY_LEN(sp, *wp); memcpy(p, KEY_NAME(sp, *wp), len); p += len; } np = p; *p++ = ':'; *p++ = ' '; /* Copy in the argument count. */ if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) { for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt); if (cnt > 1) { (void)snprintf(p, ep - p, msg_cat(sp, "317|%d files to edit", NULL), cnt); p += strlen(p); *p++ = ':'; *p++ = ' '; } F_CLR(sp, SC_STATUS_CNT); } /* * See nvi/exf.c:file_init() for a description of how and when the * read-only bit is set. * * !!! * The historic display for "name changed" was "[Not edited]". */ needsep = 0; if (F_ISSET(sp->frp, FR_NEWFILE)) { F_CLR(sp->frp, FR_NEWFILE); t = msg_cat(sp, "021|new file", &len); memcpy(p, t, len); p += len; needsep = 1; } else { if (F_ISSET(sp->frp, FR_NAMECHANGE)) { t = msg_cat(sp, "022|name changed", &len); memcpy(p, t, len); p += len; needsep = 1; } if (needsep) { *p++ = ','; *p++ = ' '; } if (F_ISSET(sp->ep, F_MODIFIED)) t = msg_cat(sp, "023|modified", &len); else t = msg_cat(sp, "024|unmodified", &len); memcpy(p, t, len); p += len; needsep = 1; } if (F_ISSET(sp->frp, FR_UNLOCKED)) { if (needsep) { *p++ = ','; *p++ = ' '; } t = msg_cat(sp, "025|UNLOCKED", &len); memcpy(p, t, len); p += len; needsep = 1; } if (O_ISSET(sp, O_READONLY)) { if (needsep) { *p++ = ','; *p++ = ' '; } t = msg_cat(sp, "026|readonly", &len); memcpy(p, t, len); p += len; needsep = 1; } if (needsep) { *p++ = ':'; *p++ = ' '; } if (LF_ISSET(MSTAT_SHOWLAST)) { if (db_last(sp, &last)) return; if (last == 0) { t = msg_cat(sp, "028|empty file", &len); memcpy(p, t, len); p += len; } else { t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len); (void)snprintf(p, ep - p, t, (u_long)lno, (u_long)last, ((u_long)lno * 100) / last); p += strlen(p); } } else { t = msg_cat(sp, "029|line %lu", &len); (void)snprintf(p, ep - p, t, (u_long)lno); p += strlen(p); } #ifdef DEBUG (void)snprintf(p, ep - p, " (pid %lu)", (u_long)getpid()); p += strlen(p); #endif *p++ = '\n'; len = p - bp; /* * There's a nasty problem with long path names. Cscope and tags files * can result in long paths and vi will request a continuation key from * the user as soon as it starts the screen. Unfortunately, the user * has already typed ahead, and chaos results. If we assume that the * characters in the filenames and informational messages only take a * single screen column each, we can trim the filename. * * XXX * Status lines get put up at fairly awkward times. For example, when * you do a filter read (e.g., :read ! echo foo) in the top screen of a * split screen, we have to repaint the status lines for all the screens * below the top screen. We don't want users having to enter continue * characters for those screens. Make it really hard to screw this up. */ s = bp; if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) { for (; s < np && (*s != '/' || (p - s) > sp->cols - 3); ++s); if (s == np) { s = p - (sp->cols - 5); *--s = ' '; } *--s = '.'; *--s = '.'; *--s = '.'; len = p - s; } /* Flush any waiting ex messages. */ (void)ex_fflush(sp); sp->gp->scr_msg(sp, M_INFO, s, len); FREE_SPACE(sp, bp, blen); alloc_err: return; }
int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, uint8_t plen, uint8_t mlen, uint8_t type) { struct radix_node_head *rnh, **rnh_ptr; struct table_entry *ent; in_addr_t addr; struct sockaddr_in sa, mask; struct sockaddr *sa_ptr, *mask_ptr; char c; if (tbl >= V_fw_tables_max) return (EINVAL); switch (type) { case IPFW_TABLE_CIDR: if (plen == sizeof(in_addr_t)) { /* Set 'total' structure length */ KEY_LEN(sa) = KEY_LEN_INET; KEY_LEN(mask) = KEY_LEN_INET; mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); addr = *((in_addr_t *)paddr); sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; rnh_ptr = &ch->tables[tbl]; sa_ptr = (struct sockaddr *)&sa; mask_ptr = (struct sockaddr *)&mask; #ifdef INET6 } else if (plen == sizeof(struct in6_addr)) { /* IPv6 case */ if (mlen > 128) return (EINVAL); struct sockaddr_in6 sa6, mask6; memset(&sa6, 0, sizeof(struct sockaddr_in6)); memset(&mask6, 0, sizeof(struct sockaddr_in6)); /* Set 'total' structure length */ KEY_LEN(sa6) = KEY_LEN_INET6; KEY_LEN(mask6) = KEY_LEN_INET6; ipv6_writemask(&mask6.sin6_addr, mlen); memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr); rnh_ptr = &ch->xtables[tbl]; sa_ptr = (struct sockaddr *)&sa6; mask_ptr = (struct sockaddr *)&mask6; #endif } else { /* Unknown CIDR type */ return (EINVAL); } break; case IPFW_TABLE_INTERFACE: /* Check if string is terminated */ c = ((char *)paddr)[IF_NAMESIZE - 1]; ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) return (EINVAL); struct xaddr_iface ifname, ifmask; memset(&ifname, 0, sizeof(ifname)); /* Include last \0 into comparison */ mlen++; /* Set 'total' structure length */ KEY_LEN(ifname) = KEY_LEN_IFACE + mlen; KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen; /* Assume direct match */ /* FIXME: Add interface pattern matching */ #if 0 memset(ifmask.ifname, 0xFF, IF_NAMESIZE); mask_ptr = (struct sockaddr *)&ifmask; #endif mask_ptr = NULL; memcpy(ifname.ifname, paddr, mlen); /* Set pointers */ rnh_ptr = &ch->xtables[tbl]; sa_ptr = (struct sockaddr *)&ifname; break; default: return (EINVAL); } IPFW_WLOCK(ch); if ((rnh = *rnh_ptr) == NULL) { IPFW_WUNLOCK(ch); return (ESRCH); } if (ch->tabletype[tbl] != type) { IPFW_WUNLOCK(ch); return (EINVAL); } ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh); IPFW_WUNLOCK(ch); if (ent == NULL) return (ESRCH); free(ent, M_IPFW_TBL); return (0); }
int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) { struct radix_node_head *rnh, **rnh_ptr; struct table_entry *ent; struct table_xentry *xent; struct radix_node *rn; in_addr_t addr; int offset; void *ent_ptr; struct sockaddr *addr_ptr, *mask_ptr; char c; if (tbl >= V_fw_tables_max) return (EINVAL); switch (type) { case IPFW_TABLE_CIDR: if (plen == sizeof(in_addr_t)) { #ifdef INET /* IPv4 case */ if (mlen > 32) return (EINVAL); ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); ent->value = value; /* Set 'total' structure length */ KEY_LEN(ent->addr) = KEY_LEN_INET; KEY_LEN(ent->mask) = KEY_LEN_INET; /* Set offset of IPv4 address in bits */ offset = OFF_LEN_INET; ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); addr = *((in_addr_t *)paddr); ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; /* Set pointers */ rnh_ptr = &ch->tables[tbl]; ent_ptr = ent; addr_ptr = (struct sockaddr *)&ent->addr; mask_ptr = (struct sockaddr *)&ent->mask; #endif #ifdef INET6 } else if (plen == sizeof(struct in6_addr)) { /* IPv6 case */ if (mlen > 128) return (EINVAL); xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); xent->value = value; /* Set 'total' structure length */ KEY_LEN(xent->a.addr6) = KEY_LEN_INET6; KEY_LEN(xent->m.mask6) = KEY_LEN_INET6; /* Set offset of IPv6 address in bits */ offset = OFF_LEN_INET6; ipv6_writemask(&xent->m.mask6.sin6_addr, mlen); memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr)); APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr); /* Set pointers */ rnh_ptr = &ch->xtables[tbl]; ent_ptr = xent; addr_ptr = (struct sockaddr *)&xent->a.addr6; mask_ptr = (struct sockaddr *)&xent->m.mask6; #endif } else { /* Unknown CIDR type */ return (EINVAL); } break; case IPFW_TABLE_INTERFACE: /* Check if string is terminated */ c = ((char *)paddr)[IF_NAMESIZE - 1]; ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) return (EINVAL); /* Include last \0 into comparison */ mlen++; xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); xent->value = value; /* Set 'total' structure length */ KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen; KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen; /* Set offset of interface name in bits */ offset = OFF_LEN_IFACE; memcpy(xent->a.iface.ifname, paddr, mlen); /* Assume direct match */ /* TODO: Add interface pattern matching */ #if 0 memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE); mask_ptr = (struct sockaddr *)&xent->m.ifmask; #endif /* Set pointers */ rnh_ptr = &ch->xtables[tbl]; ent_ptr = xent; addr_ptr = (struct sockaddr *)&xent->a.iface; mask_ptr = NULL; break; default: return (EINVAL); } IPFW_WLOCK(ch); /* Check if tabletype is valid */ if ((ch->tabletype[tbl] != 0) && (ch->tabletype[tbl] != type)) { IPFW_WUNLOCK(ch); free(ent_ptr, M_IPFW_TBL); return (EINVAL); } /* Check if radix tree exists */ if ((rnh = *rnh_ptr) == NULL) { IPFW_WUNLOCK(ch); /* Create radix for a new table */ if (!rn_inithead((void **)&rnh, offset)) { free(ent_ptr, M_IPFW_TBL); return (ENOMEM); } IPFW_WLOCK(ch); if (*rnh_ptr != NULL) { /* Tree is already attached by other thread */ rn_detachhead((void **)&rnh); rnh = *rnh_ptr; /* Check table type another time */ if (ch->tabletype[tbl] != type) { IPFW_WUNLOCK(ch); free(ent_ptr, M_IPFW_TBL); return (EINVAL); } } else { *rnh_ptr = rnh; /* * Set table type. It can be set already * (if we have IPv6-only table) but setting * it another time does not hurt */ ch->tabletype[tbl] = type; } } rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr); IPFW_WUNLOCK(ch); if (rn == NULL) { free(ent_ptr, M_IPFW_TBL); return (EEXIST); } return (0); }
/*! \brief Helper function to read key length. */ static inline uint16_t key_readlen(const void *k) { uint16_t ret; memcpy(&ret, KEY_LEN(k), sizeof(ret)); return ret; }
value_t *hhash_map(hhash_t* tbl, const char* key, uint16_t len, uint16_t mode) { if (tbl == NULL) { return NULL; } /* Find an exact match in <id, id + HOP_LEN). */ uint32_t id = hash(key, len) % tbl->size; int dist = find_match(tbl, id, key, len); if (dist <= HOP_LEN) { /* Found exact match, return value. */ hhelem_t *match = &tbl->item[(id + dist) % tbl->size]; return (value_t *)KEY_VAL(match->d); } /* We didn't find an exact match, continue only if inserting. */ if (!(mode & HHASH_INSERT)) { return NULL; } else if (tbl->weight >= tbl->size) { /* Or full table. */ return NULL; } /* Reduce distance to fit <id, id + HOP_LEN) */ dist = find_free(tbl, id); if (dist < 0) { /* Did not find any fit. */ return NULL; } int empty = (id + dist) % tbl->size; while (dist >= HOP_LEN) { dist = reduce_dist(tbl, dist, &empty); /* Couldn't reduce the distance, no fit available. */ if (dist < 0) { return NULL; } } /* Insert to given position. */ char *new_key = tbl->mm.alloc(tbl->mm.ctx, HHKEY_LEN + len); if (new_key != NULL) { memset(KEY_VAL(new_key), 0, sizeof(value_t)); memcpy(KEY_LEN(new_key), &len, sizeof(uint16_t)); memcpy(KEY_STR(new_key), key, len); } else { return NULL; } /* found free elm 'k' which is in <id, id + HOP_LEN) */ assert(tbl->item[empty].d == NULL); tbl->item[id].hop |= HOP_BIT(dist); tbl->item[empty].d = new_key; ++tbl->weight; /* Free old index. */ if (tbl->index) { if (tbl->mm.free) { free(tbl->index); } tbl->index = NULL; } return (value_t *)KEY_VAL(new_key); }
/* * vs_output -- * Output the text to the screen. */ static void vs_output(SCR *sp, mtype_t mtype, const char *line, int llen) { unsigned char *kp; GS *gp; VI_PRIVATE *vip; size_t chlen, notused; int ch, len, rlen, tlen; const char *p, *t; char *cbp, *ecbp, cbuf[128]; gp = sp->gp; vip = VIP(sp); for (p = line, rlen = llen; llen > 0;) { /* Get the next physical line. */ if ((p = memchr(line, '\n', llen)) == NULL) len = llen; else len = p - line; /* * The max is sp->cols characters, and we may have already * written part of the line. */ if (len + vip->lcontinue > sp->cols) len = sp->cols - vip->lcontinue; /* * If the first line output, do nothing. If the second line * output, draw the divider line. If drew a full screen, we * remove the divider line. If it's a continuation line, move * to the continuation point, else, move the screen up. */ if (vip->lcontinue == 0) { if (!IS_ONELINE(sp)) { if (vip->totalcount == 1) { (void)gp->scr_move(sp, LASTLINE(sp) - 1, 0); (void)gp->scr_clrtoeol(sp); (void)vs_divider(sp); F_SET(vip, VIP_DIVIDER); ++vip->totalcount; ++vip->linecount; } if (vip->totalcount == sp->t_maxrows && F_ISSET(vip, VIP_DIVIDER)) { --vip->totalcount; --vip->linecount; F_CLR(vip, VIP_DIVIDER); } } if (vip->totalcount != 0) vs_scroll(sp, NULL, SCROLL_W_QUIT); (void)gp->scr_move(sp, LASTLINE(sp), 0); ++vip->totalcount; ++vip->linecount; if (INTERRUPTED(sp)) break; } else (void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue); /* Error messages are in inverse video. */ if (mtype == M_ERR) (void)gp->scr_attr(sp, SA_INVERSE, 1); /* Display the line, doing character translation. */ #define FLUSH { \ *cbp = '\0'; \ (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \ cbp = cbuf; \ } ecbp = (cbp = cbuf) + sizeof(cbuf) - 1; for (t = line, tlen = len; tlen--; ++t) { ch = *t; /* * Replace tabs with spaces, there are places in * ex that do column calculations without looking * at <tabs> -- and all routines that care about * <tabs> do their own expansions. This catches * <tabs> in things like tag search strings. */ if (ch == '\t') ch = ' '; chlen = KEY_LEN(sp, ch); if (cbp + chlen >= ecbp) FLUSH; for (kp = KEY_NAME(sp, ch); chlen--;) *cbp++ = *kp++; } if (cbp > cbuf) FLUSH; if (mtype == M_ERR) (void)gp->scr_attr(sp, SA_INVERSE, 0); /* Clear the rest of the line. */ (void)gp->scr_clrtoeol(sp); /* If we loop, it's a new line. */ vip->lcontinue = 0; /* Reset for the next line. */ line += len; llen -= len; if (p != NULL) { ++line; --llen; } } /* Set up next continuation line. */ if (p == NULL) gp->scr_cursor(sp, ¬used, &vip->lcontinue); }
/* * vs_line -- * Update one line on the screen. * * PUBLIC: int vs_line __P((SCR *, SMAP *, size_t *, size_t *)); */ int vs_line(SCR *sp, SMAP *smp, size_t *yp, size_t *xp) { unsigned char *kp; GS *gp; SMAP *tsmp; size_t chlen = 0, cno_cnt, cols_per_screen, len, nlen; size_t offset_in_char, offset_in_line, oldx, oldy; size_t scno, skip_cols, skip_screens; int dne, is_cached, is_partial, is_tab, no_draw; int list_tab, list_dollar; CHAR_T *p; CHAR_T *cbp, *ecbp, cbuf[128]; ARG_CHAR_T ch = L('\0'); #if defined(DEBUG) && 0 vtrace(sp, "vs_line: row %u: line: %u off: %u\n", smp - HMAP, smp->lno, smp->off); #endif /* * If ex modifies the screen after ex output is already on the screen, * don't touch it -- we'll get scrolling wrong, at best. */ no_draw = 0; if (!F_ISSET(sp, SC_TINPUT_INFO) && VIP(sp)->totalcount > 1) no_draw = 1; if (F_ISSET(sp, SC_SCR_EXWROTE) && (size_t)(smp - HMAP) != LASTLINE(sp)) no_draw = 1; /* * Assume that, if the cache entry for the line is filled in, the * line is already on the screen, and all we need to do is return * the cursor position. If the calling routine doesn't need the * cursor position, we can just return. */ is_cached = SMAP_CACHE(smp); if (yp == NULL && (is_cached || no_draw)) return (0); /* * A nasty side effect of this routine is that it returns the screen * position for the "current" character. Not pretty, but this is the * only routine that really knows what's out there. * * Move to the line. This routine can be called by vs_sm_position(), * which uses it to fill in the cache entry so it can figure out what * the real contents of the screen are. Because of this, we have to * return to whereever we started from. */ gp = sp->gp; (void)gp->scr_cursor(sp, &oldy, &oldx); (void)gp->scr_move(sp, smp - HMAP, 0); /* Get the line. */ dne = db_get(sp, smp->lno, 0, &p, &len); /* * Special case if we're printing the info/mode line. Skip printing * the leading number, as well as other minor setup. The only time * this code paints the mode line is when the user is entering text * for a ":" command, so we can put the code here instead of dealing * with the empty line logic below. This is a kludge, but it's pretty * much confined to this module. * * Set the number of columns for this screen. * Set the number of chars or screens to skip until a character is to * be displayed. */ cols_per_screen = sp->cols; if (O_ISSET(sp, O_LEFTRIGHT)) { skip_screens = 0; skip_cols = smp->coff; } else { skip_screens = smp->soff - 1; skip_cols = skip_screens * cols_per_screen; } list_tab = O_ISSET(sp, O_LIST); if (F_ISSET(sp, SC_TINPUT_INFO)) list_dollar = 0; else { list_dollar = list_tab; /* * If O_NUMBER is set, the line doesn't exist and it's line * number 1, i.e., an empty file, display the line number. * * If O_NUMBER is set, the line exists and the first character * on the screen is the first character in the line, display * the line number. * * !!! * If O_NUMBER set, decrement the number of columns in the * first screen. DO NOT CHANGE THIS -- IT'S RIGHT! The * rest of the code expects this to reflect the number of * columns in the first screen, regardless of the number of * columns we're going to skip. */ if (O_ISSET(sp, O_NUMBER)) { cols_per_screen -= O_NUMBER_LENGTH; if ((!dne || smp->lno == 1) && skip_cols == 0) { nlen = snprintf((char*)cbuf, sizeof(cbuf), O_NUMBER_FMT, (unsigned long)smp->lno); (void)gp->scr_addstr(sp, (char*)cbuf, nlen); } } } /* * Special case non-existent lines and the first line of an empty * file. In both cases, the cursor position is 0, but corrected * as necessary for the O_NUMBER field, if it was displayed. */ if (dne || len == 0) { /* Fill in the cursor. */ if (yp != NULL && smp->lno == sp->lno) { *yp = smp - HMAP; *xp = sp->cols - cols_per_screen; } /* If the line is on the screen, quit. */ if (is_cached || no_draw) goto ret1; /* Set line cache information. */ smp->c_sboff = smp->c_eboff = 0; smp->c_scoff = smp->c_eclen = 0; /* * Lots of special cases for empty lines, but they only apply * if we're displaying the first screen of the line. */ if (skip_cols == 0) { if (dne) { if (smp->lno == 1) { if (list_dollar) { ch = L('$'); goto empty; } } else { ch = L('~'); goto empty; } } else if (list_dollar) { ch = L('$'); empty: (void)gp->scr_addstr(sp, (const char *)KEY_NAME(sp, ch), KEY_LEN(sp, ch)); } } (void)gp->scr_clrtoeol(sp); (void)gp->scr_move(sp, oldy, oldx); return (0); } /* If we shortened this line in another screen, the cursor * position may have fallen off. */ if (sp->lno == smp->lno && sp->cno >= len) sp->cno = len - 1; /* * If we just wrote this or a previous line, we cached the starting * and ending positions of that line. The way it works is we keep * information about the lines displayed in the SMAP. If we're * painting the screen in the forward direction, this saves us from * reformatting the physical line for every line on the screen. This * wins big on binary files with 10K lines. * * Test for the first screen of the line, then the current screen line, * then the line behind us, then do the hard work. Note, it doesn't * do us any good to have a line in front of us -- it would be really * hard to try and figure out tabs in the reverse direction, i.e. how * many spaces a tab takes up in the reverse direction depends on * what characters preceded it. * * Test for the first screen of the line. */ if (skip_cols == 0) { smp->c_sboff = offset_in_line = 0; smp->c_scoff = offset_in_char = 0; p = &p[offset_in_line]; goto display; } /* Test to see if we've seen this exact line before. */ if (is_cached) { offset_in_line = smp->c_sboff; offset_in_char = smp->c_scoff; p = &p[offset_in_line]; /* Set cols_per_screen to 2nd and later line length. */ if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen) cols_per_screen = sp->cols; goto display; } /* Test to see if we saw an earlier part of this line before. */ if (smp != HMAP && SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) { if (tsmp->c_eclen != tsmp->c_ecsize) { offset_in_line = tsmp->c_eboff; offset_in_char = tsmp->c_eclen; } else { offset_in_line = tsmp->c_eboff + 1; offset_in_char = 0; } /* Put starting info for this line in the cache. */ smp->c_sboff = offset_in_line; smp->c_scoff = offset_in_char; p = &p[offset_in_line]; /* Set cols_per_screen to 2nd and later line length. */ if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen) cols_per_screen = sp->cols; goto display; } scno = 0; offset_in_line = 0; offset_in_char = 0; /* Do it the hard way, for leftright scrolling screens. */ if (O_ISSET(sp, O_LEFTRIGHT)) { for (; offset_in_line < len; ++offset_in_line) { chlen = (ch = (UCHAR_T)*p++) == L('\t') && !list_tab ? TAB_OFF(scno) : KEY_COL(sp, ch); if ((scno += chlen) >= skip_cols) break; } /* Set cols_per_screen to 2nd and later line length. */ cols_per_screen = sp->cols; /* Put starting info for this line in the cache. */ if (offset_in_line >= len) { smp->c_sboff = offset_in_line; smp->c_scoff = 255; } else if (scno != skip_cols) { smp->c_sboff = offset_in_line; smp->c_scoff = offset_in_char = chlen - (scno - skip_cols); --p; } else { smp->c_sboff = ++offset_in_line; smp->c_scoff = 0; } } /* Do it the hard way, for historic line-folding screens. */ else { for (; offset_in_line < len; ++offset_in_line) { chlen = (ch = (UCHAR_T)*p++) == L('\t') && !list_tab ? TAB_OFF(scno) : KEY_COL(sp, ch); if ((scno += chlen) < cols_per_screen) continue; scno -= cols_per_screen; /* Set cols_per_screen to 2nd and later line length. */ cols_per_screen = sp->cols; /* * If crossed the last skipped screen boundary, start * displaying the characters. */ if (--skip_screens == 0) break; } /* Put starting info for this line in the cache. */ if (scno != 0) { smp->c_sboff = offset_in_line; smp->c_scoff = offset_in_char = chlen - scno; --p; } else { smp->c_sboff = ++offset_in_line; smp->c_scoff = 0; } } display: /* * Set the number of characters to skip before reaching the cursor * character. Offset by 1 and use 0 as a flag value. Vs_line is * called repeatedly with a valid pointer to a cursor position. * Don't fill anything in unless it's the right line and the right * character, and the right part of the character... */ if (yp == NULL || smp->lno != sp->lno || sp->cno < offset_in_line || offset_in_line + cols_per_screen < sp->cno) { cno_cnt = 0; /* If the line is on the screen, quit. */ if (is_cached || no_draw) goto ret1; } else cno_cnt = (sp->cno - offset_in_line) + 1; /* This is the loop that actually displays characters. */ ecbp = (cbp = cbuf) + sizeof(cbuf)/sizeof(CHAR_T) - 1; for (is_partial = 0, scno = 0; offset_in_line < len; ++offset_in_line, offset_in_char = 0) { if ((ch = (UCHAR_T)*p++) == L('\t') && !list_tab) { scno += chlen = TAB_OFF(scno) - offset_in_char; is_tab = 1; } else { scno += chlen = KEY_COL(sp, ch) - offset_in_char; is_tab = 0; } /* * Only display up to the right-hand column. Set a flag if * the entire character wasn't displayed for use in setting * the cursor. If reached the end of the line, set the cache * info for the screen. Don't worry about there not being * characters to display on the next screen, its lno/off won't * match up in that case. */ if (scno >= cols_per_screen) { if (is_tab == 1) { chlen -= scno - cols_per_screen; smp->c_ecsize = smp->c_eclen = chlen; scno = cols_per_screen; } else { smp->c_ecsize = chlen; chlen -= scno - cols_per_screen; smp->c_eclen = chlen; if (scno > cols_per_screen) is_partial = 1; } smp->c_eboff = offset_in_line; /* Terminate the loop. */ offset_in_line = len; } /* * If the caller wants the cursor value, and this was the * cursor character, set the value. There are two ways to * put the cursor on a character -- if it's normal display * mode, it goes on the last column of the character. If * it's input mode, it goes on the first. In normal mode, * set the cursor only if the entire character was displayed. */ if (cno_cnt && --cno_cnt == 0 && (F_ISSET(sp, SC_TINPUT) || !is_partial)) { *yp = smp - HMAP; if (F_ISSET(sp, SC_TINPUT)) if (is_partial) *xp = scno - smp->c_ecsize; else *xp = scno - chlen; else *xp = scno - 1; if (O_ISSET(sp, O_NUMBER) && !F_ISSET(sp, SC_TINPUT_INFO) && skip_cols == 0) *xp += O_NUMBER_LENGTH; /* If the line is on the screen, quit. */ if (is_cached || no_draw) goto ret1; } /* If the line is on the screen, don't display anything. */ if (is_cached || no_draw) continue; #define FLUSH { \ *cbp = '\0'; \ (void)gp->scr_waddstr(sp, cbuf, cbp - cbuf); \ cbp = cbuf; \ } /* * Display the character. We do tab expansion here because * the screen interface doesn't have any way to set the tab * length. Note, it's theoretically possible for chlen to * be larger than cbuf, if the user set a impossibly large * tabstop. */ if (is_tab) while (chlen--) { if (cbp >= ecbp) FLUSH; *cbp++ = TABCH; } else { if (cbp + chlen >= ecbp) FLUSH; /* don't display half a wide character */ if (is_partial && CHAR_WIDTH(sp, ch) > 1) { *cbp++ = ' '; break; } /* XXXX this needs some rethinking */ if (INTISWIDE(ch)) { /* Put a space before non-spacing char. */ if (CHAR_WIDTH(sp, ch) <= 0) *cbp++ = L(' '); *cbp++ = ch; } else for (kp = KEY_NAME(sp, ch) + offset_in_char; chlen--;) *cbp++ = (u_char)*kp++; } } if (scno < cols_per_screen) { /* If didn't paint the whole line, update the cache. */ smp->c_ecsize = smp->c_eclen = KEY_LEN(sp, ch); smp->c_eboff = len - 1; /* * If not the info/mode line, and O_LIST set, and at the * end of the line, and the line ended on this screen, * add a trailing $. */ if (list_dollar) { ++scno; chlen = KEY_LEN(sp, L('$')); if (cbp + chlen >= ecbp) FLUSH; for (kp = KEY_NAME(sp, L('$')); chlen--;) *cbp++ = *kp++; } /* If still didn't paint the whole line, clear the rest. */ if (scno < cols_per_screen) (void)gp->scr_clrtoeol(sp); } /* Flush any buffered characters. */ if (cbp > cbuf) FLUSH; ret1: (void)gp->scr_move(sp, oldy, oldx); return (0); }
int main(int argc, char *argv[]) { plan(11); /* Create memory pool context. */ struct mempool *pool = mp_new(64 * 1024); knot_mm_t mm; mm.ctx = pool; mm.alloc = (knot_mm_alloc_t)mp_alloc; mm.free = NULL; /* Create hashtable */ int ret = KNOT_EOK; uint16_t len = 0; const char *key = "mykey", *cur = NULL, *prev = NULL; value_t val = (void*)0xdeadbeef, *rval = NULL; hhash_iter_t it; hhash_t *tbl = hhash_create_mm(ELEM_COUNT, &mm); ok(tbl != NULL, "hhash: create"); if (tbl == NULL) { return KNOT_ERROR; /* No point in testing further on. */ } /* Generate random keys. */ char *keys[ELEM_COUNT]; unsigned nfilled = 0; for (unsigned i = 0; i < ELEM_COUNT; ++i) { keys[i] = test_randstr_mm(&mm); } /* Insert single element. */ ret = hhash_insert(tbl, key, KEY_LEN(key), val); ok(ret == KNOT_EOK, "hhash: insert single element"); /* Retrieve nonexistent element. */ cur = "nokey"; rval = hhash_find(tbl, cur, KEY_LEN(cur)); ok(rval == NULL, "hhash: find non-existent element"); /* Retrieve single element. */ rval = hhash_find(tbl, key, KEY_LEN(key)); ok(rval != NULL, "hhash: find existing element"); /* Fill the table. */ for (unsigned i = 0; i < ELEM_COUNT; ++i) { ret = hhash_insert(tbl, keys[i], KEY_LEN(keys[i]), keys[i]); if (ret != KNOT_EOK) { nfilled = i; break; } } /* Check all keys integrity. */ unsigned nfound = 0; for (unsigned i = 0; i < nfilled; ++i) { rval = hhash_find(tbl, keys[i], KEY_LEN(keys[i])); if (!rval || memcmp(*rval, keys[i], KEY_LEN(keys[i])) != 0) { break; /* Mismatch */ } ++nfound; } is_int(nfilled, nfound, "hhash: found all inserted keys"); /* Test keys order index. */ hhash_build_index(tbl); hhash_iter_begin(tbl, &it, true); while (!hhash_iter_finished(&it)) { cur = hhash_iter_key(&it, &len); if (!str_check_sort(prev, cur)) { break; } prev = cur; int strl = strlen(cur); assert(strl + 1 == len); hhash_iter_next(&it); } ok(hhash_iter_finished(&it), "hhash: passed order index checks"); /* Retrieve all keys. */ nfound = 0; hhash_iter_begin(tbl, &it, false); while (!hhash_iter_finished(&it)) { cur = hhash_iter_key(&it, &len); if (hhash_find(tbl, cur, len) == NULL) { break; } else { ++nfound; } hhash_iter_next(&it); } ok(hhash_iter_finished(&it), "hhash: found all iterated keys"); is_int(tbl->weight, nfound, "hhash: all iterated keys found"); /* Test find less or equal. */ prev = "mykey0"; /* mykey should precede it */ hhash_find_leq(tbl, prev, KEY_LEN(prev), &rval); ok(rval && *rval == val, "hhash: find less or equal"); /* Delete key and retrieve it. */ ret = hhash_del(tbl, key, KEY_LEN(key)); ok(ret == KNOT_EOK, "hhash: remove key"); rval = hhash_find(tbl, key, KEY_LEN(key)); ok(rval == NULL, "hhash: find removed element"); /* Free all memory. */ mp_delete(mm.ctx); return KNOT_EOK; }
/* * vs_columns -- * Return the screen columns necessary to display the line, or, * if specified, the physical character column within the line. * * PUBLIC: size_t vs_columns __P((SCR *, CHAR_T *, db_recno_t, size_t *, size_t *)); */ size_t vs_columns(SCR *sp, CHAR_T *lp, db_recno_t lno, size_t *cnop, size_t *diffp) { size_t chlen, cno, curoff, last = 0, len, scno; int ch, leftright, listset; CHAR_T *p; /* * Initialize the screen offset. */ scno = 0; /* Leading number if O_NUMBER option set. */ if (O_ISSET(sp, O_NUMBER)) scno += O_NUMBER_LENGTH; /* Need the line to go any further. */ if (lp == NULL) { (void)db_get(sp, lno, 0, &lp, &len); if (len == 0) goto done; } /* Missing or empty lines are easy. */ if (lp == NULL) { done: if (diffp != NULL) /* XXX */ *diffp = 0; return scno; } /* Store away the values of the list and leftright edit options. */ listset = O_ISSET(sp, O_LIST); leftright = O_ISSET(sp, O_LEFTRIGHT); /* * Initialize the pointer into the buffer and current offset. */ p = lp; curoff = 0; /* Macro to return the display length of any signal character. */ #define CHLEN(val) (ch = *(UCHAR_T *)p++) == '\t' && \ !listset ? TAB_OFF(val) : KEY_COL(sp, ch); /* * If folding screens (the historic vi screen format), past the end * of the current screen, and the character was a tab, reset the * current screen column to 0, and the total screen columns to the * last column of the screen. Otherwise, display the rest of the * character in the next screen. */ #define TAB_RESET { \ curoff += chlen; \ if (!leftright && curoff >= sp->cols) { \ if (ch == '\t') { \ curoff = 0; \ scno -= scno % sp->cols; \ } else \ curoff -= sp->cols; \ } \ } if (cnop == NULL) while (len--) { chlen = CHLEN(curoff); last = scno; scno += chlen; TAB_RESET; } else for (cno = *cnop;; --cno) { chlen = CHLEN(curoff); last = scno; scno += chlen; TAB_RESET; if (cno == 0) break; } /* Add the trailing '$' if the O_LIST option set. */ if (listset && cnop == NULL) scno += KEY_LEN(sp, '$'); /* * The text input screen code needs to know how much additional * room the last two characters required, so that it can handle * tab character displays correctly. */ if (diffp != NULL) *diffp = scno - last; return (scno); }