getopts.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1993,97 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. function GetCommandLine:pchar;
  48. begin
  49. GetCommandLine:=ptr(prefixseg,$81);
  50. end;
  51. function GetCommandFile:pchar;
  52. var
  53. p : pchar;
  54. begin
  55. p:=ptr(memw[prefixseg:$2c],0);
  56. repeat
  57. while p^<>#0 do
  58. inc(longint(p));
  59. { next char also #0 ? }
  60. inc(longint(p));
  61. if p^=#0 then
  62. begin
  63. inc(longint(p),3);
  64. GetCommandFile:=p;
  65. exit;
  66. end;
  67. until false;
  68. end;
  69. type
  70. ppchar = ^pchar;
  71. apchar = array[0..127] of pchar;
  72. var
  73. argc : longint;
  74. argv : apchar;
  75. procedure setup_arguments;
  76. var
  77. arglen,
  78. count : longint;
  79. argstart,
  80. cmdline : pchar;
  81. quote : set of char;
  82. argsbuf : array[0..127] of pchar;
  83. begin
  84. { create argv[0] which is the started filename }
  85. argstart:=GetCommandFile;
  86. arglen:=strlen(argstart)+1;
  87. getmem(argsbuf[0],arglen);
  88. move(argstart^,argsbuf[0]^,arglen);
  89. { create commandline }
  90. cmdline:=GetCommandLine;
  91. count:=1;
  92. repeat
  93. { skip leading spaces }
  94. while cmdline^ in [' ',#9,#13] do
  95. inc(longint(cmdline));
  96. case cmdline^ of
  97. #0 : break;
  98. '"' : begin
  99. quote:=['"'];
  100. inc(longint(cmdline));
  101. end;
  102. '''' : begin
  103. quote:=[''''];
  104. inc(longint(cmdline));
  105. end;
  106. else
  107. quote:=[' ',#9,#13];
  108. end;
  109. { scan until the end of the argument }
  110. argstart:=cmdline;
  111. while (cmdline^<>#0) and not(cmdline^ in quote) do
  112. inc(longint(cmdline));
  113. { reserve some memory }
  114. arglen:=cmdline-argstart;
  115. getmem(argsbuf[count],arglen+1);
  116. move(argstart^,argsbuf[count]^,arglen);
  117. argsbuf[count][arglen]:=#0;
  118. { skip quote }
  119. if cmdline^ in quote then
  120. inc(longint(cmdline));
  121. inc(count);
  122. until false;
  123. { create argc }
  124. argc:=count;
  125. { create an nil entry }
  126. argsbuf[count]:=nil;
  127. inc(count);
  128. { create the argv }
  129. move(argsbuf,argv,count shl 2);
  130. end;
  131. {$endif TP}
  132. {***************************************************************************
  133. Real Getopts
  134. ***************************************************************************}
  135. Var
  136. NextChar,
  137. Nrargs,
  138. first_nonopt,
  139. last_nonopt : Longint;
  140. Ordering : Orderings;
  141. Procedure Exchange;
  142. var
  143. bottom,
  144. middle,
  145. top,i,len : longint;
  146. temp : pchar;
  147. begin
  148. bottom:=first_nonopt;
  149. middle:=last_nonopt;
  150. top:=optind;
  151. while (top>middle) and (middle>bottom) do
  152. begin
  153. if (top-middle>middle-bottom) then
  154. begin
  155. len:=middle-bottom;
  156. for i:=1 to len-1 do
  157. begin
  158. temp:=argv[bottom+i];
  159. argv[bottom+i]:=argv[top-(middle-bottom)+i];
  160. argv[top-(middle-bottom)+i]:=temp;
  161. end;
  162. top:=top-len;
  163. end
  164. else
  165. begin
  166. len:=top-middle;
  167. for i:=0 to len-1 do
  168. begin
  169. temp:=argv[bottom+i];
  170. argv[bottom+i]:=argv[middle+i];
  171. argv[middle+i]:=temp;
  172. end;
  173. bottom:=bottom+len;
  174. end;
  175. end;
  176. first_nonopt:=first_nonopt + optind-last_nonopt;
  177. last_nonopt:=optind;
  178. end; { exchange }
  179. procedure getopt_init (var opts : string);
  180. begin
  181. { Initialize some defaults. }
  182. Optarg:='';
  183. Optind:=1;
  184. First_nonopt:=1;
  185. Last_nonopt:=1;
  186. OptOpt:='?';
  187. Nextchar:=0;
  188. case opts[1] of
  189. '-' : begin
  190. ordering:=return_in_order;
  191. delete(opts,1,1);
  192. end;
  193. '+' : begin
  194. ordering:=require_order;
  195. delete(opts,1,1);
  196. end;
  197. else
  198. ordering:=permute;
  199. end;
  200. end;
  201. Function Internal_getopt (Var Optstring : string;LongOpts : POption;
  202. LongInd : pointer;Long_only : boolean ) : char;
  203. type
  204. pinteger=^integer;
  205. var
  206. temp,endopt,
  207. option_index : byte;
  208. indfound : integer;
  209. currentarg,
  210. optname : string;
  211. p,pfound : POption;
  212. exact,ambig : boolean;
  213. c : char;
  214. begin
  215. optarg:='';
  216. if optind=0 then
  217. getopt_init(optstring);
  218. { Check if We need the next argument. }
  219. if (optind<nrargs) then
  220. currentarg:=strpas(argv[optind])
  221. else
  222. currentarg:='';
  223. if (nextchar=0) then
  224. begin
  225. if ordering=permute then
  226. begin
  227. { If we processed options following non-options : exchange }
  228. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  229. exchange
  230. else
  231. if last_nonopt<>optind then
  232. first_nonopt:=optind;
  233. while (optind<nrargs) and (not(argv[optind][0] in OptSpecifier) or
  234. (length(strpas(argv[optind]))=1)) do
  235. inc(optind);
  236. last_nonopt:=optind;
  237. end;
  238. { Check for '--' argument }
  239. if optind<nrargs then
  240. currentarg:=strpas(argv[optind])
  241. else
  242. currentarg:='';
  243. if (optind<>nrargs) and (currentarg='--') then
  244. begin
  245. inc(optind);
  246. if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  247. exchange
  248. else
  249. if first_nonopt=last_nonopt then
  250. first_nonopt:=optind;
  251. last_nonopt:=nrargs;
  252. optind:=nrargs;
  253. end;
  254. { Are we at the end of all arguments ? }
  255. if optind>=nrargs then
  256. begin
  257. if first_nonopt<>last_nonopt then
  258. optind:=first_nonopt;
  259. Internal_getopt:=EndOfOptions;
  260. exit;
  261. end;
  262. if optind<nrargs then
  263. currentarg:=strpas(argv[optind])
  264. else
  265. currentarg:='';
  266. { Are we at a non-option ? }
  267. if not(currentarg[1] in OptSpecifier) or (length(currentarg)=1) then
  268. begin
  269. if ordering=require_order then
  270. begin
  271. Internal_getopt:=EndOfOptions;
  272. exit;
  273. end
  274. else
  275. begin
  276. optarg:=strpas(argv[optind]);
  277. inc(optind);
  278. Internal_getopt:=#1;
  279. exit;
  280. end;
  281. end;
  282. { At this point we're at an option ...}
  283. nextchar:=2;
  284. if (longopts<>nil) and (currentarg[2]='-') then
  285. inc(nextchar);
  286. { So, now nextchar points at the first character of an option }
  287. end;
  288. { Check if we have a long option }
  289. if longopts<>nil then
  290. if length(currentarg)>1 then
  291. if (currentarg[2]='-') or
  292. ((not long_only) and (pos(currentarg[2],optstring)<>0)) then
  293. begin
  294. { Get option name }
  295. endopt:=pos('=',currentarg);
  296. if endopt=0 then
  297. endopt:=length(currentarg)+1;
  298. optname:=copy(currentarg,nextchar,endopt-nextchar);
  299. { Match partial or full }
  300. p:=longopts;
  301. pfound:=nil;
  302. exact:=false;
  303. ambig:=false;
  304. option_index:=0;
  305. indfound:=0;
  306. while (p^.name<>'') and (not exact) do
  307. begin
  308. if pos(optname,p^.name)<>0 then
  309. begin
  310. if length(optname)=length(p^.name) then
  311. begin
  312. exact:=true;
  313. pfound:=p;
  314. indfound:=option_index;
  315. end
  316. else
  317. if pfound=nil then
  318. begin
  319. indfound:=option_index;
  320. pfound:=p
  321. end
  322. else
  323. ambig:=true;
  324. end;
  325. inc(longint(p),sizeof(toption));
  326. inc(option_index);
  327. end;
  328. if ambig and not exact then
  329. begin
  330. if opterr then
  331. writeln(argv[0],': option "',optname,'" is ambiguous');
  332. nextchar:=0;
  333. inc(optind);
  334. Internal_getopt:='?';
  335. end;
  336. if pfound<>nil then
  337. begin
  338. inc(optind);
  339. if endopt<=length(currentarg) then
  340. begin
  341. if pfound^.has_arg>0 then
  342. optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt)
  343. else
  344. begin
  345. if opterr then
  346. if currentarg[2]='-' then
  347. writeln(argv[0],': option "--',pfound^.name,'" doesn''t allow an argument')
  348. else
  349. writeln(argv[0],': option "',currentarg[1],pfound^.name,'" doesn''t allow an argument');
  350. nextchar:=0;
  351. internal_getopt:='?';
  352. exit;
  353. end;
  354. end
  355. else { argument in next paramstr... }
  356. begin
  357. if pfound^.has_arg=1 then
  358. begin
  359. if optind<nrargs then
  360. begin
  361. optarg:=strpas(argv[optind]);
  362. inc(optind);
  363. end { required argument }
  364. else
  365. begin { no req argument}
  366. if opterr then
  367. writeln(argv[0],': option ',pfound^.name,' requires an argument');
  368. nextchar:=0;
  369. if optstring[1]=':' then
  370. Internal_getopt:=':'
  371. else
  372. Internal_getopt:='?';
  373. exit;
  374. end;
  375. end;
  376. end; { argument in next parameter end;}
  377. nextchar:=0;
  378. if longind<>nil then
  379. pinteger(longind)^:=indfound+1;
  380. if pfound^.flag<>nil then
  381. begin
  382. pfound^.flag^:=pfound^.value;
  383. internal_getopt:=#0;
  384. exit;
  385. end;
  386. internal_getopt:=pfound^.value;
  387. exit;
  388. end; { pfound<>nil }
  389. { We didn't find it as an option }
  390. if (not long_only) or
  391. ((currentarg[2]='-') or (pos(CurrentArg[nextchar],optstring)=0)) then
  392. begin
  393. if opterr then
  394. if currentarg[2]='-' then
  395. writeln(argv[0],' unrecognized option "--',optname,'"')
  396. else
  397. writeln(argv[0],' unrecognized option "',currentarg[1],optname,'"');
  398. nextchar:=0;
  399. inc(optind);
  400. Internal_getopt:='?';
  401. exit;
  402. end;
  403. end; { Of long options.}
  404. { We check for a short option. }
  405. temp:=pos(currentarg[nextchar],optstring);
  406. c:=currentarg[nextchar];
  407. inc(nextchar);
  408. if nextchar>length(currentarg) then
  409. begin
  410. inc(optind);
  411. nextchar:=0;
  412. end;
  413. if (temp=0) or (c=':') then
  414. begin
  415. if opterr then
  416. writeln(argv[0],': illegal option -- ',c);
  417. optopt:=c;
  418. internal_getopt:='?';
  419. exit;
  420. end;
  421. Internal_getopt:=optstring[temp];
  422. if optstring[temp+1]=':' then
  423. if currentarg[temp+2]=':' then
  424. begin { optional argument }
  425. optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  426. nextchar:=0;
  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.4 1998-10-29 23:06:55 peter
  475. + OptSpecifier
  476. Revision 1.3 1998/06/18 10:49:04 peter
  477. * some fixes with indexes
  478. * bp7 compatible
  479. Revision 1.2 1998/05/21 19:30:57 peter
  480. * objects compiles for linux
  481. + assign(pchar), assign(char), rename(pchar), rename(char)
  482. * fixed read_text_as_array
  483. + read_text_as_pchar which was not yet in the rtl
  484. Revision 1.1 1998/05/12 10:42:45 peter
  485. * moved getopts to inc/, all supported OS's need argc,argv exported
  486. + strpas, strlen are now exported in the systemunit
  487. * removed logs
  488. * removed $ifdef ver_above
  489. }