getopts.pp 13 KB

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