sort.c 9.9 KB

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