getopts.pp 13 KB

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