Skip to content

Commit b77a7dc

Browse files
authored
refactor: getFullArg (#56)
* make getFullArg private * move comment * move free of temps to just after last use To check this, ensure that any code path ending in `goto return0` or `goto return1` either frees `tmpStr` and `tmpArg` or never sets them. * unify two return paths To verify, note that each `goto return0` becomes `ret = 0; goto getFullArg_ret` and `goto return1` becomes ` goto getFullArg_ret`, because the initial value of `ret` is 1. * control flow transformations For the two if statements, note that both branches execute the same statements before and after. For the while loop, note that it is an infinite loop and there are no internal `continue`s so it is safe to roll `q = p` from the beginning to the end and before the loop. * move down temporary declarations To verify: observe that it still compiles, nothing is out of scope. * move integer declarations down Note that `j` was split to another variable `j2` at the end of the function. To verify, note that all subsequent uses of `j` have become `j2`. * move file down * improve variable names This was done by 'rename variable' tool. (Note that we couldn't have done this before splitting temporaries used for multple purposes.) * document why this isn't a memory leak The code was doing something tricky here that should be called out explicitly. To verify: `defaultCmd` is set inside the `if (cmdList[0] == '#')` and `if (cmdList[0] == '*' || cmdList[0] == '&')` conditionals, but in both cases there is an unconditional jump at the end of the if, so any path that makes it to the commented line has still not set `defaultCmd`. * bugfix: memory safety after bug() It is an easily-forgotten fact that bug() is not a `noreturn` function, because the user can choose to continue after hitting a bug. Therefore, code must be careful not to trigger undefined behavior after a `bug()` check, and should always do their best to exit the current scope without undue damage and ideally without leaking memory either. * use else if for mutually exclusive options * drop tmpArg
1 parent 037d1e4 commit b77a7dc

File tree

2 files changed

+85
-82
lines changed

2 files changed

+85
-82
lines changed

src/mmcmdl.c

Lines changed: 85 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ flag g_sourceHasBeenRead = 0; /* 1 if a source file has been read in */
3838
vstring_def(g_rootDirectory); /* Directory prefix to use for included files */
3939

4040

41+
static flag getFullArg(long arg, const char *cmdList);
4142

4243
flag processCommandLine(void) {
4344
vstring_def(defaultArg);
@@ -1627,29 +1628,25 @@ flag processCommandLine(void) {
16271628

16281629

16291630

1630-
flag getFullArg(long arg, vstring cmdList1) {
1631-
/* This function converts the user's abbreviated keyword in
1632-
g_rawArgPntr[arg] to a full, upper-case keyword,
1633-
in g_fullArg[arg], matching
1634-
the available choices in cmdList. */
1635-
/* Special cases: cmdList = "# xxx <yyy>?" - get an integer */
1636-
/* cmdList = "* xxx <yyy>?" - get any string;
1637-
don't convert to upper case
1638-
cmdList = "& xxx <yyy>?" - same as * except
1639-
verify it is a file that exists */
1640-
/* "$" means a null argument is acceptable; put it in as
1641-
special character chr(3) so it can be recognized */
1642-
1631+
/* This function converts the user's abbreviated keyword in
1632+
g_rawArgPntr[arg] to a full, upper-case keyword,
1633+
in g_fullArg[arg], matching
1634+
the available choices in cmdList. */
1635+
/* Special cases: cmdList = "# xxx <yyy>?" - get an integer */
1636+
/* cmdList = "* xxx <yyy>?" - get any string;
1637+
don't convert to upper case
1638+
cmdList = "& xxx <yyy>?" - same as * except
1639+
verify it is a file that exists */
1640+
/* "$" means a null argument is acceptable; put it in as
1641+
special character chr(3) so it can be recognized */
1642+
static flag getFullArg(long arg, const char *cmdList1) {
16431643
pntrString_def(possCmd);
1644-
long possCmds, i, j, k, m, p, q;
1644+
flag ret = 1;
16451645
vstring_def(defaultCmd);
16461646
vstring_def(infoStr);
1647-
vstring_def(tmpStr);
1648-
vstring_def(tmpArg);
16491647
vstring_def(errorLine);
16501648
vstring_def(keyword);
16511649
vstring_def(cmdList);
1652-
FILE *tmpFp;
16531650

16541651
let(&cmdList,cmdList1); /* In case cmdList1 gets deallocated when it comes
16551652
directly from a vstring function such as cat() */
@@ -1669,32 +1666,38 @@ flag getFullArg(long arg, vstring cmdList1) {
16691666
if (g_rawArgs <= arg) bug(1103);
16701667

16711668
g_queryMode = 1;
1672-
tmpArg = cmdInput1(right(cmdList,3));
1669+
vstring argLine = cmdInput1(right(cmdList,3));
16731670
let(&errorLine,right(cmdList,3));
1674-
if (tmpArg[0] == 0) { /* Use default argument */
1675-
let(&tmpArg, seg(defaultCmd,2,len(defaultCmd) - 1));
1671+
if (argLine[0] == 0) { /* Use default argument */
1672+
let(&argLine, seg(defaultCmd,2,len(defaultCmd) - 1));
16761673
}
1677-
let((vstring *)(&g_rawArgPntr[arg]), tmpArg);
1674+
let((vstring *)(&g_rawArgPntr[arg]), argLine);
1675+
free_vstring(argLine);
16781676
g_rawArgNmbr[arg] = len(cmdList) - 1;/* Line position for error msgs */
16791677

16801678
} /* End of asking user for additional argument */
16811679

16821680
/* Make sure that the argument is a non-negative integer */
1681+
vstring_def(tmpArg);
16831682
let(&tmpArg,g_rawArgPntr[arg]);
16841683
if (tmpArg[0] == 0) { /* Use default argument */
16851684
/* (This code is needed in case of null string passed directly) */
16861685
let(&tmpArg, seg(defaultCmd,2,len(defaultCmd) - 1));
16871686
}
1687+
vstring_def(tmpStr);
16881688
let(&tmpStr, str(val(tmpArg)));
16891689
let(&tmpStr, cat(string(len(tmpArg)-len(tmpStr),'0'), tmpStr, NULL));
16901690
if (strcmp(tmpStr, tmpArg)) {
16911691
printCommandError(errorLine, arg,
16921692
"?A number was expected here.");
1693-
goto return0;
1693+
ret = 0;
1694+
} else {
1695+
let(&keyword, str(val(tmpArg)));
16941696
}
16951697

1696-
let(&keyword, str(val(tmpArg)));
1697-
goto return1;
1698+
free_vstring(tmpArg);
1699+
free_vstring(tmpStr);
1700+
goto getFullArg_ret;
16981701
}
16991702

17001703

@@ -1711,14 +1714,14 @@ flag getFullArg(long arg, vstring cmdList1) {
17111714
if (!strcmp(defaultCmd, "<$>")) { /* End of command acceptable */
17121715
/* Note: in this case, user will never be prompted for anything. */
17131716
let(&keyword,chr(3));
1714-
goto return1;
1717+
goto getFullArg_ret;
17151718
}
17161719
g_rawArgs++;
17171720
pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
17181721
nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, 0));
17191722
if (g_rawArgs <= arg) bug(1104);
17201723
g_queryMode = 1;
1721-
tmpArg = cmdInput1(right(cmdList,3));
1724+
vstring tmpArg = cmdInput1(right(cmdList,3));
17221725

17231726
/* Strip off any quotes around it
17241727
and tolerate lack of trailing quote */
@@ -1738,7 +1741,7 @@ flag getFullArg(long arg, vstring cmdList1) {
17381741
}
17391742
let((vstring *)(&g_rawArgPntr[arg]), tmpArg);
17401743
g_rawArgNmbr[arg] = len(cmdList) - 1; /* Line position for error msgs */
1741-
1744+
free_vstring(tmpArg);
17421745
} /* End of asking user for additional argument */
17431746

17441747
let(&keyword,g_rawArgPntr[arg]);
@@ -1765,32 +1768,37 @@ flag getFullArg(long arg, vstring cmdList1) {
17651768
}
17661769
if (cmdList[0] == '&') {
17671770
/* See if file exists */
1771+
vstring_def(tmpStr);
17681772
let(&tmpStr, cat(g_rootDirectory, keyword, NULL));
1769-
tmpFp = fopen(tmpStr, "r");
1773+
FILE *tmpFp = fopen(tmpStr, "r");
17701774
if (!tmpFp) {
17711775
let(&tmpStr, cat("?Sorry, couldn't open the file \"", tmpStr, "\".", NULL));
17721776
printCommandError(errorLine, arg, tmpStr);
1773-
goto return0;
1777+
free_vstring(tmpStr);
1778+
ret = 0;
1779+
} else {
1780+
fclose(tmpFp);
17741781
}
1775-
fclose(tmpFp);
17761782
}
1777-
goto return1;
1783+
goto getFullArg_ret;
17781784
}
17791785

17801786

17811787

17821788
/* Parse the choices available */
1783-
possCmds = 0;
1784-
p = 0;
1789+
long possCmds = 0;
1790+
long p = 0;
1791+
long q = 0;
17851792
while (1) {
1786-
q = p;
17871793
p = instr(p + 1, cat(cmdList, "|", NULL), "|");
17881794
if (!p) break;
17891795
pntrLet(&possCmd,pntrAddElement(possCmd));
17901796
let((vstring *)(&possCmd[possCmds]),seg(cmdList,q+1,p-1));
17911797
possCmds++;
1798+
q = p;
17921799
}
17931800
if (!strcmp(left(possCmd[possCmds - 1],1), "<")) {
1801+
// free_vstring(defaultCmd); // Not needed because defaultCmd is already empty
17941802
/* Get default argument, if any */
17951803
defaultCmd = possCmd[possCmds - 1]; /* re-use old allocation */
17961804
if (!strcmp(defaultCmd, "<$>")) {
@@ -1805,17 +1813,19 @@ flag getFullArg(long arg, vstring cmdList1) {
18051813
}
18061814

18071815
/* Create a string used for queries and error messages */
1808-
if (possCmds < 1) bug(1105);
1816+
if (possCmds < 1) {
1817+
bug(1105);
1818+
ret = 0;
1819+
goto getFullArg_ret;
1820+
}
18091821
if (possCmds == 1) {
18101822
let(&infoStr,possCmd[0]);
1811-
}
1812-
if (possCmds == 2) {
1823+
} else if (possCmds == 2) {
18131824
let(&infoStr, cat(possCmd[0], " or ",
18141825
possCmd[1], NULL));
1815-
}
1816-
if (possCmds > 2) {
1826+
} else /* possCmds > 2 */ {
18171827
let(&infoStr, "");
1818-
for (i = 0; i < possCmds - 1; i++) {
1828+
for (long i = 0; i < possCmds - 1; i++) {
18191829
let(&infoStr, cat(infoStr, possCmd[i], ", ", NULL));
18201830
}
18211831
let(&infoStr, cat(infoStr, "or ", possCmd[possCmds - 1], NULL));
@@ -1825,12 +1835,14 @@ flag getFullArg(long arg, vstring cmdList1) {
18251835
if (g_rawArgs <= arg && (strcmp(possCmd[possCmds - 1], "nothing")
18261836
|| g_queryMode == 1)) {
18271837

1838+
vstring_def(tmpStr);
18281839
let(&tmpStr, infoStr);
18291840
if (defaultCmd[0] != 0) {
18301841
let(&tmpStr, cat(tmpStr, " ",defaultCmd, NULL));
18311842
}
18321843
let(&tmpStr, cat(tmpStr, "? ", NULL));
18331844
g_queryMode = 1;
1845+
vstring_def(tmpArg);
18341846
if (possCmds != 1) {
18351847
tmpArg = cmdInput1(tmpStr);
18361848
} else {
@@ -1839,7 +1851,7 @@ flag getFullArg(long arg, vstring cmdList1) {
18391851
if (!strcmp(cmdList, "$|<$>")) {
18401852
let(&tmpArg, possCmd[0]);
18411853
print2("The command so far is: ");
1842-
for (i = 0; i < arg; i++) {
1854+
for (long i = 0; i < arg; i++) {
18431855
print2("%s ", g_fullArg[i]);
18441856
}
18451857
print2("%s\n", tmpArg);
@@ -1859,22 +1871,25 @@ flag getFullArg(long arg, vstring cmdList1) {
18591871
g_rawArgNmbr[arg] = len(tmpStr) + 1; /* Line position for error msgs */
18601872
}
18611873

1874+
free_vstring(tmpStr);
1875+
free_vstring(tmpArg);
18621876
} /* End of asking user for additional argument */
18631877

18641878
if (g_rawArgs <= arg) {
18651879
/* No argument was specified, and "nothing" is a valid argument */
18661880
let(&keyword,chr(3));
1867-
goto return1;
1881+
goto getFullArg_ret;
18681882
}
18691883

18701884

1885+
vstring_def(tmpArg);
18711886
let(&tmpArg,edit(g_rawArgPntr[arg], 32)); /* Convert to upper case */
1872-
j = 0;
1873-
k = 0;
1874-
m = len(tmpArg);
1875-
let(&tmpStr, "");
1887+
long j = 0;
1888+
long k = 0;
1889+
long m = len(tmpArg);
1890+
vstring_def(tmpStr);
18761891
/* Scan the possible arguments for a match */
1877-
for (i = 0; i < possCmds; i++) {
1892+
for (long i = 0; i < possCmds; i++) {
18781893
if (!strcmp(possCmd[i], tmpArg)) {
18791894
/* An exact match was found, so ignore any other matches
18801895
and use this one */
@@ -1892,6 +1907,7 @@ flag getFullArg(long arg, vstring cmdList1) {
18921907
k++; /* Number of matches */
18931908
}
18941909
}
1910+
free_vstring(tmpArg);
18951911
if (k < 1 || k > 1) {
18961912
if (k < 1) {
18971913
let(&tmpStr, cat("?Expected ", infoStr, ".", NULL));
@@ -1907,53 +1923,41 @@ flag getFullArg(long arg, vstring cmdList1) {
19071923
let(&tmpStr, cat("?Ambiguous keyword - please specify ",tmpStr, ".", NULL));
19081924
}
19091925
printCommandError(errorLine, arg, tmpStr);
1910-
goto return0;
1926+
free_vstring(tmpStr);
1927+
ret = 0;
1928+
goto getFullArg_ret;
19111929
}
1930+
free_vstring(tmpStr);
19121931

19131932
let(&keyword,possCmd[j]);
1914-
goto return1;
19151933

1916-
return1:
1917-
if (keyword[0] == 0) {
1918-
if (g_rawArgs > arg && strcmp(defaultCmd, "<>")) {
1919-
/* otherwise, "nothing" was specified */
1920-
printCommandError("", arg,
1921-
"?No default answer is available - please be explicit.");
1922-
goto return0;
1923-
}
1934+
getFullArg_ret:
1935+
if (ret) {
1936+
if (keyword[0] == 0) {
1937+
if (g_rawArgs > arg && strcmp(defaultCmd, "<>")) {
1938+
/* otherwise, "nothing" was specified */
1939+
printCommandError("", arg,
1940+
"?No default answer is available - please be explicit.");
1941+
ret = 0;
1942+
goto getFullArg_ret;
1943+
}
1944+
}
1945+
/* Add new field to g_fullArg */
1946+
pntrLet(&g_fullArg,pntrAddElement(g_fullArg));
1947+
if (pntrLen(g_fullArg) != arg + 1) bug(1107);
1948+
else let((vstring *)(&g_fullArg[arg]),keyword);
19241949
}
1925-
/* Add new field to g_fullArg */
1926-
pntrLet(&g_fullArg,pntrAddElement(g_fullArg));
1927-
if (pntrLen(g_fullArg) != arg + 1) bug(1107);
1928-
let((vstring *)(&g_fullArg[arg]),keyword);
1929-
1930-
/* Deallocate memory */
1931-
j = pntrLen(possCmd);
1932-
for (i = 0; i < j; i++) free_vstring(*(vstring *)(&possCmd[i]));
1933-
free_pntrString(possCmd);
1934-
free_vstring(defaultCmd);
1935-
free_vstring(infoStr);
1936-
free_vstring(tmpStr);
1937-
free_vstring(tmpArg);
1938-
free_vstring(errorLine);
1939-
free_vstring(keyword);
1940-
free_vstring(cmdList);
1941-
return(1);
19421950

1943-
return0:
19441951
/* Deallocate memory */
1945-
j = pntrLen(possCmd);
1946-
for (i = 0; i < j; i++) free_vstring(*(vstring *)(&possCmd[i]));
1952+
long len = pntrLen(possCmd);
1953+
for (long i = 0; i < len; i++) free_vstring(*(vstring *)(&possCmd[i]));
19471954
free_pntrString(possCmd);
19481955
free_vstring(defaultCmd);
19491956
free_vstring(infoStr);
1950-
free_vstring(tmpStr);
1951-
free_vstring(tmpArg);
19521957
free_vstring(errorLine);
19531958
free_vstring(keyword);
19541959
free_vstring(cmdList);
1955-
return(0);
1956-
1960+
return ret;
19571961
} /* getFullArg */
19581962

19591963

src/mmcmdl.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include "mmdata.h"
1212

1313
flag processCommandLine(void);
14-
flag getFullArg(long arg, vstring cmdList);
1514
void parseCommandLine(vstring line);
1615
flag lastArgMatches(vstring argString);
1716
flag cmdMatches(vstring cmdString);

0 commit comments

Comments
 (0)