getopts.pp 13 KB

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