program define stak
*! version 1.2.1  05dec2000  TJS & NJC (hon.)
  version 6

  if "`1'" == "" {
    di in re "varlist required"
    di in bl "r(100);"
    help stak
    error 100
  }

  syntax varlist [if] [in] [ , /*
    */ Into(str) Gid(str) Retain Wide Dummy Labels CLEAR ]
  tokenize `varlist'

* convert switch-strings to logicals
  local retain = "`retain'" == "retain"
  local wide   = "`wide'"   == "wide"
  local dummy  = "`dummy'"  == "dummy"
  local labels = "`labels'" == "labels"
  local clear  = "`clear'"  == "clear"

* check for wide vs dummy conflict
  if `wide' & `dummy' {
    di in re "may not specify both wide and dummy; data unchanged"
    exit 198
  }

* set new var/grp names
  if "`into'" == "" { local into _into }
  if "`gid'"  == "" { local gid  _gid  }
  if "`into'" == "`gid'" {
    di in re "INTO varname cannot be same as GID varname;" _c
    di in re " data unchanged"
    exit 110
  }

* set up retain list
  if `retain' {
    unab  rvars : _all
    local nvars : word count `varlist'
    local i = 1
    while `i' <= `nvars' {
    	local rvars : subinstr local rvars "``i''" "", word
	local i = `i' + 1
    }
  }

* check for invalid into or gid names
  unab  vr : `varlist' `rvars'
  local vr : subinstr local vr "`into'" "", word count(local n2)
  if `n2' {
    di in re "INTO varname same as a varlist or" _c
    di in re " retained variable; data unchanged"
    exit 110
  }
  local vr : subinstr local vr "`gid'"  "", word count(local n2)
  if `n2' {
    di in re "GID varname same as a varlist or" _c
    di in re " retained variable; data unchanged"
    exit 110
  }

* get # vars to be stacked
  local nvars : word count `varlist'

* check if varlist all numeric or all string
  local j = 1
  local nnum = 0
  while `j' <= `nvars' {
    capture confirm numeric variable ``j''
    local nnum = `nnum' + (_rc == 0)
    local j = `j' + 1
  }
  if `nnum' > 0 & `nnum' < `nvars' {
    local nstr = `nvars' - `nnum'
    di in re "cannot stack mixed variables: `nnum' numeric," _c
    di in re " `nstr' string; data unchanged"
    exit 198
  }
  if `nnum' == 0 { local strvar 1 }
    else         { local strvar 0 }

* preserve data for potential restore
  preserve

* provide a warning before proceeding
  if `clear' {
    di in bl "Warning:  data in memory will be lost."
    di in bl "         Press any key to continue," _c
    di in bl " Ctrl-Break to abort."
    set more 0
    more
  }

* set up gid labels
  cheklab , label(`gid') invars("`varlist' `rvars'")
  local glab = r(label)
  local j = 1
  while `j' <= `nvars' {
    local vl : variable label ``j''
    if `labels' & "`vl'" != "" /*
        */ { label define `glab' `j' "`vl'",  modify }
      else { label define `glab' `j' "``j''", modify }
    local j = `j' + 1
  }

* copy varlist for later processing
    if `wide' | `dummy' { local wdvars `varlist' }

* prepare the modified data label
  local dlabel : data label
  label data "stak: `dlabel'"

* data generation loop
  local j = 1
  while `j' <= `nvars' {
    if `wide' | `dummy' { local wdvar ``j'' }
    cap drop `into'
    cap drop `gid'
    if (`"`if'"' != "" | `"`in'"' != "") /*
       */ { quietly keep `if' `in' }
    tempfile new`j'
    local type : type ``j''
    qui gen `type' `into' = ``j''
    qui gen int `gid' = `j'
    if `dummy' {
      if `strvar' {
        tempvar v
        gen `v' = 0
        copydes ``j'' `v'
        drop ``j''
        rename `v' ``j''
      }
      qui replace `wdvar' = 1
      label values `wdvar'
      local varlab : variable label `wdvar'
      label variable `wdvar' "`varlab' dummy"
      format `wdvar' %8.0g
    }
    if `j' == 1 { label values `gid' `glab' }
    keep `rvars' `into' `gid' `wdvar'
    qui save "`new`j''"
    local j = `j' + 1
    restore, preserve
  }

* capture all errors
  capture {

* combine temp data sets
    use "`new1'", replace
    local j = 2
    while `j' <= `nvars' {
      qui append using "`new`j''"
      local j = `j' + 1
    }

* do housekeeping
* (blank file name & date, set var labels, complete dummys)
    global S_FN
    global S_FNDATE
    label variable `gid'  "group identifiers"
    label variable `into' "`into' values"
    if `dummy' {
      tokenize `wdvars'
      local j = 1
      while `j' <= `nvars' {
        qui replace ``j'' = 0 if ``j'' == .
        local j = `j' + 1
      }
    }
    qui compress
    order `into' `gid' `wdvars' `rvars'

* end capture and, if needed, print note
  }
  if _rc {
    local rc _rc
    di _n in bl "Note:  original data restored"
    di in ye "(`dlabel')"
    error `rc'
  }
  restore, not

  exit
  end


program define cheklab, rclass
*! version 1.0.0  01dec2000  TJS
  version 6
  syntax , Label(str) Invars(str)
  tokenize `invars'
  local j 1
  while "``j''" != "" {
    local vl : value label ``j''
    if "`vl'" == "`label'" {
      local label _gid
      cap label drop _gid
      local k 1
      while "``k''" != "" {
        local vl : value label ``k''
        if "`vl'" == "`label'" { label value ``k'' }
        local k = `k' + 1
      }
      return local label = "`label'"
      exit
    }
    local j = `j' + 1
  }
  return local label = "`label'"
  exit
  end


program def copydes
*! from -copydesc- NJC 1.2.1 8 August 2000
  version 6.0
  syntax varlist(min=2 max=2)
  tokenize `varlist'
  args src dst
  local w : variable label `src'
  if `"`w'"' == "" { local w "`src'" }
  label variable `dst' `"`w'"'
  local srclab : value label `src'
  capture label val `dst' `srclab'
  local srcfmt : format `src'
  capture format `dst' `srcfmt'
  local chsrc : char `src'[]
  if "`chsrc'" != "" {
    tokenize `chsrc'
    while "`1'" != "" {
      local schar : char `src'[`1']
      char `dst'[`1'] `"`schar'"'
      mac shift
    }
  }
end