getopts.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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. ordering:=permute;
  192. if length(opts)>0 then
  193. case opts[1] of
  194. '-' : begin
  195. ordering:=return_in_order;
  196. delete(opts,1,1);
  197. end;
  198. '+' : begin
  199. ordering:=require_order;
  200. delete(opts,1,1);
  201. end;
  202. else
  203. ordering:=permute;
  204. end;
  205. end;
  206. Function Internal_getopt (Var Optstring : string;LongOpts : POption;
  207. LongInd : pointer;Long_only : boolean ) : char;
  208. var
  209. temp,endopt,
  210. option_index : byte;
  211. indfound : integer;
  212. currentarg,
  213. optname : string;
  214. p,pfound : POption;
  215. exact,ambig : boolean;
  216. c : char;
  217. begin
  218. optarg:='';
  219. if optind=0 then
  220. getopt_init(optstring);
  221. { Check if We need the next argument. }
  222. if (optind<nrargs) then
  223. currentarg:=strpas(argv[optind])
  224. else
  225. currentarg:='';
  226. if (nextchar=0) then
  227. begin
  228. if ordering=permute then
  229. begin
  230. { If we processed options following non-options : exchange }
  231. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  232. exchange
  233. else
  234. if last_nonopt<>optind then
  235. first_nonopt:=optind;
  236. while (optind<nrargs) and (not(argv[optind][0] in OptSpecifier) or
  237. (length(strpas(argv[optind]))=1)) do
  238. inc(optind);
  239. last_nonopt:=optind;
  240. end;
  241. { Check for '--' argument }
  242. if optind<nrargs then
  243. currentarg:=strpas(argv[optind])
  244. else
  245. currentarg:='';
  246. if (optind<>nrargs) and (currentarg='--') then
  247. begin
  248. inc(optind);
  249. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  250. exchange
  251. else
  252. if first_nonopt=last_nonopt then
  253. first_nonopt:=optind;
  254. last_nonopt:=nrargs;
  255. optind:=nrargs;
  256. end;
  257. { Are we at the end of all arguments ? }
  258. if optind>=nrargs then
  259. begin
  260. if first_nonopt<>last_nonopt then
  261. optind:=first_nonopt;
  262. Internal_getopt:=EndOfOptions;
  263. exit;
  264. end;
  265. if optind<nrargs then
  266. currentarg:=strpas(argv[optind])
  267. else
  268. currentarg:='';
  269. { Are we at a non-option ? }
  270. if not(currentarg[1] in OptSpecifier) or (length(currentarg)=1) then
  271. begin
  272. if ordering=require_order then
  273. begin
  274. Internal_getopt:=EndOfOptions;
  275. exit;
  276. end
  277. else
  278. begin
  279. optarg:=strpas(argv[optind]);
  280. inc(optind);
  281. Internal_getopt:=#0;
  282. exit;
  283. end;
  284. end;
  285. { At this point we're at an option ...}
  286. nextchar:=2;
  287. if (longopts<>nil) and ((currentarg[2]='-') and
  288. (currentArg[1]='-')) then
  289. inc(nextchar);
  290. { So, now nextchar points at the first character of an option }
  291. end;
  292. { Check if we have a long option }
  293. if longopts<>nil then
  294. if length(currentarg)>1 then
  295. if ((currentarg[2]='-') and (currentArg[1]='-'))
  296. or
  297. ((not long_only) and (pos(currentarg[2],optstring)<>0)) then
  298. begin
  299. { Get option name }
  300. endopt:=pos('=',currentarg);
  301. if endopt=0 then
  302. endopt:=length(currentarg)+1;
  303. optname:=copy(currentarg,nextchar,endopt-nextchar);
  304. { Match partial or full }
  305. p:=longopts;
  306. pfound:=nil;
  307. exact:=false;
  308. ambig:=false;
  309. option_index:=0;
  310. indfound:=0;
  311. while (p^.name<>'') and (not exact) do
  312. begin
  313. if pos(optname,p^.name)<>0 then
  314. begin
  315. if length(optname)=length(p^.name) then
  316. begin
  317. exact:=true;
  318. pfound:=p;
  319. indfound:=option_index;
  320. end
  321. else
  322. if pfound=nil then
  323. begin
  324. indfound:=option_index;
  325. pfound:=p
  326. end
  327. else
  328. ambig:=true;
  329. end;
  330. inc(PByte(p),sizeof(toption)); //inc(pointer(p),sizeof(toption)); // for Delphi compatibility
  331. inc(option_index);
  332. end;
  333. if ambig and not exact then
  334. begin
  335. if opterr then
  336. writeln(argv[0],': option "',optname,'" is ambiguous');
  337. nextchar:=0;
  338. inc(optind);
  339. Internal_getopt:='?';
  340. exit;
  341. end;
  342. if pfound<>nil then
  343. begin
  344. inc(optind);
  345. if endopt<=length(currentarg) then
  346. begin
  347. if pfound^.has_arg>0 then
  348. optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt)
  349. else
  350. begin
  351. if opterr then
  352. if currentarg[2]='-' then
  353. writeln(argv[0],': option "--',pfound^.name,'" doesn''t allow an argument')
  354. else
  355. writeln(argv[0],': option "',currentarg[1],pfound^.name,'" doesn''t allow an argument');
  356. nextchar:=0;
  357. internal_getopt:='?';
  358. exit;
  359. end;
  360. end
  361. else { argument in next paramstr... }
  362. begin
  363. if pfound^.has_arg=1 then
  364. begin
  365. if optind<nrargs then
  366. begin
  367. optarg:=strpas(argv[optind]);
  368. inc(optind);
  369. end { required argument }
  370. else
  371. begin { no req argument}
  372. if opterr then
  373. writeln(argv[0],': option ',pfound^.name,' requires an argument');
  374. nextchar:=0;
  375. if optstring[1]=':' then
  376. Internal_getopt:=':'
  377. else
  378. Internal_getopt:='?';
  379. exit;
  380. end;
  381. end;
  382. end; { argument in next parameter end;}
  383. nextchar:=0;
  384. if longind<>nil then
  385. plongint(longind)^:=indfound+1;
  386. if pfound^.flag<>nil then
  387. begin
  388. pfound^.flag^:=pfound^.value;
  389. internal_getopt:=#0;
  390. exit;
  391. end;
  392. internal_getopt:=pfound^.value;
  393. exit;
  394. end; { pfound<>nil }
  395. { We didn't find it as an option }
  396. if (not long_only) or
  397. ((currentarg[2]='-') or (pos(CurrentArg[nextchar],optstring)=0)) then
  398. begin
  399. if opterr then
  400. if currentarg[2]='-' then
  401. writeln(argv[0],' unrecognized option "--',optname,'"')
  402. else
  403. writeln(argv[0],' unrecognized option "',currentarg[1],optname,'"');
  404. nextchar:=0;
  405. inc(optind);
  406. Internal_getopt:='?';
  407. exit;
  408. end;
  409. end; { Of long options.}
  410. { We check for a short option. }
  411. temp:=pos(currentarg[nextchar],optstring);
  412. c:=currentarg[nextchar];
  413. inc(nextchar);
  414. if nextchar>length(currentarg) then
  415. begin
  416. inc(optind);
  417. nextchar:=0;
  418. end;
  419. if (temp=0) or (c=':') then
  420. begin
  421. if opterr then
  422. writeln(argv[0],': illegal option -- ',c);
  423. optopt:=c;
  424. internal_getopt:='?';
  425. exit;
  426. end;
  427. Internal_getopt:=optstring[temp];
  428. if (length(optstring)>temp) and (optstring[temp+1]=':') then
  429. if (length(optstring)>temp+1) and (optstring[temp+2]=':') then
  430. begin { optional argument }
  431. if nextchar>0 then
  432. begin
  433. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  434. inc(optind);
  435. nextchar:=0;
  436. end else if (optind<>nrargs) then
  437. begin
  438. optarg:=strpas(argv[optind]);
  439. if optarg[1]='-' then
  440. optarg:=''
  441. else
  442. inc(optind);
  443. nextchar:=0;
  444. end;
  445. end
  446. else
  447. begin { required argument }
  448. if nextchar>0 then
  449. begin
  450. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  451. inc(optind);
  452. end
  453. else
  454. if (optind=nrargs) then
  455. begin
  456. if opterr then
  457. writeln (argv[0],': option requires an argument -- ',optstring[temp]);
  458. optopt:=optstring[temp];
  459. if optstring[1]=':' then
  460. Internal_getopt:=':'
  461. else
  462. Internal_Getopt:='?';
  463. end
  464. else
  465. begin
  466. optarg:=strpas(argv[optind]);
  467. inc(optind)
  468. end;
  469. nextchar:=0;
  470. end; { End of required argument}
  471. end; { End of internal getopt...}
  472. Function GetOpt(ShortOpts : String) : char;
  473. begin
  474. getopt:=internal_getopt(shortopts,nil,nil,false);
  475. end;
  476. Function GetLongOpts(ShortOpts : String;LongOpts : POption;var Longind : Longint) : char;
  477. begin
  478. getlongopts:=internal_getopt(shortopts,longopts,@longind,true);
  479. end;
  480. initialization
  481. { create argv if not running under FPC }
  482. {$ifndef FPC}
  483. setup_arguments;
  484. {$endif}
  485. { Needed to detect startup }
  486. Opterr:=true;
  487. Optind:=0;
  488. nrargs:=argc;
  489. end.