/* Reader for Stata .dta files, versions 8.0, 7.0, 7/SE, 6.0 and 5.0. Based on stataread.c from the GNU R "foreign" package with the following original info: * $Id: stata_import.c,v 1.16 2006/09/24 23:56:43 allin Exp $ (c) 1999, 2000, 2001, 2002 Thomas Lumley. 2000 Saikat DebRoy The format of Stata files is documented under 'file formats' in the Stata manual. This code currently does not make use of the print format information in a .dta file (except for dates). It cannot handle files with 'int' 'float' or 'double' that differ from IEEE 4-byte integer, 4-byte real and 8-byte real respectively: it's not clear whether such files can exist. Versions of Stata before 4.0 used different file formats. This version was fairly substantially modified for gretl by Allin Cottrell, July 2005. */ #include #include #include #include #include "libgretl.h" #include "gretl_string_table.h" #include "swap_bytes.h" enum { CN_TYPE_BIG = 1, CN_TYPE_LITTLE, }; #ifdef WORDS_BIGENDIAN # define CN_TYPE_NATIVE CN_TYPE_BIG #else # define CN_TYPE_NATIVE CN_TYPE_LITTLE #endif /* Stata versions */ #define VERSION_5 0x69 #define VERSION_6 'l' #define VERSION_7 0x6e #define VERSION_7SE 111 #define VERSION_8 113 /* Stata format constants */ #define STATA_STRINGOFFSET 0x7f #define STATA_FLOAT 'f' #define STATA_DOUBLE 'd' #define STATA_INT 'l' #define STATA_SHORTINT 'i' #define STATA_BYTE 'b' /* Stata SE format constants */ #define STATA_SE_STRINGOFFSET 0 #define STATA_SE_FLOAT 254 #define STATA_SE_DOUBLE 255 #define STATA_SE_INT 253 #define STATA_SE_SHORTINT 252 #define STATA_SE_BYTE 251 /* Stata missing value codes */ #define STATA_FLOAT_NA pow(2.0, 127) #define STATA_DOUBLE_NA pow(2.0, 1023) #define STATA_INT_NA 2147483647 #define STATA_SHORTINT_NA 32767 #define STATA_BYTE_NA(b,v) ((v<8 && b==127) || b>=101) #define NA_INT -999 /* it's convenient to have these as file-scope globals */ int stata_version; int stata_endian; int swapends; static void bin_error (int *err) { fputs("binary read error\n", stderr); *err = 1; } static int read_int (FILE *fp, int naok, int *err) { int i; if (fread(&i, sizeof i, 1, fp) != 1) { bin_error(err); } if (swapends) { reverse_int(i); } return ((i == STATA_INT_NA) & !naok ? NA_INT : i); } static int read_signed_byte (FILE *fp, int naok, int *err) { signed char b; int ret; if (fread(&b, 1, 1, fp) != 1) { bin_error(err); ret = NA_INT; } else { ret = (int) b; if (!naok) { int v = abs(stata_version); if (STATA_BYTE_NA(b, v)) { ret = NA_INT; } } } return ret; } static int read_byte (FILE *fp, int *err) { unsigned char u; if (fread(&u, 1, 1, fp) != 1) { bin_error(err); return NA_INT; } return (int) u; } static int read_short (FILE *fp, int naok, int *err) { unsigned first, second; int s; first = read_byte(fp, err); second = read_byte(fp, err); if (stata_endian == CN_TYPE_BIG) { s = (first << 8) | second; } else { s = (second << 8) | first; } if (s > STATA_SHORTINT_NA) { s -= 65536; } return ((s == STATA_SHORTINT_NA) & !naok)? NA_INT : s; } static double read_double (FILE *fp, int *err) { double d; if (fread(&d, sizeof d, 1, fp) != 1) { bin_error(err); } if (swapends) { reverse_double(d); } return (d == STATA_DOUBLE_NA)? NADBL : d; } static double read_float (FILE *fp, int *err) { float f; if (fread(&f, sizeof f, 1, fp) != 1) { bin_error(err); } if (swapends) { reverse_float(f); } return (f == STATA_FLOAT_NA)? NADBL : (double) f; } static void read_string (FILE *fp, int nc, char *buf, int *err) { if (fread(buf, 1, nc, fp) != nc) { bin_error(err); } } static int get_version_and_namelen (unsigned char u, int *vnamelen) { int err = 0; switch (u) { case VERSION_5: stata_version = 5; *vnamelen = 8; break; case VERSION_6: stata_version = 6; *vnamelen = 8; break; case VERSION_7: stata_version = 7; *vnamelen = 32; break; case VERSION_7SE: stata_version = -7; *vnamelen = 32; break; case VERSION_8: stata_version = -8; /* version 8 automatically uses SE format */ *vnamelen = 32; break; default: err = 1; } return err; } #define stata_type_float(t) ((stata_version > 0 && t == STATA_FLOAT) || t == STATA_SE_FLOAT) #define stata_type_double(t) ((stata_version > 0 && t == STATA_DOUBLE) || t == STATA_SE_DOUBLE) #define stata_type_int(t) ((stata_version > 0 && t == STATA_INT) || t == STATA_SE_INT) #define stata_type_short(t) ((stata_version > 0 && t == STATA_SHORTINT) || t == STATA_SE_SHORTINT) #define stata_type_byte(t) ((stata_version > 0 && t == STATA_BYTE) || t == STATA_SE_BYTE) #define stata_type_string(t) ((stata_version > 0 && t >= STATA_STRINGOFFSET) || t <= 244) static int check_variable_types (FILE *fp, int *types, int nvar, int *nsv) { int i, err = 0; *nsv = 0; for (i=0; idescrip = malloc(len); if (dinfo->descrip != NULL) { *dinfo->descrip = '\0'; strcat(dinfo->descrip, s1); strcat(dinfo->descrip, "\n"); strcat(dinfo->descrip, s2); } else { err = 1; } return err; } static int try_fix_varname (char *name) { char test[VNAMELEN]; int err = 0; *test = 0; if (*name == '_') { strcat(test, "x"); strncat(test, name, VNAMELEN - 2); } else { strncat(test, name, VNAMELEN - 2); strcat(test, "1"); } err = check_varname(test); if (!err) { fprintf(stderr, "Warning: illegal name '%s' changed to '%s'\n", name, test); strcpy(name, test); } else { /* get the right error message in place */ check_varname(name); } return err; } /* use Stata's "date formats" to reconstruct time series information FIXME: add recognition for daily data too? (Stata dates are all zero at the start of 1960.) */ static int set_time_info (int t1, int pd, DATAINFO *dinfo) { int yr, mo, qt; if (pd == 12) { yr = (t1 / 12) + 1960; mo = t1 % 12 + 1; sprintf(dinfo->stobs, "%d:%02d", yr, mo); } else if (pd == 4) { yr = (t1 / 4) + 1960; qt = t1 % 4 + 1; sprintf(dinfo->stobs, "%d:%d", yr, qt); } else { yr = t1 + 1960; sprintf(dinfo->stobs, "%d", yr); } printf("starting obs seems to be %s\n", dinfo->stobs); dinfo->pd = pd; dinfo->structure = TIME_SERIES; dinfo->sd0 = get_date_x(dinfo->pd, dinfo->stobs); return 0; } static int read_dta_data (FILE *fp, double **Z, DATAINFO *dinfo, gretl_string_table **pst, int namelen, int *nvread, PRN *prn) { int i, j, t, clen; int labellen, nlabels, totlen; int nvar = dinfo->v - 1, nsv = 0; int soffset, pd = 0, tnum = -1; char datalabel[81], c18[18], aname[33]; int *types = NULL; char strbuf[129]; char *txt = NULL; int *off = NULL; int err = 0; labellen = (stata_version == 5)? 32 : 81; soffset = (stata_version > 0)? STATA_STRINGOFFSET : STATA_SE_STRINGOFFSET; *nvread = nvar; printf("Max length of labels = %d\n", labellen); /* data label - zero terminated string */ read_string(fp, labellen, datalabel, &err); printf("datalabel: '%s'\n", datalabel); /* file creation time - zero terminated string */ read_string(fp, 18, c18, &err); printf("timestamp: '%s'\n", c18); if (*datalabel != '\0' || *c18 != '\0') { save_dataset_info(dinfo, datalabel, c18); } /** read variable descriptors **/ /* types */ types = malloc(nvar * sizeof *types); if (types == NULL) { return E_ALLOC; } err = check_variable_types(fp, types, nvar, &nsv); if (err) { free(types); return err; } if (nsv > 0) { /* we have 1 or more non-numeric variables */ *pst = dta_make_string_table(types, nvar, nsv); } /* names */ for (i=0; ivarname[i+1], aname, VNAMELEN - 1); } } /* sortlist -- not relevant */ for (i=0; i<2*(nvar+1) && !err; i++) { read_byte(fp, &err); } /* format list (use it to identify date variables?) */ for (i=0; i= 7) { /* manual is wrong here */ clen = read_int(fp, 1, &err); } else { clen = read_short(fp, 1, &err); } for (i=0; i= 7) { clen = read_int(fp, 1, &err); } else { clen = read_short(fp, 1, &err); } if (clen != 0) { fputs(_("something strange in the file\n" "(Type 0 characteristic of nonzero length)"), stderr); fputc('\n', stderr); } } /* actual data values */ for (t=0; tn && !err; t++) { for (i=0; i= 0) { Z[v][t] = ix; } } } if (i == tnum && t == 0) { set_time_info((int) Z[v][t], pd, dinfo); } } } /* value labels (??) */ if (!err && abs(stata_version) > 5) { for (j=0; jv = nvar + 1; newinfo->n = nobs; /* time-series info?? */ err = start_new_Z(&newZ, newinfo, 0); if (err) { pputs(prn, _("Out of memory\n")); free_datainfo(newinfo); fclose(fp); return E_ALLOC; } err = read_dta_data(fp, newZ, newinfo, &st, namelen, &nvread, prn); if (err) { destroy_dataset(newZ, newinfo); if (st != NULL) { gretl_string_table_destroy(st); } } else { int nvtarg = newinfo->v - 1; if (nvread < nvtarg) { dataset_drop_last_variables(nvtarg - nvread, &newZ, newinfo); } if (fix_varname_duplicates(newinfo)) { pputs(prn, _("warning: some variable names were duplicated\n")); } if (st != NULL) { gretl_string_table_print(st, newinfo, fname, prn); } if (*pZ == NULL) { *pZ = newZ; *pdinfo = *newinfo; free(newinfo); } else { err = merge_data(pZ, pdinfo, newZ, newinfo, prn); } } fclose(fp); return err; }