user/shadow.go
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
}
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
}
// parseShadow parses the row of a shadowed password.