*! version 1.2.4 20261103 github.com/mdroste/stata-ctools
*! Binary statistical readers; parsing and values are provided by C.
program define _cio_import, rclass
version 14.1
gettoken kind 0 : 0, parse(" ,")
local options CLEAR SNAPshot FAST
if inlist("`kind'","sas","spss","sasxport8","dbase","xls","xlsx") local options "`options' CASE(string)"
if "`kind'"=="sas" local options "`options' ENCoding(string) BCAT(string)"
if "`kind'"=="sasxport8" local options "`options' VLABFile(string)"
if "`kind'"=="spss" local options "`options' ENCoding(string) ZSAV"
if "`kind'"=="sasxport5" local options "`options' Describe NOVALlabels Member(string)"
if inlist("`kind'","xls","xlsx") local options "`options' SHEET(string) CELLRange(string) FIRSTrow ALLString ALLSTRINGFMT(string) DEScribe"
* Parse selectors as text: their variables belong to the incoming dataset.
syntax [anything(everything equalok)] [, `options']
if "`kind'"=="sasxport5" & "`describe'"!="" & ("`clear'"!="" | "`novallabels'"!="") exit 198
if "`snapshot'" != "" & "`fast'" != "" {
di as error "options snapshot and fast may not be combined"
exit 198
}
local rest `"`anything'"'
local prefix ""
local found 0
while `"`rest'"'!="" {
gettoken token rest : rest, bind quotes
if `"`token'"'=="using" {
local found 1
continue, break
}
local prefix `"`prefix' `token'"'
}
if `found' {
gettoken using extra : rest
local selection `"`prefix' `extra'"'
}
else {
gettoken using selection : anything
}
if `"`using'"'=="" exit 198
local rest = strtrim(`"`selection'"')
local wanted ""
local selectors ""
while `"`rest'"'!="" {
gettoken token rest : rest, bind quotes
if inlist(`"`token'"',"if","in") {
local selectors `"`token' `rest'"'
continue, break
}
local wanted `"`wanted' `token'"'
}
local wanted = strtrim(`"`wanted'"')
if !inlist("`kind'","sas","spss","xls","xlsx") & `"`wanted'"'!="" exit 198
if !inlist("`kind'","sas","spss") & `"`selectors'"'!="" exit 198
local ext ".sas7bdat"
if "`kind'" == "xls" local ext ".xls"
if "`kind'" == "xlsx" local ext ".xlsx"
if "`kind'" == "shp" local ext ".shp"
if "`kind'" == "dbase" local ext ".dbf"
if "`kind'" == "spss" local ext ".sav"
if "`kind'" == "spss" & "`zsav'" != "" local ext ".zsav"
if "`kind'" == "sasxport5" local ext ".xpt"
if "`kind'" == "sasxport8" local ext ".v8xpt"
mata: st_local("base",pathbasename(st_local("using")))
if !strpos(`"`base'"',".") local using `"`using'`ext'"'
confirm file `"`using'"'
if "`case'" == "" local case = cond("`kind'"=="sasxport5","lower","preserve")
if "`case'"!="" {
if substr("lower",1,strlen("`case'"))=="`case'" local case "lower"
if substr("upper",1,strlen("`case'"))=="`case'" local case "upper"
if strlen("`case'")>=3 & substr("preserve",1,strlen("`case'"))=="`case'" local case "preserve"
}
if inlist("`case'","low","u","pre") {
if "`case'" == "low" local case "lower"
if "`case'" == "u" local case "upper"
if "`case'" == "pre" local case "preserve"
}
if !inlist("`case'","preserve","lower","upper") exit 198
if "`clear'" == "" & "`describe'" == "" {
if "`kind'"=="sasxport5" {
if c(changed) exit 4
}
else if (_N | c(k)) exit 4
}
if `"`bcat'"' != "" {
mata: st_local("base",pathbasename(st_local("bcat")))
if !strpos(`"`base'"',".") local bcat `"`bcat'.sas7bcat"'
confirm file `"`bcat'"'
}
if "`kind'"=="sasxport8" & `"`vlabfile'"'!="" {
mata: st_local("vlabbase",pathbasename(st_local("vlabfile")))
if !strpos(`"`vlabbase'"',".") local vlabfile `"`vlabfile'.v8xpt"'
confirm file `"`vlabfile'"'
}
* Explicit snapshot protects the caller before any destructive operation.
* preserve is program-scoped: early scan/load errors and Break restore it.
local snap 0
local snapframe ""
if "`snapshot'" != "" & "`describe'" == "" {
preserve
local snap 1
}
* The plugin is checked before the data are cleared.
_ctools_load
capture program ctools_plugin, plugin using("`__ctools_plugin'")
if _rc != 0 & _rc != 110 exit 601
* Native statistical/DBF/SHP readers clear before parsing the file, even
* when parsing fails. Excel validates its container before replacing data.
if "`describe'"=="" & !inlist("`kind'","xls","xlsx") quietly clear
local __cio_member "`member'"
local __cio_sheet `"`sheet'"'
local __cio_cellrange "`cellrange'"
local __cio_firstrow = ("`firstrow'" != "")
local __cio_maxvar = c(maxvar)
local __cio_maxmemory = c(max_memory)
local __cio_allstring = ("`allstring'" != "")
local __cio_allstringfmt `"`allstringfmt'"'
local __cio_filename `"`using'"'
local __cio_encoding `"`encoding'"'
local __cio_bcat `"`bcat'"'
local __cio_xpf ""
local __cio_rawstorage 0
if "`kind'" == "sasxport5" & "`novallabels'" == "" & "`describe'" == "" {
* The file's directory with its trailing separator ("" in the working
* directory), from pathbasename(), which Stata 14.1 has.
mata: st_local("xpfdir",substr(st_local("using"),1,strlen(st_local("using"))-strlen(pathbasename(st_local("using")))))
foreach candidate in formats.xpf FORMATS.xpf {
capture confirm file `"`xpfdir'`candidate'"'
if !_rc local __cio_xpf `"`xpfdir'`candidate'"'
}
}
if "`describe'"!="" & inlist("`kind'","xls","xlsx") {
capture noisily plugin call ctools_plugin, "cio workbook `kind'"
local rc=_rc
if `rc' {
macro drop CTOOLS_CIO_*
exit `rc'
}
local sheets $CTOOLS_CIO_nworksheets
return scalar N_worksheet=`sheets'
* Sheet names come from the file: they reach r() and the screen
* without passing through macro expansion.
forvalues j=1/`sheets' {
mata: st_local("worksheet",st_global("CTOOLS_CIO_worksheet_"+st_local("j")))
mata: st_local("range",st_global("CTOOLS_CIO_range_"+st_local("j")))
return local worksheet_`j' : copy local worksheet
return local range_`j' : copy local range
mata: printf("%s %s\n", st_local("worksheet"), st_local("range"))
}
macro drop CTOOLS_CIO_*
exit
}
if "`kind'"=="sasxport8" & `"`vlabfile'"'!="" {
local __cio_filename `"`vlabfile'"'
local __cio_rawstorage 1
capture noisily plugin call ctools_plugin, "cio scan sasxport8"
local rc=_rc
if `rc' {
capture plugin call ctools_plugin, "cio clear"
macro drop CTOOLS_CIO_*
exit `rc'
}
* The native command loads its companion before reading the main file.
* Its second read fails before compression or case conversion, leaving
* the companion's declared storage and original names in memory.
if $CTOOLS_CIO_nvar>0 {
local nobs $CTOOLS_CIO_nobs
local nvar $CTOOLS_CIO_nvar
local nlabels $CTOOLS_CIO_nlabels
capture noisily _cio_import_load sasxport8 `nobs' `nvar' `nlabels', case(preserve)
local rc=_rc
capture plugin call ctools_plugin, "cio clear"
macro drop CTOOLS_CIO_*
if `rc' exit `rc'
return scalar N=_N
return scalar k=c(k)
return scalar width=c(width)
return scalar changed=c(changed)
local datalabel : data label
return local datalabel : copy local datalabel
di as error "no; dataset in memory has changed since last saved"
exit 4
}
local __cio_rawstorage 0
local __cio_filename `"`using'"'
}
capture noisily plugin call ctools_plugin, "cio scan `kind'"
local rc = _rc
if `rc' {
macro drop CTOOLS_CIO_*
exit `rc'
}
local nobs = $CTOOLS_CIO_nobs
local nvar = $CTOOLS_CIO_nvar
local nlabels = $CTOOLS_CIO_nlabels
if "`describe'" != "" {
local nmembers $CTOOLS_CIO_nmembers
* Member names, variable names and labels come from the file: they are
* handled in Mata and reach r() and the screen without macro expansion.
mata: st_local("members", strlower(st_global("CTOOLS_CIO_members")))
mata: st_local("descriptions", st_local("member"))
if "`kind'"=="sasxport5" & "`member'"=="" local descriptions : copy local members
mata: st_local("ndescriptions", strofreal(cols(tokens(st_local("descriptions")))))
* An error or Break in the listing still releases the scanned file
capture noisily {
forvalues m=1/`ndescriptions' {
mata: st_local("item", tokens(st_local("descriptions"))[`m'])
if "`kind'"=="sasxport5" {
local __cio_member : copy local item
plugin call ctools_plugin, "cio scan `kind'"
local nobs $CTOOLS_CIO_nobs
local nvar $CTOOLS_CIO_nvar
local size $CTOOLS_CIO_size
}
mata: printf("%s: %s variables, %s observations\n", st_local("item"), st_local("nvar"), st_local("nobs"))
forvalues j=1/`nvar' {
plugin call ctools_plugin, "cio column `j'"
mata: printf("%s %s %s\n", st_global("CTOOLS_CIO_name"), st_global("CTOOLS_CIO_type"), st_global("CTOOLS_CIO_label"))
}
}
}
local rc = _rc
capture plugin call ctools_plugin, "cio clear"
macro drop CTOOLS_CIO_*
if `rc' exit `rc'
return scalar N = `nobs'
return scalar k = `nvar'
if "`kind'"=="sasxport5" {
return scalar size = `size'
if "`member'"=="" {
return scalar n_members = `nmembers'
return local members : copy local members
}
}
exit
}
* A failure after the scan (allocation, SPI, Break) leaves the data in
* memory as they were. Only an Excel workbook reaches this point with data
* in memory (the other readers cleared them above, as native ones do);
* from Stata 16 they are copied into a frame of this program's own, in
* memory (the copy fails before anything changes if memory is short), and
* restored from it with frame copy (see cimport.ado for why the load is
* not built in a frame that is then renamed). Option snapshot preserves
* the data instead (on disk above c(max_preservemem)), as do loads before
* Stata 16 and loads into an empty dataset. Option fast keeps no copy and
* undoes nothing. preserve and the frame belong to this program, so these
* steps are inline.
if "`fast'" == "" & !`snap' {
if "`snapshot'" == "" & c(stata_version) >= 16 & (_N > 0 | c(k) > 0) {
tempname snapframe
capture noisily frame copy `c(frame)' `snapframe'
}
else {
capture noisily preserve
local snap 1
}
* A failed copy still releases the scanned file
if _rc {
local rc = _rc
capture plugin call ctools_plugin, "cio clear"
macro drop CTOOLS_CIO_*
exit `rc'
}
}
local loadcase "`case'"
if inlist("`kind'","sas","spss") | (inlist("`kind'","xls","xlsx") & "`firstrow'"=="") local loadcase preserve
capture noisily _cio_import_load `kind' `nobs' `nvar' `nlabels', case(`loadcase') wanted(`"`wanted'"') allstringfmt(`"`allstringfmt'"') `novallabels' `allstring' filename(`"`using'"')
local rc = _rc
capture plugin call ctools_plugin, "cio clear"
macro drop CTOOLS_CIO_*
if !`rc' {
capture noisily {
if `"`selectors'"' != "" quietly keep `selectors'
if `"`wanted'"' != "" & !inlist("`kind'","xls","xlsx") keep `wanted'
if inlist("`kind'","sas","spss") {
if "`case'" != "preserve" rename _all, `case'
quietly compress
}
* Validation hook: fail as Break would once the data are loaded
if "$CTOOLS_cimport_fault" == "load" error 1
}
local rc = _rc
}
if `rc' {
nobreak {
if "`snapframe'" != "" frame copy `snapframe' `c(frame)', replace
else if `snap' restore
}
exit `rc'
}
if "`snapframe'" != "" frame drop `snapframe'
else if `snap' restore, not
return scalar N = _N
return scalar k = c(k)
return scalar width = c(width)
return scalar changed = 1
local datalabel : data label
return local datalabel : copy local datalabel
return local filename `"`using'"'
end
program define _cio_import_load
version 14.1
gettoken kind 0 : 0, parse(" ,")
gettoken nobs 0 : 0, parse(" ,")
gettoken nvar 0 : 0, parse(" ,")
gettoken nlabels 0 : 0, parse(" ,")
syntax, CASE(string) [WANTED(string) NOVALLabels ALLSTRINGFMT(string) ALLString FILEname(string)]
* Plugin registrations belong to the invoking ado program.
_ctools_load
capture program ctools_plugin, plugin using("`__ctools_plugin'")
if _rc != 0 & _rc != 110 exit 601
clear
* The file's data label. label data cannot quote one holding an
* unbalanced compound quote (it fails or stores other text); that label
* comes from an empty dataset written to hold it. Later steps keep it.
mata: st_local("__data_label",st_global("CTOOLS_CIO_datalabel"))
capture label data `"`macval(__data_label)'"'
local __label_rc = _rc
local __label_got : data label
mata: st_local("__label_rc",strofreal(`__label_rc' | st_local("__label_got")!=usubstr(st_local("__data_label"),1,80)))
if `__label_rc' {
tempfile __label_stub
mata: _cio_label_stub(st_local("__label_stub"),st_local("__data_label"))
quietly use `"`__label_stub'"', clear
}
quietly set obs `=max(1,`nobs')'
mata: __ctools_cio_sets = J(1,0,"")
if `nvar' {
if inlist("`kind'","xls","xlsx") {
* Resolve the entire header before creating variables: a later column's
* fallback can require moving an earlier header to its own column letter.
forvalues j=1/`nvar' {
plugin call ctools_plugin, "cio column `j'"
mata: st_local("__cio_candidate_`j'",_cio_excel_name(st_global("CTOOLS_CIO_name"),st_global("CTOOLS_CIO_excel_column"),st_local("case")))
mata: st_local("__cio_fallback_`j'",st_global("CTOOLS_CIO_excel_column"))
}
mata: _cio_excel_names(`nvar')
}
forvalues j=1/`nvar' {
plugin call ctools_plugin, "cio column `j'"
if inlist("`kind'","xls","xlsx") {
local vname "`__cio_resolved_`j''"
}
else mata: st_local("vname",ustrtoname(st_global("CTOOLS_CIO_name")))
if "`case'" == "lower" & !inlist("`kind'","xls","xlsx") local vname = ustrlower("`vname'")
if "`case'" == "upper" & !inlist("`kind'","xls","xlsx") local vname = ustrupper("`vname'")
if inlist("`kind'","xls","xlsx") {
if "`case'" == "lower" local vname = ustrlower("`vname'")
if "`case'" == "upper" local vname = ustrupper("`vname'")
}
if !inlist("`kind'","xls","xlsx") {
capture confirm new variable `vname'
if _rc local vname "v`j'"
}
if $CTOOLS_CIO_external local external `external' `j'
if $CTOOLS_CIO_numerictext local textnumbers `textnumbers' `j'
local sourcecol`j' "${CTOOLS_CIO_excel_column}"
local sourcevar`j' "`vname'"
local type ${CTOOLS_CIO_type}
if inlist("`kind'","xls","xlsx") & ("`vname'"=="_cons" | ustrregexm("`vname'","^str[0-9]+$")) {
di as error "`vname' invalid varname"
exit 198
}
mata: (void) st_addvar(st_local("type"),st_local("vname"))
mata: st_varlabel(st_local("vname"),st_global("CTOOLS_CIO_labelnumber")=="" ? st_global("CTOOLS_CIO_label") : strtrim(strofreal(strtoreal(st_global("CTOOLS_CIO_labelnumber")),"%18.0g")))
mata: st_varformat(st_local("vname"),st_global("CTOOLS_CIO_format"))
if ("`novallabels'" == "" | "`kind'"=="sasxport5") & inlist("`kind'","spss","sasxport5") {
* Label-set names come from the file and stay in Mata. Native
* SPSS labels use their dictionary ordinal, beginning at 0.
mata: st_local("labelname",_cio_labelset_name(st_local("kind"),st_global("CTOOLS_CIO_labelset"),1))
* String variables take no value label: native SPSS notes it,
* native XPORT fails unless novallabels is given.
if "`labelname'" != "" & substr("`type'",1,3) == "str" {
if "`kind'" == "sasxport5" & "`novallabels'" == "" {
mata: printf("{err}%s has string variable %s with format (value label) %s; value labels for string variables are not allowed in Stata\n", st_local("filename"), st_local("vname"), st_global("CTOOLS_CIO_labelset"))
exit 610
}
if "`kind'" == "spss" di as text "note: may not attach value label to string variable: no label applied"
local labelname ""
}
if "`labelname'" != "" mata: st_varvaluelabel(st_local("vname"),st_local("labelname"))
}
}
}
if `nobs' {
foreach hdridx of local external {
forvalues i=1/`nobs' {
local offset 0
mata: __ctools_cio_blob = ""
while 1 {
plugin call ctools_plugin, "cio blob `hdridx' `i' `offset'"
mata: __ctools_cio_blob = __ctools_cio_blob + _cio_hex_bytes(st_global("CTOOLS_CIO_blobhex"))
local offset = $CTOOLS_CIO_blobnext
if !`offset' continue, break
}
mata: st_sstore(`i',`hdridx',__ctools_cio_blob)
}
mata: mata drop __ctools_cio_blob
}
}
if "`kind'" == "shp" {
foreach field in type xmin ymin xmax ymax zmin zmax mmin mmax num_records {
mata: st_global("_dta[shp_"+st_local("field")+"]",st_global("CTOOLS_CIO_shp_"+st_local("field")))
}
char _dta[dta_shp_ver] "1"
char _dta[shp_code] "9994"
char _dta[shp_version] "1000"
}
if "`kind'"=="dbase" {
foreach field in version year month day {
mata: st_global("_dta[dbf_"+st_local("field")+"]",st_global("CTOOLS_CIO_dbf_"+st_local("field")))
}
}
if `nobs' & `nvar' plugin call ctools_plugin *, "cio load"
* Numbers in string columns: allstring()'s format, else native's %18.0g
* (for which the plugin reserved 18 characters).
local textfmt `"`allstringfmt'"'
if `"`textfmt'"' == "" local textfmt "%18.0g"
if `nobs' {
foreach j of local textnumbers {
tempvar rawnumber
quietly gen double `rawnumber' = .
plugin call ctools_plugin `rawnumber', "cio textnumbers `j'"
local target `sourcevar`j''
mata: _cio_format_text(st_local("target"),st_local("rawnumber"),st_local("textfmt"),st_local("allstringfmt")=="")
quietly drop `rawnumber'
quietly compress `target'
mata: st_varformat(st_local("target"),"%"+strofreal(st_vartype(st_local("target"))=="strL" ? 9 : max((9,max(strlen(st_sdata(.,st_local("target")))))))+"s")
}
}
if !`nobs' quietly drop in 1
if "`kind'" == "shp" {
capture confirm variable shape_order
if !_rc sort _ID
}
if "`novallabels'" == "" & `nlabels' {
forvalues i=1/`nlabels' {
plugin call ctools_plugin, "cio label `i'"
mata: st_local("labelname",_cio_labelset_name(st_local("kind"),st_global("CTOOLS_CIO_labelset"),0))
mata: st_vlmodify(st_local("labelname"),strtoreal(st_global("CTOOLS_CIO_labelvalue")),st_global("CTOOLS_CIO_labeltext"))
}
}
if inlist("`kind'","xls","xlsx") & `"`wanted'"'!="" {
local rest `"`wanted'"'
local desired ""
local selected ""
local position 0
while strtrim(`"`rest'"')!="" {
gettoken name rest : rest, parse(" =")
if `: list name in desired' exit 198
local ++position
gettoken equal tail : rest, parse(" =")
local column ""
if "`equal'"=="=" {
gettoken column rest : tail
local column=upper("`column'")
}
else {
mata: st_local("column",_cio_excel_column(`position'))
}
local index 0
forvalues j=1/`nvar' {
if "`sourcecol`j''"=="`column'" local index `j'
}
tempvar picked`position'
if !`index' {
if !`nvar' exit 198
mata: st_local("columnindex",strofreal(_cio_excel_index(st_local("column"))))
mata: st_local("columnlimit",strofreal(_cio_excel_index(st_local("sourcecol`nvar'"))+1))
if `columnindex'<1 | `columnindex'>`columnlimit' exit 198
if "`allstring'"!="" quietly gen str1 `picked`position''=""
else {
quietly gen byte `picked`position''=.
format `picked`position'' %10.0g
}
}
else clonevar `picked`position''=`sourcevar`index''
local selected `selected' `picked`position''
local desired `desired' `name'
}
keep `selected'
local j=0
foreach v of local selected {
local ++j
local name : word `j' of `desired'
rename `v' `name'
}
}
capture mata: mata drop __ctools_cio_sets
end
mata:
real scalar _cio_excel_index(string scalar column) {
real scalar j, n, code
n=0
for(j=1;j<=strlen(column);j++) {
code=ascii(substr(column,j,1))-64
if(code<1 || code>26)return(.)
n=n*26+code
if(n>16384)return(.)
}
return(n)
}
string scalar _cio_excel_column(real scalar index) {
string scalar column
column=""
while(index>0) {
column=char(65+mod(index-1,26))+column
index=floor((index-1)/26)
}
return(column)
}
/* Lowercase hex pairs to bytes, decoded as whole vectors. */
string scalar _cio_hex_bytes(string scalar hex) {
real matrix digits
if (strlen(hex) < 2) return("")
digits = ascii(hex)
digits = colshape(digits :- 48 :- 39 :* (digits :> 57), 2)
return(char((digits[., 1] * 16 + digits[., 2])'))
}
end
mata:
/* An empty .dta (format 118, no variables or observations) whose header
holds label, the first 80 characters as label data keeps them: using it
sets a data label that label data cannot quote (an unbalanced compound
quote). */
void _cio_label_stub(string scalar path, string scalar label) {
string scalar head, body, map, text
real colvector at
real scalar fh, i
string rowvector tags
text=usubstr(label,1,80)
head="118LSF"+_cio_le(0,2)+""+_cio_le(0,8)+""+char(0)+""
tags=("variable_types","varnames","sortlist","formats","value_label_names","variable_labels","characteristics","data","strls","value_labels")
at=J(14,1,0)
at[2]=strlen(head)
body=""
for (i=1;i<=10;i++) {
at[i+2]=at[2]+strlen("")+strlen(body)
body=body+"<"+tags[i]+">"+(tags[i]=="sortlist" ? _cio_le(0,2) : "")+""+tags[i]+">"
}
at[13]=at[2]+strlen("")+strlen(body)
at[14]=at[13]+strlen("")
map=""
if (fileexists(path)) unlink(path)
fh=fopen(path,"w")
fwrite(fh,head+map+body+"")
fclose(fh)
}
string scalar _cio_le(real scalar value, real scalar bytes) {
string scalar out
real scalar i, rest
out=""
rest=value
for (i=1;i<=bytes;i++) {
out=out+char(mod(rest,256))
rest=floor(rest/256)
}
return(out)
}
void _cio_format_text(string scalar target, string scalar source, string scalar fmt, real scalar trim) {
real colvector values, selected
string colvector text
real scalar width
values=st_data(.,source)
selected=selectindex(values:<.)
if (!length(selected)) return
text=trim ? strtrim(strofreal(values[selected],fmt)) : strofreal(values[selected],fmt)
/* Widen the variable only as far as the text needs (strL past 2045) */
width=max(strlen(text))
if (st_vartype(target)!="strL" & width>strtoreal(substr(st_vartype(target),4,.))) stata("quietly recast "+(width>2045 ? "strL" : "str"+strofreal(width))+" "+target)
st_sstore(selected,target,text)
}
end
mata:
/* Native cuts the header to 32 characters, keeps Unicode letters, decimal
digits and underscores, drops leading digits, and uses the column letter
when the result, in the requested case, is not a valid name. */
string scalar _cio_excel_name(string scalar source, string scalar fallback, string scalar casing) {
string scalar name, cased
name=ustrregexra(usubstr(source,1,32),"[^\p{L}\p{Nd}_]","")
name=ustrregexrf(name,"^\p{Nd}+","")
cased=casing=="lower" ? ustrlower(name) : (casing=="upper" ? ustrupper(name) : name)
/* Native does not replace _cons or str#: creating them fails. */
if (cased=="_cons" || ustrregexm(cased,"^str[0-9]+$")) return(name)
if (name=="" || !st_isvarname(cased)) return(fallback)
return(name)
}
/* Value-label name for a label set named in the file: for SPSS the reader's
labels# (the set's dictionary ordinal, as native), the lowercased name for
the other formats. "" when a variable has no label set (add=1). */
string scalar _cio_labelset_name(string scalar kind, string scalar set, real scalar add) {
if (add & set=="") return("")
if (kind!="spss") return(ustrlower(ustrtoname(set)))
return(set)
}
void _cio_excel_names(real scalar n) {
string rowvector names, fallback
string scalar name
real scalar j, at, previous
transmorphic scalar used
names=J(1,n,""); fallback=J(1,n,"")
used=asarray_create("string",1)
for(j=1;j<=n;j++) fallback[j]=st_local(sprintf("__cio_fallback_%g",j))
for(j=1;j<=n;j++) {
name=st_local(sprintf("__cio_candidate_%g",j))
if(asarray_contains(used,name)) name=fallback[j]
at=j
while(asarray_contains(used,name)) {
previous=asarray(used,name)
names[at]=name
asarray(used,name,at)
at=previous
name=fallback[at]
}
names[at]=name
asarray(used,name,at)
}
for(j=1;j<=n;j++) st_local(sprintf("__cio_resolved_%g",j),names[j])
}
end