getopts.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by Michael Van Canneyt,
  5. member of the Free Pascal development team.
  6. Getopt implementation for Free Pascal, modeled after GNU getopt
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. unit getopts;
  14. Interface
  15. Const
  16. No_Argument = 0;
  17. Required_Argument = 1;
  18. Optional_Argument = 2;
  19. EndOfOptions = #255;
  20. Type
  21. POption = ^TOption;
  22. TOption = Record
  23. Name : String;
  24. Has_arg : Integer;
  25. Flag : PChar;
  26. Value : Char;
  27. end;
  28. Orderings = (require_order,permute,return_in_order);
  29. Const
  30. OptSpecifier : set of char=['-'];
  31. Var
  32. OptArg : String;
  33. OptInd : Longint;
  34. OptErr : Boolean;
  35. OptOpt : Char;
  36. Function GetOpt (ShortOpts : String) : char;
  37. Function GetLongOpts (ShortOpts : String;LongOpts : POption;var Longind : Longint) : char;
  38. Implementation
  39. {$ifdef TP}
  40. uses
  41. strings;
  42. {$endif}
  43. {***************************************************************************
  44. Create an ArgV
  45. ***************************************************************************}
  46. {$ifdef TP}
  47. type
  48. ppchar = ^pchar;
  49. apchar = array[0..127] of pchar;
  50. var
  51. argc : longint;
  52. argv : apchar;
  53. procedure setup_arguments;
  54. var
  55. arglen,
  56. count : longint;
  57. argstart,
  58. cmdline : pchar;
  59. quote : set of char;
  60. argsbuf : array[0..127] of pchar;
  61. s : string;
  62. i : integer;
  63. begin
  64. { create argv[0] which is the started filename }
  65. s:=paramstr(0);
  66. arglen:=length(s);
  67. getmem(argsbuf[0],arglen + 1);
  68. strpcopy(argsbuf[0],s);
  69. { create commandline }
  70. s:='';
  71. for i:=1 to paramcount do
  72. begin
  73. s:=s+paramstr(i)+' ';
  74. end;
  75. s:=s+#0;
  76. cmdline:=@s[1];
  77. count:=1;
  78. repeat
  79. { skip leading spaces }
  80. while cmdline^ in [' ',#9,#13] do
  81. inc(longint(cmdline));
  82. case cmdline^ of
  83. #0 : break;
  84. '"' : begin
  85. quote:=['"'];
  86. inc(longint(cmdline));
  87. end;
  88. '''' : begin
  89. quote:=[''''];
  90. inc(longint(cmdline));
  91. end;
  92. else
  93. quote:=[' ',#9,#13];
  94. end;
  95. { scan until the end of the argument }
  96. argstart:=cmdline;
  97. while (cmdline^<>#0) and not(cmdline^ in quote) do
  98. inc(longint(cmdline));
  99. { reserve some memory }
  100. arglen:=cmdline-argstart;
  101. getmem(argsbuf[count],arglen+1);
  102. move(argstart^,argsbuf[count]^,arglen);
  103. argsbuf[count][arglen]:=#0;
  104. { skip quote }
  105. if cmdline^ in quote then
  106. inc(longint(cmdline));
  107. inc(count);
  108. until false;
  109. { create argc }
  110. argc:=count;
  111. { create an nil entry }
  112. argsbuf[count]:=nil;
  113. inc(count);
  114. { create the argv }
  115. move(argsbuf,argv,count shl 2);
  116. end;
  117. {$endif TP}
  118. {***************************************************************************
  119. Real Getopts
  120. ***************************************************************************}
  121. Var
  122. NextChar,
  123. Nrargs,
  124. first_nonopt,
  125. last_nonopt : Longint;
  126. Ordering : Orderings;
  127. Procedure Exchange;
  128. var
  129. bottom,
  130. middle,
  131. top,i,len : longint;
  132. temp : pchar;
  133. begin
  134. bottom:=first_nonopt;
  135. middle:=last_nonopt;
  136. top:=optind;
  137. while (top>middle) and (middle>bottom) do
  138. begin
  139. if (top-middle>middle-bottom) then
  140. begin
  141. len:=middle-bottom;
  142. for i:=1 to len-1 do
  143. begin
  144. temp:=argv[bottom+i];
  145. argv[bottom+i]:=argv[top-(middle-bottom)+i];
  146. argv[top-(middle-bottom)+i]:=temp;
  147. end;
  148. top:=top-len;
  149. end
  150. else
  151. begin
  152. len:=top-middle;
  153. for i:=0 to len-1 do
  154. begin
  155. temp:=argv[bottom+i];
  156. argv[bottom+i]:=argv[middle+i];
  157. argv[middle+i]:=temp;
  158. end;
  159. bottom:=bottom+len;
  160. end;
  161. end;
  162. first_nonopt:=first_nonopt + optind-last_nonopt;
  163. last_nonopt:=optind;
  164. end; { exchange }
  165. procedure getopt_init (var opts : string);
  166. begin
  167. { Initialize some defaults. }
  168. Optarg:='';
  169. Optind:=1;
  170. First_nonopt:=1;
  171. Last_nonopt:=1;
  172. OptOpt:='?';
  173. Nextchar:=0;
  174. case opts[1] of
  175. '-' : begin
  176. ordering:=return_in_order;
  177. delete(opts,1,1);
  178. end;
  179. '+' : begin
  180. ordering:=require_order;
  181. delete(opts,1,1);
  182. end;
  183. else
  184. ordering:=permute;
  185. end;
  186. end;
  187. Function Internal_getopt (Var Optstring : string;LongOpts : POption;
  188. LongInd : pointer;Long_only : boolean ) : char;
  189. type
  190. pinteger=^integer;
  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:=#1;
  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(pointer(p),sizeof(toption));
  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. end;
  324. if pfound<>nil then
  325. begin
  326. inc(optind);
  327. if endopt<=length(currentarg) then
  328. begin
  329. if pfound^.has_arg>0 then
  330. optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt)
  331. else
  332. begin
  333. if opterr then
  334. if currentarg[2]='-' then
  335. writeln(argv[0],': option "--',pfound^.name,'" doesn''t allow an argument')
  336. else
  337. writeln(argv[0],': option "',currentarg[1],pfound^.name,'" doesn''t allow an argument');
  338. nextchar:=0;
  339. internal_getopt:='?';
  340. exit;
  341. end;
  342. end
  343. else { argument in next paramstr... }
  344. begin
  345. if pfound^.has_arg=1 then
  346. begin
  347. if optind<nrargs then
  348. begin
  349. optarg:=strpas(argv[optind]);
  350. inc(optind);
  351. end { required argument }
  352. else
  353. begin { no req argument}
  354. if opterr then
  355. writeln(argv[0],': option ',pfound^.name,' requires an argument');
  356. nextchar:=0;
  357. if optstring[1]=':' then
  358. Internal_getopt:=':'
  359. else
  360. Internal_getopt:='?';
  361. exit;
  362. end;
  363. end;
  364. end; { argument in next parameter end;}
  365. nextchar:=0;
  366. if longind<>nil then
  367. pinteger(longind)^:=indfound+1;
  368. if pfound^.flag<>nil then
  369. begin
  370. pfound^.flag^:=pfound^.value;
  371. internal_getopt:=#0;
  372. exit;
  373. end;
  374. internal_getopt:=pfound^.value;
  375. exit;
  376. end; { pfound<>nil }
  377. { We didn't find it as an option }
  378. if (not long_only) or
  379. ((currentarg[2]='-') or (pos(CurrentArg[nextchar],optstring)=0)) then
  380. begin
  381. if opterr then
  382. if currentarg[2]='-' then
  383. writeln(argv[0],' unrecognized option "--',optname,'"')
  384. else
  385. writeln(argv[0],' unrecognized option "',currentarg[1],optname,'"');
  386. nextchar:=0;
  387. inc(optind);
  388. Internal_getopt:='?';
  389. exit;
  390. end;
  391. end; { Of long options.}
  392. { We check for a short option. }
  393. temp:=pos(currentarg[nextchar],optstring);
  394. c:=currentarg[nextchar];
  395. inc(nextchar);
  396. if nextchar>length(currentarg) then
  397. begin
  398. inc(optind);
  399. nextchar:=0;
  400. end;
  401. if (temp=0) or (c=':') then
  402. begin
  403. if opterr then
  404. writeln(argv[0],': illegal option -- ',c);
  405. optopt:=c;
  406. internal_getopt:='?';
  407. exit;
  408. end;
  409. Internal_getopt:=optstring[temp];
  410. if optstring[temp+1]=':' then
  411. if optstring[temp+2]=':' then
  412. begin { optional argument }
  413. if nextchar>0 then
  414. begin
  415. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  416. inc(optind);
  417. nextchar:=0;
  418. end else if (optind<>nrargs) then
  419. begin
  420. optarg:=strpas(argv[optind]);
  421. if optarg[1]='-' then
  422. optarg:=''
  423. else
  424. inc(optind);
  425. nextchar:=0;
  426. end;
  427. end
  428. else
  429. begin { required argument }
  430. if nextchar>0 then
  431. begin
  432. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  433. inc(optind);
  434. end
  435. else
  436. if (optind=nrargs) then
  437. begin
  438. if opterr then
  439. writeln (argv[0],': option requires an argument -- ',optstring[temp]);
  440. optopt:=optstring[temp];
  441. if optstring[1]=':' then
  442. Internal_getopt:=':'
  443. else
  444. Internal_Getopt:='?';
  445. end
  446. else
  447. begin
  448. optarg:=strpas(argv[optind]);
  449. inc(optind)
  450. end;
  451. nextchar:=0;
  452. end; { End of required argument}
  453. end; { End of internal getopt...}
  454. Function GetOpt(ShortOpts : String) : char;
  455. begin
  456. getopt:=internal_getopt(shortopts,nil,nil,false);
  457. end;
  458. Function GetLongOpts(ShortOpts : String;LongOpts : POption;var Longind : Longint) : char;
  459. begin
  460. getlongopts:=internal_getopt(shortopts,longopts,@longind,true);
  461. end;
  462. begin
  463. { create argv if running under TP }
  464. {$ifdef TP}
  465. setup_arguments;
  466. {$endif}
  467. { Needed to detect startup }
  468. Opterr:=true;
  469. Optind:=0;
  470. nrargs:=argc;
  471. end.
  472. {
  473. $Log$
  474. Revision 1.6 2004-02-22 16:48:39 florian
  475. * several 64 bit issues fixed
  476. Revision 1.5 2002/11/20 14:31:22 jonas
  477. * applied fix from Maxim Artemev ([email protected])
  478. Revision 1.4 2002/03/28 20:54:25 carl
  479. * merged fixes from version 1.0.x branch
  480. }