getopts.pp 11 KB

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