getopts.pp 12 KB

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