getopts.pp 13 KB

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