getopts.pp 13 KB

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