getopts.pp 14 KB

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