code: func parseShadow(row string) (*Shadow, error) {
  	fields := strings.Split(row, ":")
  	if len(fields) != 9 {
  		return nil, rowError{_SHADOW_FILE, row}
  	}
  
  	var inactive, expire, flag int
  
  	changed, err := parseChange(fields[2])
  	if err != nil {
  		return nil, atoiError{_SHADOW_FILE, row, "changed"}
  	}
  	min, err := strconv.Atoi(fields[3])
  	if err != nil {
  		return nil, atoiError{_SHADOW_FILE, row, "Min"}
  	}
  	max, err := strconv.Atoi(fields[4])
  	if err != nil {
  		return nil, atoiError{_SHADOW_FILE, row, "Max"}
  	}
  	warn, err := strconv.Atoi(fields[5])
  	if err != nil {
  		return nil, atoiError{_SHADOW_FILE, row, "Warn"}
  	}
  
  	// Optional fields
  
  	if fields[6] != "" {
  		if inactive, err = strconv.Atoi(fields[6]); err != nil {
  			return nil, atoiError{_SHADOW_FILE, row, "Inactive"}
  		}
  	}
  	if fields[7] != "" {
  		if expire, err = strconv.Atoi(fields[7]); err != nil {
  			return nil, atoiError{_SHADOW_FILE, row, "expire"}
  		}
  	}
  	if fields[8] != "" {
  		if flag, err = strconv.Atoi(fields[8]); err != nil {
  			return nil, atoiError{_SHADOW_FILE, row, "flag"}
  		}
  	}
  
  	return &Shadow{
  		fields[0],
  		fields[1],
  		changed,
  		min,
  		max,
  		warn,
  		inactive,
  		expire,
  		flag,
  	}, nil
  }
docs: // parseShadow parses the row of a shadowed password.