getopts.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  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. {***************************************************************************
  40. Create an ArgV
  41. ***************************************************************************}
  42. uses SysUtils;
  43. type PtrInt = Integer;
  44. type
  45. ppchar = ^pchar;
  46. apchar = array[0..127] of pchar;
  47. var
  48. argc : longint;
  49. argv : apchar;
  50. const
  51. CHAR_SIZE = SizeOf(Char);
  52. procedure setup_arguments;
  53. var
  54. arglen,
  55. count : longint;
  56. argstart,
  57. cmdline : pchar;
  58. quote : set of char;
  59. argsbuf : array[0..127] of pchar;
  60. s : string;
  61. i : integer;
  62. begin
  63. { create argv[0] which is the started filename }
  64. s:=paramstr(0);
  65. arglen:=length(s);
  66. getmem(argsbuf[0], ( ( arglen + 1 ) * CHAR_SIZE ) );
  67. strpcopy(argsbuf[0],s);
  68. { create commandline }
  69. s:='';
  70. for i:=1 to paramcount do
  71. begin
  72. if Pos(' ', paramstr(i)) > 0 then
  73. s := s + '"' + paramstr(i) + '" '
  74. else
  75. s:=s+paramstr(i)+' ';
  76. end;
  77. s:=s+#0;
  78. cmdline:=@s[1];
  79. count:=1;
  80. repeat
  81. { skip leading spaces }
  82. while cmdline^ in [' ',#9,#13] do
  83. inc(PtrInt(cmdline),CHAR_SIZE);
  84. case cmdline^ of
  85. #0 : break;
  86. '"' : begin
  87. quote:=['"'];
  88. inc(PtrInt(cmdline),CHAR_SIZE);
  89. end;
  90. '''' : begin
  91. quote:=[''''];
  92. inc(PtrInt(cmdline),CHAR_SIZE);
  93. end;
  94. else
  95. quote:=[' ',#9,#13];
  96. end;
  97. { scan until the end of the argument }
  98. argstart:=cmdline;
  99. while (cmdline^<>#0) and not(cmdline^ in quote) do
  100. inc(PtrInt(cmdline),CHAR_SIZE);
  101. { reserve some memory }
  102. arglen:=cmdline-argstart;
  103. getmem(argsbuf[count],(arglen+1) * CHAR_SIZE);
  104. move(argstart^,argsbuf[count]^,arglen * CHAR_SIZE);
  105. argsbuf[count][arglen]:=#0;
  106. { skip quote }
  107. if cmdline^ in quote then
  108. inc(PtrInt(cmdline),CHAR_SIZE);
  109. inc(count);
  110. until false;
  111. { create argc }
  112. argc:=count;
  113. { create an nil entry }
  114. argsbuf[count]:=nil;
  115. inc(count);
  116. { create the argv }
  117. move(argsbuf,argv,count shl 2);
  118. end;
  119. {$ENDIF}
  120. {***************************************************************************
  121. Real Getopts
  122. ***************************************************************************}
  123. Var
  124. NextChar,
  125. Nrargs,
  126. first_nonopt,
  127. last_nonopt : Longint;
  128. Ordering : Orderings;
  129. Procedure Exchange;
  130. var
  131. bottom,
  132. middle,
  133. top,i,len : longint;
  134. temp : pchar;
  135. begin
  136. bottom:=first_nonopt;
  137. middle:=last_nonopt;
  138. top:=optind;
  139. while (top>middle) and (middle>bottom) do
  140. begin
  141. if (top-middle>middle-bottom) then
  142. begin
  143. len:=middle-bottom;
  144. for i:=0 to len-1 do
  145. begin
  146. temp:=argv[bottom+i];
  147. argv[bottom+i]:=argv[top-(middle-bottom)+i];
  148. argv[top-(middle-bottom)+i]:=temp;
  149. end;
  150. top:=top-len;
  151. end
  152. else
  153. begin
  154. len:=top-middle;
  155. for i:=0 to len-1 do
  156. begin
  157. temp:=argv[bottom+i];
  158. argv[bottom+i]:=argv[middle+i];
  159. argv[middle+i]:=temp;
  160. end;
  161. bottom:=bottom+len;
  162. end;
  163. end;
  164. first_nonopt:=first_nonopt + optind-last_nonopt;
  165. last_nonopt:=optind;
  166. end; { exchange }
  167. procedure getopt_init (var opts : string);
  168. begin
  169. { Initialize some defaults. }
  170. Optarg:='';
  171. Optind:=1;
  172. First_nonopt:=1;
  173. Last_nonopt:=1;
  174. OptOpt:='?';
  175. Nextchar:=0;
  176. case opts[1] of
  177. '-' : begin
  178. ordering:=return_in_order;
  179. delete(opts,1,1);
  180. end;
  181. '+' : begin
  182. ordering:=require_order;
  183. delete(opts,1,1);
  184. end;
  185. else
  186. ordering:=permute;
  187. end;
  188. end;
  189. Function Internal_getopt (Var Optstring : string;LongOpts : POption;
  190. LongInd : pointer;Long_only : boolean ) : char;
  191. var
  192. temp,endopt,
  193. option_index : byte;
  194. indfound : integer;
  195. currentarg,
  196. optname : string;
  197. p,pfound : POption;
  198. exact,ambig : boolean;
  199. c : char;
  200. begin
  201. optarg:='';
  202. if optind=0 then
  203. getopt_init(optstring);
  204. { Check if We need the next argument. }
  205. if (optind<nrargs) then
  206. currentarg:=strpas(argv[optind])
  207. else
  208. currentarg:='';
  209. if (nextchar=0) then
  210. begin
  211. if ordering=permute then
  212. begin
  213. { If we processed options following non-options : exchange }
  214. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  215. exchange
  216. else
  217. if last_nonopt<>optind then
  218. first_nonopt:=optind;
  219. while (optind<nrargs) and (not(argv[optind][0] in OptSpecifier) or
  220. (length(strpas(argv[optind]))=1)) do
  221. inc(optind);
  222. last_nonopt:=optind;
  223. end;
  224. { Check for '--' argument }
  225. if optind<nrargs then
  226. currentarg:=strpas(argv[optind])
  227. else
  228. currentarg:='';
  229. if (optind<>nrargs) and (currentarg='--') then
  230. begin
  231. inc(optind);
  232. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  233. exchange
  234. else
  235. if first_nonopt=last_nonopt then
  236. first_nonopt:=optind;
  237. last_nonopt:=nrargs;
  238. optind:=nrargs;
  239. end;
  240. { Are we at the end of all arguments ? }
  241. if optind>=nrargs then
  242. begin
  243. if first_nonopt<>last_nonopt then
  244. optind:=first_nonopt;
  245. Internal_getopt:=EndOfOptions;
  246. exit;
  247. end;
  248. if optind<nrargs then
  249. currentarg:=strpas(argv[optind])
  250. else
  251. currentarg:='';
  252. { Are we at a non-option ? }
  253. if not(currentarg[1] in OptSpecifier) or (length(currentarg)=1) then
  254. begin
  255. if ordering=require_order then
  256. begin
  257. Internal_getopt:=EndOfOptions;
  258. exit;
  259. end
  260. else
  261. begin
  262. optarg:=strpas(argv[optind]);
  263. inc(optind);
  264. Internal_getopt:=#0;
  265. exit;
  266. end;
  267. end;
  268. { At this point we're at an option ...}
  269. nextchar:=2;
  270. if (longopts<>nil) and ((currentarg[2]='-') and
  271. (currentArg[1]='-')) then
  272. inc(nextchar);
  273. { So, now nextchar points at the first character of an option }
  274. end;
  275. { Check if we have a long option }
  276. if longopts<>nil then
  277. if length(currentarg)>1 then
  278. if ((currentarg[2]='-') and (currentArg[1]='-'))
  279. or
  280. ((not long_only) and (pos(currentarg[2],optstring)<>0)) then
  281. begin
  282. { Get option name }
  283. endopt:=pos('=',currentarg);
  284. if endopt=0 then
  285. endopt:=length(currentarg)+1;
  286. optname:=copy(currentarg,nextchar,endopt-nextchar);
  287. { Match partial or full }
  288. p:=longopts;
  289. pfound:=nil;
  290. exact:=false;
  291. ambig:=false;
  292. option_index:=0;
  293. indfound:=0;
  294. while (p^.name<>'') and (not exact) do
  295. begin
  296. if pos(optname,p^.name)<>0 then
  297. begin
  298. if length(optname)=length(p^.name) then
  299. begin
  300. exact:=true;
  301. pfound:=p;
  302. indfound:=option_index;
  303. end
  304. else
  305. if pfound=nil then
  306. begin
  307. indfound:=option_index;
  308. pfound:=p
  309. end
  310. else
  311. ambig:=true;
  312. end;
  313. inc(PByte(p),sizeof(toption)); //inc(pointer(p),sizeof(toption)); // for Delphi compatibility
  314. inc(option_index);
  315. end;
  316. if ambig and not exact then
  317. begin
  318. if opterr then
  319. writeln(argv[0],': option "',optname,'" is ambiguous');
  320. nextchar:=0;
  321. inc(optind);
  322. Internal_getopt:='?';
  323. exit;
  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. initialization
  464. { create argv if not running under FPC }
  465. {$ifndef FPC}
  466. setup_arguments;
  467. {$endif}
  468. { Needed to detect startup }
  469. Opterr:=true;
  470. Optind:=0;
  471. nrargs:=argc;
  472. end.