#define PERL_NO_GET_CONTEXT /* we want efficiency */
#include "EXTERN.h"
#include "perl.h"
#include "glog.h"
#include "gmem.h"
#include "header.h"
#include "util.h"
/* Append string str at pos in buf. */
static int string_append(char* buf, int pos, const char* str);
/* Cleanup string str (as used in as_string), leaving cleaned up result in */
/* buf, with maximum length len; use newl as new line terminator. */
static int string_cleanup(const char* str, char* buf, int len, const char* newl);
void set_value(pTHX_ HList* h, const char* ckey, SV* pval) {
SV *deref;
AV *array;
if ( ! SvOK(pval) ) {
GLOG(("=X= deleting [%s]", ckey));
hlist_del( h, ckey );
return;
}
if ( ! SvROK(pval) ) {
set_scalar(aTHX_ h, ckey, pval);
return;
}
deref = SvRV(pval);
if (SvTYPE(deref) != SVt_PVAV) {
set_scalar(aTHX_ h, ckey, pval);
return;
}
array = (AV*) deref;
set_array(aTHX_ h, ckey, array);
}
void set_scalar(pTHX_ HList* h, const char* ckey, SV* pval) {
hlist_add(h, ckey, newSVsv(pval));
GLOG(("=X= set scalar [%s] => [%s]", ckey, SvPV_nolen(pval)));
}
void set_array(pTHX_ HList* h, const char* ckey, AV* pval) {
int count = av_len(pval) + 1;
int j;
for (j = 0; j < count; ++j) {
SV** svp = av_fetch(pval, j, 0);
GLOG(("=X= set array %2d [%s]", j, ckey));
set_value(aTHX_ h, ckey, *svp);
}
}
void return_hlist(pTHX_ HList* list, const char* func, int want) {
dSP;
int count;
if (want == G_VOID) {
GLOG(("=X= %s: no return expected, nothing will be returned", func));
return;
}
count = hlist_size(list);
if (want == G_SCALAR) {
GLOG(("=X= %s: returning number of elements", func));
EXTEND(SP, 1);
PUSHs(sv_2mortal(newSViv(count)));
PUTBACK;
}
if (count <= 0) {
GLOG(("=X= %s: hlist is empty, returning nothing", func));
return;
}
if (want == G_ARRAY) {
int num = 0;
int j;
GLOG(("=X= %s: returning as %d elements", func, count));
EXTEND(SP, count);
for (j = 0; j < list->ulen; ++j) {
HNode* node = &list->data[j];
const char* s = node->header->name;
++num;
GLOG(("=X= %s: returning %2d - str [%s]", func, num, s));
PUSHs(sv_2mortal(newSVpv(s, 0)));
}
PUTBACK;
}
}
void return_plist(pTHX_ PList* list, const char* func, int want) {
int count;
dSP;
if (want == G_VOID) {
GLOG(("=X= %s: no return expected, nothing will be returned", func));
return;
}
count = plist_size(list);
if (count <= 0) {
if (want == G_ARRAY) {
GLOG(("=X= %s: plist is empty, wantarray => 0", func));
EXTEND(SP, 1);
PUSHs(sv_2mortal(newSViv(0)));
PUTBACK;
} else {
GLOG(("=X= %s: plist is empty, returning nothing", func));
}
return;
}
GLOG(("=X= %s: returning %d values", func, count));
if (want == G_SCALAR) {
GLOG(("=X= %s: returning as single string", func));
EXTEND( SP, 1 );
if ( count == 1 ) {
/*
* handle returning one value, useful when storing an object
*/
PNode* node = &list->data[0];
PUSHs( (SV*)node->ptr );
} else {
/*
* concatenate values, useful for full header strings
*/
int size = 16;
int j;
for (j = 0; j < list->ulen; ++j) {
PNode* node = &list->data[j];
STRLEN len;
SvPV( (SV*)node->ptr, len ); /* We just need the length */
size += len + 2;
}
{
char* rstr;
int rpos = 0;
int num = 0;
int j;
GMEM_NEW(rstr, char*, size);
for (j = 0; j < list->ulen; ++j) {
PNode* node = &list->data[j];
++num;
STRLEN len;
char* str = SvPV( (SV*)node->ptr, len );
GLOG(("=X= %s: returning %2d - str [%s]", func, num, str));
if (rpos > 0) {
rstr[rpos++] = ',';
rstr[rpos++] = ' ';
}
memcpy(rstr + rpos, str, len);
rpos += len;
}
rstr[rpos] = '\0';
PUSHs(sv_2mortal(newSVpv(rstr, rpos)));
GMEM_DEL(rstr, char*, size);
}
}
PUTBACK;
}
if (want == G_ARRAY) {
int num = 0;
int j;
GLOG(("=X= %s: returning as %d elements", func, count));
EXTEND(SP, count);
for (j = 0; j < list->ulen; ++j) {
PNode* node = &list->data[j];
++num;
PUSHs( (SV*)node->ptr );
}
PUTBACK;
}
}
char* format_all(pTHX_ HList* h, int sort, const char* endl, int* size) {
int le = strlen(endl);
int j;
*size = 64;
if (sort) {
hlist_sort(h);
}
for (j = 0; j < h->ulen; ++j) {
HNode* hn = &h->data[j];
const char* header = hn->header->name;
int lh = strlen(header);
PList* pl = hn->values;
int k;
for (k = 0; k < pl->ulen; ++k) {
PNode* pn = &pl->data[k];
const char* value = SvPV_nolen( (SV*) pn->ptr );
int lv = strlen(value);
*size += lh + 2 + lv + lv * le;
}
}
{
char* rstr;
int rpos = 0;
GMEM_NEW(rstr, char*, *size);
for (j = 0; j < h->ulen; ++j) {
HNode* hn = &h->data[j];
const char* header = hn->header->name;
int lh = strlen(header);
PList* pl = hn->values;
int k;
PNode *pn;
const char *value;
for (k = 0; k < pl->ulen; ++k) {
memcpy(rstr + rpos, header, lh);
rpos += lh;
rstr[rpos++] = ':';
rstr[rpos++] = ' ';
pn = &pl->data[k];
value = SvPV_nolen( (SV*) pn->ptr );
rpos += string_cleanup(value, rstr + rpos, *size - rpos, endl);
}
}
rstr[rpos] = '\0';
GLOG(("=X= format_all (%d/%d) [%s]", rpos, *size, rstr));
return rstr;
}
}
static int string_append(char* buf, int pos, const char* str) {
int k;
for (k = 0; str[k] != '\0'; ++k) {
buf[pos++] = str[k];
}
return pos;
}
static int string_cleanup(const char* str, char* buf, int len, const char* newl) {
int pos = 0;
int last_nonblank = -1;
int saw_newline = 0;
int j;
for (j = 0; str[j] != '\0'; ++j) {
if (pos >= len) {
break;
}
if (isspace(str[j])) {
if (saw_newline) {
/* ignore */
} else {
if (str[j] == '\n') {
pos = string_append(buf, pos, newl);
saw_newline = 1;
last_nonblank = pos-1;
} else {
buf[pos++] = str[j];
}
}
} else {
if (saw_newline) {
buf[pos++] = ' ';
}
buf[pos++] = str[j];
last_nonblank = pos-1;
saw_newline = 0;
}
}
if (! saw_newline) {
pos = string_append(buf, last_nonblank+1, newl);
last_nonblank = pos-1;
}
buf[++last_nonblank] = '\0';
return last_nonblank;
/*
* This is the original code in Perl, for reference.
sub _process_newline {
local $_ = shift;
my $endl = shift;
# must handle header values with embedded newlines with care
s/\s+$//; # trailing newlines and space must go
s/\n(\x0d?\n)+/\n/g; # no empty lines
s/\n([^\040\t])/\n $1/g; # intial space for continuation
s/\n/$endl/g; # substitute with requested line ending
$_;
*/
}