2
0

getopts.pp 13 KB

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