String subParams(String replString, String defName, String parms, Vector<PPToken> argv) {
		String substString = replString;

		ArrayList<Integer> beginPos = new ArrayList<Integer>();
		ArrayList<Integer> endPos = new ArrayList<Integer>();
		ArrayList<String> subValue = new ArrayList<>();
		int index = 0;
		int pos = 0;
		StringBuffer argsfound = new StringBuffer();
		boolean isVarArg = false;
		boolean hadVarArgs = false;
		while (pos < parms.length() || index < argv.size()) {
			String argValue = "";
			int origPos = pos;
			if (pos < parms.length()) {
				argValue = getParams(new StringBuffer(parms), pos, ',');
			}
			pos += argValue.length() + 1;

			if (index >= argv.size()) {
				Msg.error(this,
					"Define parameter mismatch for macro " + defName + "(" + parms + ")" +
						" Expected " + argv.size() + " arguments.  " + " badarg(" + index + ") " +
						argValue + " args processed : " + argsfound);
				return replString;
			}

			// Handle "..." varargs
			//    if last argument is ellipsis, then is varargs, replace the rest of the params
			String curArgName = argv.elementAt(index).image;
			if (index == argv.size() - 1 && VARARG_ELLIPSIS.equals(curArgName)) {
				isVarArg = true;
				//   Replace __VA_ARGS__ with the rest of params
				curArgName = "__VA_ARGS__";
				argValue = getParams(new StringBuffer(parms), origPos, '\0');
				pos += argValue.length() + 1;
			}
			index++;
			argValue = argValue.trim();
			argsfound.append(argValue);
			argsfound.append(", ");

			// isVarArg, and had variable arguments
			if (isVarArg && argValue.length() != 0) {
				hadVarArgs = true;
			}

			int curpos = -1;
			// find argname in substString
			// note begin and end position
			do {
				curpos = substString.indexOf(curArgName, curpos + 1);

				if (curpos < 0) {
					continue;
				}

				// this match is not exact so skip it (borrowing from
				// JavaCharacter)
				if (curpos > 0 &&
					(Character.isJavaIdentifierStart(substString.charAt(curpos - 1)) ||
						Character.isJavaIdentifierPart(substString.charAt(curpos - 1)))) {
					continue;
				}

				int afterIndex = curpos + curArgName.length();
				if (afterIndex < substString.length() &&
					(Character.isJavaIdentifierStart(substString.charAt(afterIndex)) ||
						Character.isJavaIdentifierPart(substString.charAt(afterIndex)))) {
					continue;
				}

				Integer begin = Integer.valueOf(curpos);
				int insertLoc = 0;
				for (; insertLoc < beginPos.size(); insertLoc++) {
					Integer loc = beginPos.get(insertLoc);
					if (loc.compareTo(begin) > 0) {
						break;
					}
				}

				beginPos.add(insertLoc, begin);
				endPos.add(insertLoc, Integer.valueOf(curpos + curArgName.length()));
				subValue.add(insertLoc, argValue);
			}
			while (curpos >= 0);
		}

		StringBuffer buf = new StringBuffer();
		int listSize = beginPos.size();
		int startpos = 0;
		for (int i = 0; i < listSize; i++) {
			int begin = beginPos.get(i).intValue();
			int end = endPos.get(i).intValue();
			String value = subValue.get(i);

			buf.append(substString.substring(startpos, begin));
			buf.append(value);
			startpos = end;
		}
		buf.append(substString.substring(startpos));

		// Handle __VA_OPT__(<repl>)
		//    if varargs and no more params, replace with ""
		//    if varargs and has vararg params, replace with <repl>
		if (isVarArg) {
			replace_VaOpt(buf, hadVarArgs);
		}

		substString = buf.toString();
		return substString;
	}
expand a define with arguments

@return the newly expanded string
Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/CPP/DefineTable.java