getopts.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Michael Van Canneyt,
  4. member of the Free Pascal development team.
  5. Getopt implementation for Free Pascal, modeled after GNU getopt
  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. Interface
  14. Const
  15. No_Argument = 0;
  16. Required_Argument = 1;
  17. Optional_Argument = 2;
  18. EndOfOptions = #255;
  19. Type
  20. POption = ^TOption;
  21. TOption = Record
  22. Name : String;
  23. Has_arg : Integer;
  24. Flag : PChar;
  25. Value : Char;
  26. end;
  27. Orderings = (require_order,permute,return_in_order);
  28. Const
  29. OptSpecifier : set of char=['-'];
  30. Var
  31. OptArg : String;
  32. OptInd : Longint;
  33. OptErr : Boolean;
  34. OptOpt : Char;
  35. Function GetOpt (ShortOpts : String) : char;
  36. Function GetLongOpts (ShortOpts : String;LongOpts : POption;var Longind : Longint) : char;
  37. Implementation
  38. {$IFNDEF FPC}
  39. {$ifdef TP}
  40. uses strings;
  41. {$else }
  42. uses SysUtils;
  43. type PtrInt = Integer;
  44. {$endif}
  45. {$ENDIF FPC}
  46. {***************************************************************************
  47. Create an ArgV
  48. ***************************************************************************}
  49. {$IF not Declared(argv) or (sizeof(RtlChar)<>1)} //{$ifdef TP}
  50. type
  51. ppchar = ^pchar;
  52. apchar = array[0..127] of pchar;
  53. var
  54. argc : longint;
  55. argv : apchar;
  56. procedure setup_arguments;
  57. var
  58. arglen,
  59. count : longint;
  60. argstart,
  61. cmdline : pchar;
  62. quote : set of char;
  63. argsbuf : array[0..127] of pchar;
  64. s : string;
  65. i : integer;
  66. begin
  67. { create argv[0] which is the started filename }
  68. s:=paramstr(0);
  69. arglen:=length(s);
  70. getmem(argsbuf[0],arglen + 1);
  71. move(s[1], argsbuf[0]^, arglen);
  72. argsbuf[0][arglen]:=#0;
  73. { create commandline }
  74. s:='';
  75. for i:=1 to paramcount do
  76. begin
  77. s:=s+paramstr(i)+' ';
  78. end;
  79. s:=s+#0;
  80. cmdline:=@s[1];
  81. count:=1;
  82. repeat
  83. { skip leading spaces }
  84. while cmdline^ in [' ',#9,#13] do
  85. inc(PtrInt(cmdline));
  86. case cmdline^ of
  87. #0 : break;
  88. '"' : begin
  89. quote:=['"'];
  90. inc(PtrInt(cmdline));
  91. end;
  92. '''' : begin
  93. quote:=[''''];
  94. inc(PtrInt(cmdline));
  95. end;
  96. else
  97. quote:=[' ',#9,#13];
  98. end;
  99. { scan until the end of the argument }
  100. argstart:=cmdline;
  101. while (cmdline^<>#0) and not(cmdline^ in quote) do
  102. inc(PtrInt(cmdline));
  103. { reserve some memory }
  104. arglen:=cmdline-argstart;
  105. getmem(argsbuf[count],arglen+1);
  106. move(argstart^,argsbuf[count]^,arglen);
  107. argsbuf[count][arglen]:=#0;
  108. { skip quote }
  109. if cmdline^ in quote then
  110. inc(PtrInt(cmdline));
  111. inc(count);
  112. until false;
  113. { create argc }
  114. argc:=count;
  115. { create an nil entry }
  116. argsbuf[count]:=nil;
  117. inc(count);
  118. { create the argv }
  119. move(argsbuf,argv,count shl 2);
  120. end;
  121. {$IFEND} //{$endif TP}
  122. {***************************************************************************
  123. Real Getopts
  124. ***************************************************************************}
  125. Var
  126. NextChar,
  127. Nrargs,
  128. first_nonopt,
  129. last_nonopt : Longint;
  130. Ordering : Orderings;
  131. Procedure Exchange;
  132. var
  133. bottom,
  134. middle,
  135. top,i,len : longint;
  136. temp : pchar;
  137. begin
  138. bottom:=first_nonopt;
  139. middle:=last_nonopt;
  140. top:=optind;
  141. while (top>middle) and (middle>bottom) do
  142. begin
  143. if (top-middle>middle-bottom) then
  144. begin
  145. len:=middle-bottom;
  146. for i:=1 to len-1 do
  147. begin
  148. temp:=argv[bottom+i];
  149. argv[bottom+i]:=argv[top-(middle-bottom)+i];
  150. argv[top-(middle-bottom)+i]:=temp;
  151. end;
  152. top:=top-len;
  153. end
  154. else
  155. begin
  156. len:=top-middle;
  157. for i:=0 to len-1 do
  158. begin
  159. temp:=argv[bottom+i];
  160. argv[bottom+i]:=argv[middle+i];
  161. argv[middle+i]:=temp;
  162. end;
  163. bottom:=bottom+len;
  164. end;
  165. end;
  166. first_nonopt:=first_nonopt + optind-last_nonopt;
  167. last_nonopt:=optind;
  168. end; { exchange }
  169. procedure getopt_init (var opts : string);
  170. begin
  171. { Initialize some defaults. }
  172. Optarg:='';
  173. Optind:=1;
  174. First_nonopt:=1;
  175. Last_nonopt:=1;
  176. OptOpt:='?';
  177. Nextchar:=0;
  178. case opts[1] of
  179. '-' : begin
  180. ordering:=return_in_order;
  181. delete(opts,1,1);
  182. end;
  183. '+' : begin
  184. ordering:=require_order;
  185. delete(opts,1,1);
  186. end;
  187. else
  188. ordering:=permute;
  189. end;
  190. end;
  191. Function Internal_getopt (Var Optstring : string;LongOpts : POption;
  192. LongInd : pointer;Long_only : boolean ) : char;
  193. var
  194. temp,endopt,
  195. option_index : byte;
  196. indfound : integer;
  197. currentarg,
  198. optname : string;
  199. p,pfound : POption;
  200. exact,ambig : boolean;
  201. c : char;
  202. begin
  203. optarg:='';
  204. if optind=0 then
  205. getopt_init(optstring);
  206. { Check if We need the next argument. }
  207. if (optind<nrargs) then
  208. currentarg:=strpas(argv[optind])
  209. else
  210. currentarg:='';
  211. if (nextchar=0) then
  212. begin
  213. if ordering=permute then
  214. begin
  215. { If we processed options following non-options : exchange }
  216. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  217. exchange
  218. else
  219. if last_nonopt<>optind then
  220. first_nonopt:=optind;
  221. while (optind<nrargs) and (not(argv[optind][0] in OptSpecifier) or
  222. (length(strpas(argv[optind]))=1)) do
  223. inc(optind);
  224. last_nonopt:=optind;
  225. end;
  226. { Check for '--' argument }
  227. if optind<nrargs then
  228. currentarg:=strpas(argv[optind])
  229. else
  230. currentarg:='';
  231. if (optind<>nrargs) and (currentarg='--') then
  232. begin
  233. inc(optind);
  234. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  235. exchange
  236. else
  237. if first_nonopt=last_nonopt then
  238. first_nonopt:=optind;
  239. last_nonopt:=nrargs;
  240. optind:=nrargs;
  241. end;
  242. { Are we at the end of all arguments ? }
  243. if optind>=nrargs then
  244. begin
  245. if first_nonopt<>last_nonopt then
  246. optind:=first_nonopt;
  247. Internal_getopt:=EndOfOptions;
  248. exit;
  249. end;
  250. if optind<nrargs then
  251. currentarg:=strpas(argv[optind])
  252. else
  253. currentarg:='';
  254. { Are we at a non-option ? }
  255. if not(currentarg[1] in OptSpecifier) or (length(currentarg)=1) then
  256. begin
  257. if ordering=require_order then
  258. begin
  259. Internal_getopt:=EndOfOptions;
  260. exit;
  261. end
  262. else
  263. begin
  264. optarg:=strpas(argv[optind]);
  265. inc(optind);
  266. Internal_getopt:=#0;
  267. exit;
  268. end;
  269. end;
  270. { At this point we're at an option ...}
  271. nextchar:=2;
  272. if (longopts<>nil) and ((currentarg[2]='-') and
  273. (currentArg[1]='-')) then
  274. inc(nextchar);
  275. { So, now nextchar points at the first character of an option }
  276. end;
  277. { Check if we have a long option }
  278. if longopts<>nil then
  279. if length(currentarg)>1 then
  280. if ((currentarg[2]='-') and (currentArg[1]='-'))
  281. or
  282. ((not long_only) and (pos(currentarg[2],optstring)<>0)) then
  283. begin
  284. { Get option name }
  285. endopt:=pos('=',currentarg);
  286. if endopt=0 then
  287. endopt:=length(currentarg)+1;
  288. optname:=copy(currentarg,nextchar,endopt-nextchar);
  289. { Match partial or full }
  290. p:=longopts;
  291. pfound:=nil;
  292. exact:=false;
  293. ambig:=false;
  294. option_index:=0;
  295. indfound:=0;
  296. while (p^.name<>'') and (not exact) do
  297. begin
  298. if pos(optname,p^.name)<>0 then
  299. begin
  300. if length(optname)=length(p^.name) then
  301. begin
  302. exact:=true;
  303. pfound:=p;
  304. indfound:=option_index;
  305. end
  306. else
  307. if pfound=nil then
  308. begin
  309. indfound:=option_index;
  310. pfound:=p
  311. end
  312. else
  313. ambig:=true;
  314. end;
  315. inc(PByte(p),sizeof(toption)); //inc(pointer(p),sizeof(toption)); // for Delphi compatibility
  316. inc(option_index);
  317. end;
  318. if ambig and not exact then
  319. begin
  320. if opterr then
  321. writeln(argv[0],': option "',optname,'" is ambiguous');
  322. nextchar:=0;
  323. inc(optind);
  324. Internal_getopt:='?';
  325. end;
  326. if pfound<>nil then
  327. begin
  328. inc(optind);
  329. if endopt<=length(currentarg) then
  330. begin
  331. if pfound^.has_arg>0 then
  332. optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt)
  333. else
  334. begin
  335. if opterr then
  336. if currentarg[2]='-' then
  337. writeln(argv[0],': option "--',pfound^.name,'" doesn''t allow an argument')
  338. else
  339. writeln(argv[0],': option "',currentarg[1],pfound^.name,'" doesn''t allow an argument');
  340. nextchar:=0;
  341. internal_getopt:='?';
  342. exit;
  343. end;
  344. end
  345. else { argument in next paramstr... }
  346. begin
  347. if pfound^.has_arg=1 then
  348. begin
  349. if optind<nrargs then
  350. begin
  351. optarg:=strpas(argv[optind]);
  352. inc(optind);
  353. end { required argument }
  354. else
  355. begin { no req argument}
  356. if opterr then
  357. writeln(argv[0],': option ',pfound^.name,' requires an argument');
  358. nextchar:=0;
  359. if optstring[1]=':' then
  360. Internal_getopt:=':'
  361. else
  362. Internal_getopt:='?';
  363. exit;
  364. end;
  365. end;
  366. end; { argument in next parameter end;}
  367. nextchar:=0;
  368. if longind<>nil then
  369. plongint(longind)^:=indfound+1;
  370. if pfound^.flag<>nil then
  371. begin
  372. pfound^.flag^:=pfound^.value;
  373. internal_getopt:=#0;
  374. exit;
  375. end;
  376. internal_getopt:=pfound^.value;
  377. exit;
  378. end; { pfound<>nil }
  379. { We didn't find it as an option }
  380. if (not long_only) or
  381. ((currentarg[2]='-') or (pos(CurrentArg[nextchar],optstring)=0)) then
  382. begin
  383. if opterr then
  384. if currentarg[2]='-' then
  385. writeln(argv[0],' unrecognized option "--',optname,'"')
  386. else
  387. writeln(argv[0],' unrecognized option "',currentarg[1],optname,'"');
  388. nextchar:=0;
  389. inc(optind);
  390. Internal_getopt:='?';
  391. exit;
  392. end;
  393. end; { Of long options.}
  394. { We check for a short option. }
  395. temp:=pos(currentarg[nextchar],optstring);
  396. c:=currentarg[nextchar];
  397. inc(nextchar);
  398. if nextchar>length(currentarg) then
  399. begin
  400. inc(optind);
  401. nextchar:=0;
  402. end;
  403. if (temp=0) or (c=':') then
  404. begin
  405. if opterr then
  406. writeln(argv[0],': illegal option -- ',c);
  407. optopt:=c;
  408. internal_getopt:='?';
  409. exit;
  410. end;
  411. Internal_getopt:=optstring[temp];
  412. if optstring[temp+1]=':' then
  413. if optstring[temp+2]=':' then
  414. begin { optional argument }
  415. if nextchar>0 then
  416. begin
  417. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  418. inc(optind);
  419. nextchar:=0;
  420. end else if (optind<>nrargs) then
  421. begin
  422. optarg:=strpas(argv[optind]);
  423. if optarg[1]='-' then
  424. optarg:=''
  425. else
  426. inc(optind);
  427. nextchar:=0;
  428. end;
  429. end
  430. else
  431. begin { required argument }
  432. if nextchar>0 then
  433. begin
  434. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  435. inc(optind);
  436. end
  437. else
  438. if (optind=nrargs) then
  439. begin
  440. if opterr then
  441. writeln (argv[0],': option requires an argument -- ',optstring[temp]);
  442. optopt:=optstring[temp];
  443. if optstring[1]=':' then
  444. Internal_getopt:=':'
  445. else
  446. Internal_Getopt:='?';
  447. end
  448. else
  449. begin
  450. optarg:=strpas(argv[optind]);
  451. inc(optind)
  452. end;
  453. nextchar:=0;
  454. end; { End of required argument}
  455. end; { End of internal getopt...}
  456. Function GetOpt(ShortOpts : String) : char;
  457. begin
  458. getopt:=internal_getopt(shortopts,nil,nil,false);
  459. end;
  460. Function GetLongOpts(ShortOpts : String;LongOpts : POption;var Longind : Longint) : char;
  461. begin
  462. getlongopts:=internal_getopt(shortopts,longopts,@longind,true);
  463. end;
  464. {$ifdef FPC}
  465. initialization
  466. {$endif}
  467. {$ifndef FPC}
  468. {$ifdef TP}
  469. begin
  470. {$else}
  471. initialization
  472. {$endif}
  473. {$endif}
  474. { create argv if running under TP }
  475. {$if not defined(FPC) or (sizeof(RtlChar)<>1)}
  476. setup_arguments;
  477. {$endif}
  478. { Needed to detect startup }
  479. Opterr:=true;
  480. Optind:=0;
  481. nrargs:=argc;
  482. end.