getopts.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1993,97 by Michael Van Canneyt,
  5. member of the Free Pascal development team.
  6. Getopt implementation for Free Pascal, modeled after GNU getopt.
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. unit getopts;
  14. {$I os.inc}
  15. { --------------------------------------------------------------------
  16. *NOTE*
  17. The routines are a more or less straightforward conversion
  18. of the GNU C implementation of getopt. One day they should be
  19. replaced by some 'real pascal code'.
  20. -------------------------------------------------------------------- }
  21. Interface
  22. Const No_Argument = 0;
  23. Required_Argument = 1;
  24. Optional_Argument = 2;
  25. EndOfOptions = #255;
  26. Type TOption = Record
  27. Name : String;
  28. Has_arg : Integer;
  29. Flag : PChar;
  30. Value : Char;
  31. end;
  32. POption = ^TOption;
  33. Orderings = (require_order,permute,return_in_order);
  34. Var OptArg : String;
  35. OptInd : Longint;
  36. OptErr : Boolean;
  37. OptOpt : Char;
  38. Function GetOpt (ShortOpts : String) : char;
  39. Function GetLongOpts (ShortOpts : String;
  40. LongOpts : POption;
  41. var Longind : Integer) : char;
  42. Implementation
  43. Var
  44. NextChar,
  45. Nrargs,
  46. first_nonopt,
  47. last_nonopt : Longint;
  48. Ordering : Orderings;
  49. Procedure Exchange;
  50. var
  51. bottom,
  52. middle,
  53. top,i,len : longint;
  54. temp : pchar;
  55. begin
  56. bottom:=first_nonopt;
  57. middle:=last_nonopt;
  58. top:=optind;
  59. while (top>middle) and (middle>bottom) do
  60. begin
  61. if (top-middle>middle-bottom) then
  62. begin
  63. len:=middle-bottom;
  64. for i:=1 to len-1 do
  65. begin
  66. temp:=argv[bottom+i];
  67. argv[bottom+i]:=argv[top-(middle-bottom)+i];
  68. argv[top-(middle-bottom)+i]:=temp;
  69. end;
  70. top:=top-len;
  71. end
  72. else
  73. begin
  74. len:=top-middle;
  75. for i:=0 to len-1 do
  76. begin
  77. temp:=argv[bottom+i];
  78. argv[bottom+i]:=argv[middle+i];
  79. argv[middle+i]:=temp;
  80. end;
  81. bottom:=bottom+len;
  82. end;
  83. end;
  84. first_nonopt:=first_nonopt + optind-last_nonopt;
  85. last_nonopt:=optind;
  86. end; { exchange }
  87. procedure getopt_init (var opts : string);
  88. begin
  89. { Initialize some defaults. }
  90. Optarg:='';
  91. Optind:=1;
  92. First_nonopt:=1;
  93. Last_nonopt:=1;
  94. OptOpt:='?';
  95. Nextchar:=0;
  96. if opts[1]='-' then
  97. begin
  98. ordering:=return_in_order;
  99. delete(opts,1,1);
  100. end
  101. else if opts[1]='+' then
  102. begin
  103. ordering:=require_order;
  104. delete(opts,1,1);
  105. end
  106. else ordering:=permute;
  107. end;
  108. Function Internal_getopt (Var Optstring : string;
  109. LongOpts : POption;
  110. LongInd : pointer;
  111. Long_only : boolean ) : char;
  112. type
  113. pinteger=^integer;
  114. var
  115. temp,endopt,option_index : byte;
  116. indfound: integer;
  117. currentarg,optname : string;
  118. p,pfound : POption;
  119. exact,ambig : boolean;
  120. c : char;
  121. begin
  122. optarg:='';
  123. if optind=0 then
  124. getopt_init(optstring);
  125. { Check if We need the next argument. }
  126. if optind<nrargs then currentarg:=strpas(argv[optind]) else currentarg:='';
  127. if (nextchar=0) then
  128. begin
  129. if ordering=permute then
  130. begin
  131. { If we processed options following non-options : exchange }
  132. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  133. exchange
  134. else
  135. if last_nonopt<>optind then
  136. first_nonopt:=optind;
  137. while (optind<nrargs) and ((argv[optind][0]<>'-') or
  138. (length(strpas(argv[optind]))=1)) do
  139. inc(optind);
  140. last_nonopt:=optind;
  141. end;
  142. { Check for '--' argument }
  143. if optind<nrargs then
  144. currentarg:=strpas(argv[optind])
  145. else
  146. currentarg:='';
  147. if (optind<>nrargs) and (currentarg='--') then
  148. begin
  149. inc(optind);
  150. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  151. exchange
  152. else
  153. if first_nonopt=last_nonopt then
  154. first_nonopt:=optind;
  155. last_nonopt:=nrargs;
  156. optind:=nrargs;
  157. end;
  158. { Are we at the end of all arguments ? }
  159. if optind>=nrargs then
  160. begin
  161. if first_nonopt<>last_nonopt then
  162. optind:=first_nonopt;
  163. Internal_getopt:=EndOfOptions;
  164. exit;
  165. end;
  166. if optind<nrargs then
  167. currentarg:=strpas(argv[optind])
  168. else
  169. currentarg:='';
  170. { Are we at a non-option ? }
  171. if (currentarg[1]<>'-') or (currentarg='-') then
  172. begin
  173. if ordering=require_order then
  174. begin
  175. Internal_getopt:=EndOfOptions;
  176. exit;
  177. end
  178. else
  179. begin
  180. optarg:=strpas(argv[optind]);
  181. inc(optind);
  182. Internal_getopt:=#1;
  183. exit;
  184. end;
  185. end;
  186. { At this point we're at an option ...}
  187. nextchar:=2;
  188. if (longopts<>nil) and (currentarg[2]='-') then
  189. inc(nextchar);
  190. { So, now nextchar points at the first character of an option }
  191. end;
  192. { Check if we have a long option }
  193. if longopts<>nil then
  194. if length(currentarg)>1 then
  195. if (currentarg[2]='-') or
  196. ((not long_only) and (pos(currentarg[2],optstring)<>0)) then
  197. begin
  198. { Get option name }
  199. endopt:=pos('=',currentarg);
  200. if endopt=0 then
  201. endopt:=length(currentarg)+1;
  202. optname:=copy(currentarg,nextchar,endopt-nextchar);
  203. { Match partial or full }
  204. p:=longopts;
  205. pfound:=nil;
  206. exact:=false;
  207. ambig:=false;
  208. option_index:=0;
  209. indfound:=0;
  210. while (p^.name<>'') and (not exact) do
  211. begin
  212. if pos(optname,p^.name)<>0 then
  213. begin
  214. if length(optname)=length(p^.name) then
  215. begin
  216. exact:=true;
  217. pfound:=p;
  218. indfound:=option_index;
  219. end
  220. else
  221. if pfound=nil then
  222. begin
  223. indfound:=option_index;
  224. pfound:=p
  225. end
  226. else
  227. ambig:=true;
  228. end;
  229. inc (longint(p),sizeof(toption));
  230. inc (option_index);
  231. end;
  232. if ambig and not exact then
  233. begin
  234. if opterr then
  235. writeln (paramstr(0),': option "',optname,'" is ambiguous');
  236. nextchar:=0;
  237. inc(optind);
  238. Internal_getopt:='?';
  239. end;
  240. if pfound<>nil then
  241. begin
  242. inc(optind);
  243. if endopt<=length(currentarg) then
  244. begin
  245. if pfound^.has_arg>0 then
  246. optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt)
  247. else
  248. begin
  249. if opterr then
  250. if currentarg[2]='-' then
  251. writeln (paramstr(0),': option "--',pfound^.name,'" doesn''t allow an argument')
  252. else
  253. writeln (paramstr(0),': option "',currentarg[1],pfound^.name,'" doesn''t allow an argument');
  254. nextchar:=0;
  255. internal_getopt:='?';
  256. exit;
  257. end;
  258. end
  259. else { argument in next paramstr... }
  260. begin
  261. if pfound^.has_arg=1 then
  262. begin
  263. if optind<nrargs then
  264. begin
  265. optarg:=strpas(argv[optind]);
  266. inc(optind);
  267. end { required argument }
  268. else
  269. begin { no req argument}
  270. if opterr then
  271. writeln (paramstr(0),': option ',pfound^.name,' requires an argument');
  272. nextchar:=0;
  273. if optstring[1]=':' then
  274. Internal_getopt:=':'
  275. else
  276. Internal_getopt:='?';
  277. exit;
  278. end;
  279. end;
  280. end; { argument in next parameter end;}
  281. nextchar:=0;
  282. if longind<>nil then
  283. pinteger(longind)^:=indfound+1;
  284. if pfound^.flag<>nil then
  285. begin
  286. pfound^.flag^:=pfound^.value;
  287. internal_getopt:=#0;
  288. exit;
  289. end;
  290. internal_getopt:=pfound^.value;
  291. exit;
  292. end; { pfound<>nil }
  293. { We didn't find it as an option }
  294. if (not long_only) or
  295. ((currentarg[2]='-') or (pos(CurrentArg[nextchar],optstring)=0)) then
  296. begin
  297. if opterr then
  298. if currentarg[2]='-' then
  299. writeln (paramstr(0),' unrecognized option "--',optname,'"')
  300. else
  301. writeln (paramstr(0),' unrecognized option "',currentarg[1],optname,'"');
  302. nextchar:=0;
  303. inc(optind);
  304. Internal_getopt:='?';
  305. exit;
  306. end;
  307. end; { Of long options.}
  308. { We check for a short option. }
  309. temp:=pos(currentarg[nextchar],optstring);
  310. c:=currentarg[nextchar];
  311. inc(nextchar);
  312. if nextchar>length(currentarg) then
  313. begin
  314. inc(optind);
  315. nextchar:=0;
  316. end;
  317. if (temp=0) or (c=':') then
  318. begin
  319. if opterr then
  320. writeln (paramstr(0),': illegal option -- ',c);
  321. optopt:=currentarg[nextchar-1];
  322. internal_getopt:='?';
  323. exit;
  324. end;
  325. Internal_getopt:=optstring[temp];
  326. if optstring[temp+1]=':' then
  327. if currentarg[temp+2]=':' then
  328. begin { optional argument }
  329. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  330. nextchar:=0;
  331. end
  332. else
  333. begin { required argument }
  334. if nextchar>0 then
  335. begin
  336. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  337. inc(optind)
  338. end
  339. else
  340. if (optind=nrargs) then
  341. begin
  342. if opterr then
  343. writeln (paramstr(0),': option requires an argument -- ',optstring[temp]);
  344. optopt:=optstring[temp];
  345. if optstring[1]=':' then
  346. Internal_getopt:=':'
  347. else
  348. Internal_Getopt:='?'
  349. end
  350. else
  351. begin
  352. optarg:=strpas(argv[optind]);
  353. inc(optind)
  354. end;
  355. nextchar:=0;
  356. end; { End of required argument}
  357. end; { End of internal getopt...}
  358. Function GetOpt(ShortOpts : String) : char;
  359. begin
  360. getopt:=internal_getopt (shortopts,nil,nil,false);
  361. end;
  362. Function GetLongOpts(ShortOpts : String;
  363. LongOpts : POption;
  364. var Longind : Integer) : char;
  365. begin
  366. getlongopts:=internal_getopt ( shortopts,longopts,@longind,true);
  367. end;
  368. begin
  369. { Needed to detect startup }
  370. Opterr:=true;
  371. Optind:=0;
  372. nrargs:=paramcount+1;
  373. end.
  374. {
  375. $Log$
  376. Revision 1.1 1998-05-12 10:42:45 peter
  377. * moved getopts to inc/, all supported OS's need argc,argv exported
  378. + strpas, strlen are now exported in the systemunit
  379. * removed logs
  380. * removed $ifdef ver_above
  381. }