sort.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /*
  2. * sort.c $Id: sort.c,v 4.14 2008/06/14 22:24:22 bkorb Exp $
  3. * Time-stamp: "2007-07-04 11:34:52 bkorb"
  4. *
  5. * This module implements argument sorting.
  6. *
  7. * This file is part of AutoOpts, a companion to AutoGen.
  8. * AutoOpts is free software.
  9. * AutoOpts is copyright (c) 1992-2008 by Bruce Korb - all rights reserved
  10. * AutoOpts is copyright (c) 1992-2008 by Bruce Korb - all rights reserved
  11. *
  12. * AutoOpts is available under any one of two licenses. The license
  13. * in use must be one of these two and the choice is under the control
  14. * of the user of the license.
  15. *
  16. * The GNU Lesser General Public License, version 3 or later
  17. * See the files "COPYING.lgplv3" and "COPYING.gplv3"
  18. *
  19. * The Modified Berkeley Software Distribution License
  20. * See the file "COPYING.mbsd"
  21. *
  22. * These files have the following md5sums:
  23. *
  24. * 239588c55c22c60ffe159946a760a33e pkg/libopts/COPYING.gplv3
  25. * fa82ca978890795162346e661b47161a pkg/libopts/COPYING.lgplv3
  26. * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
  27. */
  28. /* = = = START-STATIC-FORWARD = = = */
  29. /* static forward declarations maintained by mk-fwd */
  30. static tSuccess
  31. mustHandleArg( tOptions* pOpts, char* pzArg, tOptState* pOS,
  32. char** ppzOpts, int* pOptsIdx );
  33. static tSuccess
  34. mayHandleArg( tOptions* pOpts, char* pzArg, tOptState* pOS,
  35. char** ppzOpts, int* pOptsIdx );
  36. static tSuccess
  37. checkShortOpts( tOptions* pOpts, char* pzArg, tOptState* pOS,
  38. char** ppzOpts, int* pOptsIdx );
  39. /* = = = END-STATIC-FORWARD = = = */
  40. /*
  41. * "mustHandleArg" and "mayHandleArg" are really similar. The biggest
  42. * difference is that "may" will consume the next argument only if it
  43. * does not start with a hyphen and "must" will consume it, hyphen or not.
  44. */
  45. static tSuccess
  46. mustHandleArg( tOptions* pOpts, char* pzArg, tOptState* pOS,
  47. char** ppzOpts, int* pOptsIdx )
  48. {
  49. /*
  50. * An option argument is required. Long options can either have
  51. * a separate command line argument, or an argument attached by
  52. * the '=' character. Figure out which.
  53. */
  54. switch (pOS->optType) {
  55. case TOPT_SHORT:
  56. /*
  57. * See if an arg string follows the flag character. If not,
  58. * the next arg must be the option argument.
  59. */
  60. if (*pzArg != NUL)
  61. return SUCCESS;
  62. break;
  63. case TOPT_LONG:
  64. /*
  65. * See if an arg string has already been assigned (glued on
  66. * with an `=' character). If not, the next is the opt arg.
  67. */
  68. if (pOS->pzOptArg != NULL)
  69. return SUCCESS;
  70. break;
  71. default:
  72. return FAILURE;
  73. }
  74. if (pOpts->curOptIdx >= pOpts->origArgCt)
  75. return FAILURE;
  76. ppzOpts[ (*pOptsIdx)++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ];
  77. return SUCCESS;
  78. }
  79. static tSuccess
  80. mayHandleArg( tOptions* pOpts, char* pzArg, tOptState* pOS,
  81. char** ppzOpts, int* pOptsIdx )
  82. {
  83. /*
  84. * An option argument is optional.
  85. */
  86. switch (pOS->optType) {
  87. case TOPT_SHORT:
  88. /*
  89. * IF nothing is glued on after the current flag character,
  90. * THEN see if there is another argument. If so and if it
  91. * does *NOT* start with a hyphen, then it is the option arg.
  92. */
  93. if (*pzArg != NUL)
  94. return SUCCESS;
  95. break;
  96. case TOPT_LONG:
  97. /*
  98. * Look for an argument if we don't already have one (glued on
  99. * with a `=' character)
  100. */
  101. if (pOS->pzOptArg != NULL)
  102. return SUCCESS;
  103. break;
  104. default:
  105. return FAILURE;
  106. }
  107. if (pOpts->curOptIdx >= pOpts->origArgCt)
  108. return PROBLEM;
  109. pzArg = pOpts->origArgVect[ pOpts->curOptIdx ];
  110. if (*pzArg != '-')
  111. ppzOpts[ (*pOptsIdx)++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ];
  112. return SUCCESS;
  113. }
  114. /*
  115. * Process a string of short options glued together. If the last one
  116. * does or may take an argument, the do the argument processing and leave.
  117. */
  118. static tSuccess
  119. checkShortOpts( tOptions* pOpts, char* pzArg, tOptState* pOS,
  120. char** ppzOpts, int* pOptsIdx )
  121. {
  122. while (*pzArg != NUL) {
  123. if (FAILED( shortOptionFind( pOpts, (tAoUC)*pzArg, pOS )))
  124. return FAILURE;
  125. /*
  126. * See if we can have an arg.
  127. */
  128. if (OPTST_GET_ARGTYPE(pOS->pOD->fOptState) == OPARG_TYPE_NONE) {
  129. pzArg++;
  130. } else if (pOS->pOD->fOptState & OPTST_ARG_OPTIONAL) {
  131. /*
  132. * Take an argument if it is not attached and it does not
  133. * start with a hyphen.
  134. */
  135. if (pzArg[1] != NUL)
  136. return SUCCESS;
  137. pzArg = pOpts->origArgVect[ pOpts->curOptIdx ];
  138. if (*pzArg != '-')
  139. ppzOpts[ (*pOptsIdx)++ ] =
  140. pOpts->origArgVect[ (pOpts->curOptIdx)++ ];
  141. return SUCCESS;
  142. } else {
  143. /*
  144. * IF we need another argument, be sure it is there and
  145. * take it.
  146. */
  147. if (pzArg[1] == NUL) {
  148. if (pOpts->curOptIdx >= pOpts->origArgCt)
  149. return FAILURE;
  150. ppzOpts[ (*pOptsIdx)++ ] =
  151. pOpts->origArgVect[ (pOpts->curOptIdx)++ ];
  152. }
  153. return SUCCESS;
  154. }
  155. }
  156. return SUCCESS;
  157. }
  158. /*
  159. * If the program wants sorted options (separated operands and options),
  160. * then this routine will to the trick.
  161. */
  162. LOCAL void
  163. optionSort( tOptions* pOpts )
  164. {
  165. char** ppzOpts;
  166. char** ppzOpds;
  167. int optsIdx = 0;
  168. int opdsIdx = 0;
  169. tOptState os = OPTSTATE_INITIALIZER(DEFINED);
  170. /*
  171. * Disable for POSIX conformance, or if there are no operands.
  172. */
  173. if ( (getenv( "POSIXLY_CORRECT" ) != NULL)
  174. || NAMED_OPTS(pOpts))
  175. return;
  176. /*
  177. * Make sure we can allocate two full-sized arg vectors.
  178. */
  179. ppzOpts = malloc( pOpts->origArgCt * sizeof( char* ));
  180. if (ppzOpts == NULL)
  181. goto exit_no_mem;
  182. ppzOpds = malloc( pOpts->origArgCt * sizeof( char* ));
  183. if (ppzOpds == NULL) {
  184. free( ppzOpts );
  185. goto exit_no_mem;
  186. }
  187. pOpts->curOptIdx = 1;
  188. pOpts->pzCurOpt = NULL;
  189. /*
  190. * Now, process all the options from our current position onward.
  191. * (This allows interspersed options and arguments for the few
  192. * non-standard programs that require it.)
  193. */
  194. for (;;) {
  195. char* pzArg;
  196. tSuccess res;
  197. /*
  198. * If we're out of arguments, we're done. Join the option and
  199. * operand lists into the original argument vector.
  200. */
  201. if (pOpts->curOptIdx >= pOpts->origArgCt) {
  202. errno = 0;
  203. goto joinLists;
  204. }
  205. pzArg = pOpts->origArgVect[ pOpts->curOptIdx ];
  206. if (*pzArg != '-') {
  207. ppzOpds[ opdsIdx++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ];
  208. continue;
  209. }
  210. switch (pzArg[1]) {
  211. case NUL:
  212. /*
  213. * A single hyphen is an operand.
  214. */
  215. ppzOpds[ opdsIdx++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ];
  216. continue;
  217. case '-':
  218. /*
  219. * Two consecutive hypens. Put them on the options list and then
  220. * _always_ force the remainder of the arguments to be operands.
  221. */
  222. if (pzArg[2] == NUL) {
  223. ppzOpts[ optsIdx++ ] =
  224. pOpts->origArgVect[ (pOpts->curOptIdx)++ ];
  225. goto restOperands;
  226. }
  227. res = longOptionFind( pOpts, pzArg+2, &os );
  228. break;
  229. default:
  230. /*
  231. * If short options are not allowed, then do long
  232. * option processing. Otherwise the character must be a
  233. * short (i.e. single character) option.
  234. */
  235. if ((pOpts->fOptSet & OPTPROC_SHORTOPT) == 0) {
  236. res = longOptionFind( pOpts, pzArg+1, &os );
  237. } else {
  238. res = shortOptionFind( pOpts, (tAoUC)pzArg[1], &os );
  239. }
  240. break;
  241. }
  242. if (FAILED( res )) {
  243. errno = EINVAL;
  244. goto freeTemps;
  245. }
  246. /*
  247. * We've found an option. Add the argument to the option list.
  248. * Next, we have to see if we need to pull another argument to be
  249. * used as the option argument.
  250. */
  251. ppzOpts[ optsIdx++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ];
  252. if (OPTST_GET_ARGTYPE(os.pOD->fOptState) == OPARG_TYPE_NONE) {
  253. /*
  254. * No option argument. If we have a short option here,
  255. * then scan for short options until we get to the end
  256. * of the argument string.
  257. */
  258. if ( (os.optType == TOPT_SHORT)
  259. && FAILED( checkShortOpts( pOpts, pzArg+2, &os,
  260. ppzOpts, &optsIdx )) ) {
  261. errno = EINVAL;
  262. goto freeTemps;
  263. }
  264. } else if (os.pOD->fOptState & OPTST_ARG_OPTIONAL) {
  265. switch (mayHandleArg( pOpts, pzArg+2, &os, ppzOpts, &optsIdx )) {
  266. case FAILURE: errno = EIO; goto freeTemps;
  267. case PROBLEM: errno = 0; goto joinLists;
  268. }
  269. } else {
  270. switch (mustHandleArg( pOpts, pzArg+2, &os, ppzOpts, &optsIdx )) {
  271. case PROBLEM:
  272. case FAILURE: errno = EIO; goto freeTemps;
  273. }
  274. }
  275. } /* for (;;) */
  276. restOperands:
  277. while (pOpts->curOptIdx < pOpts->origArgCt)
  278. ppzOpds[ opdsIdx++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ];
  279. joinLists:
  280. if (optsIdx > 0)
  281. memcpy( pOpts->origArgVect + 1, ppzOpts, optsIdx * sizeof( char* ));
  282. if (opdsIdx > 0)
  283. memcpy( pOpts->origArgVect + 1 + optsIdx,
  284. ppzOpds, opdsIdx * sizeof( char* ));
  285. freeTemps:
  286. free( ppzOpts );
  287. free( ppzOpds );
  288. return;
  289. exit_no_mem:
  290. errno = ENOMEM;
  291. return;
  292. }
  293. /*
  294. * Local Variables:
  295. * mode: C
  296. * c-file-style: "stroustrup"
  297. * indent-tabs-mode: nil
  298. * End:
  299. * end of autoopts/sort.c */