clapp-pm 1.0.9__py3-none-any.whl → 1.0.11__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. backup_current/build_index.py +132 -0
  2. backup_current/check_env.py +133 -0
  3. backup_current/clapp_core.py +61 -0
  4. backup_current/clean_command.py +214 -0
  5. backup_current/cli_commands.py +404 -0
  6. backup_current/dependency_resolver.py +272 -0
  7. backup_current/doctor_command.py +239 -0
  8. backup_current/info_command.py +194 -0
  9. backup_current/install_command.py +236 -0
  10. backup_current/installer.py +323 -0
  11. backup_current/list_command.py +262 -0
  12. backup_current/main.py +294 -0
  13. backup_current/manifest_schema.py +84 -0
  14. backup_current/manifest_validator.py +245 -0
  15. backup_current/package_registry.py +127 -0
  16. backup_current/package_runner.py +85 -0
  17. backup_current/post_install_hint.py +144 -0
  18. backup_current/publish_command.py +253 -0
  19. backup_current/remote_registry.py +285 -0
  20. backup_current/setup.py +160 -0
  21. backup_current/system_test.py +477 -0
  22. backup_current/uninstall_command.py +215 -0
  23. backup_current/validate_command.py +225 -0
  24. backup_current/version.py +8 -0
  25. backup_current/version_command.py +145 -0
  26. backup_current/where_command.py +207 -0
  27. check_env.py +1 -8
  28. clapp-packages-repo/packages/hello-python/main.py +0 -49
  29. clapp-packages-repo/packages/hello-python/manifest.json +0 -8
  30. {clapp_pm-1.0.9.data → clapp_pm-1.0.11.data}/data/version.json +1 -1
  31. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.dist-info}/METADATA +1 -1
  32. clapp_pm-1.0.11.dist-info/RECORD +71 -0
  33. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.dist-info}/top_level.txt +2 -0
  34. doctor_command.py +0 -1
  35. install_command.py +3 -0
  36. version.py +8 -0
  37. clapp_pm-1.0.9.dist-info/RECORD +0 -44
  38. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.dist-info}/WHEEL +0 -0
  39. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.dist-info}/entry_points.txt +0 -0
  40. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,323 @@
1
+ import os
2
+ import zipfile
3
+ import urllib.request
4
+ import tempfile
5
+ import shutil
6
+ import json
7
+ from manifest_validator import validate_manifest_verbose
8
+ from package_registry import app_exists
9
+
10
+ def find_app_folder(extract_path, app_name):
11
+ """
12
+ Zip çıkarıldıktan sonra, extract_path altında **/packages/{app_name} klasörünü bulur.
13
+ """
14
+ for root, dirs, files in os.walk(extract_path):
15
+ if os.path.basename(root) == app_name and os.path.basename(os.path.dirname(root)) == "packages":
16
+ return root
17
+ return None
18
+
19
+
20
+ def install_package(source, force=False):
21
+ """
22
+ Bir .clapp paketini zip dosyasından veya URL'den yükler.
23
+ """
24
+ temp_dir = None
25
+ try:
26
+ temp_dir = tempfile.mkdtemp()
27
+ if source.startswith(('http://', 'https://')):
28
+ zip_path = download_package(source, temp_dir)
29
+ if not zip_path:
30
+ return False, "Paket indirilemedi"
31
+ else:
32
+ if not os.path.exists(source):
33
+ return False, f"Dosya bulunamadı: {source}"
34
+ zip_path = source
35
+ extract_path = os.path.join(temp_dir, "extracted")
36
+ success, message = extract_package(zip_path, extract_path)
37
+ if not success:
38
+ return False, message
39
+
40
+ # --- YENİ: Doğru app klasörünü bul ---
41
+ # Önce manifesti bulmak için tüm app klasörlerini tara
42
+ app_folder = None
43
+ manifest = None
44
+ manifest_path = None
45
+ # Tüm packages altındaki app klasörlerini bul
46
+ for root, dirs, files in os.walk(extract_path):
47
+ if "manifest.json" in files:
48
+ with open(os.path.join(root, "manifest.json"), 'r', encoding='utf-8') as f:
49
+ try:
50
+ m = json.load(f)
51
+ if 'name' in m:
52
+ app_folder = root
53
+ manifest = m
54
+ manifest_path = os.path.join(root, "manifest.json")
55
+ break
56
+ except Exception:
57
+ continue
58
+ if not app_folder or not manifest:
59
+ return False, "Uygulama klasörü bulunamadı: manifest.json"
60
+ app_name = manifest['name']
61
+
62
+ # --- YENİ: Sadece packages/{app_name} klasörünü bul ve kopyala ---
63
+ app_real_folder = find_app_folder(extract_path, app_name)
64
+ if not app_real_folder:
65
+ return False, f"Uygulama klasörü bulunamadı: packages/{app_name}"
66
+
67
+ # Manifesti doğrula
68
+ is_valid, errors = validate_manifest_file(os.path.join(app_real_folder, "manifest.json"))
69
+ if not is_valid:
70
+ error_msg = "Manifest doğrulama hatası:\n" + "\n".join(errors)
71
+ return False, error_msg
72
+
73
+ # Giriş dosyasının varlığını kontrol et
74
+ entry_file = manifest['entry']
75
+ entry_path = os.path.join(app_real_folder, entry_file)
76
+ if not os.path.exists(entry_path):
77
+ return False, f"Giriş dosyası bulunamadı: {entry_file}"
78
+
79
+ # Hedef dizini oluştur
80
+ target_dir = os.path.join("apps", app_name)
81
+ if os.path.exists(target_dir):
82
+ shutil.rmtree(target_dir)
83
+ shutil.copytree(app_real_folder, target_dir)
84
+ return True, f"✅ '{app_name}' başarıyla yüklendi!"
85
+ except Exception as e:
86
+ return False, f"Yükleme hatası: {e}"
87
+ finally:
88
+ if temp_dir and os.path.exists(temp_dir):
89
+ shutil.rmtree(temp_dir)
90
+
91
+ def download_package(url, temp_dir):
92
+ """
93
+ Paketi URL'den indirir.
94
+
95
+ Args:
96
+ url (str): İndirilecek URL
97
+ temp_dir (str): Geçici dizin
98
+
99
+ Returns:
100
+ str or None: İndirilen dosyanın yolu veya None
101
+ """
102
+ try:
103
+ print(f"Paket indiriliyor: {url}")
104
+
105
+ # Dosya adını URL'den çıkar
106
+ filename = os.path.basename(url)
107
+ if not filename.endswith('.zip'):
108
+ filename += '.zip'
109
+
110
+ zip_path = os.path.join(temp_dir, filename)
111
+
112
+ # Dosyayı indir
113
+ urllib.request.urlretrieve(url, zip_path)
114
+
115
+ print(f"✅ İndirme tamamlandı: {filename}")
116
+ return zip_path
117
+
118
+ except Exception as e:
119
+ print(f"❌ İndirme hatası: {e}")
120
+ return None
121
+
122
+ def extract_package(zip_path, extract_path):
123
+ """
124
+ Zip dosyasını çıkarır.
125
+
126
+ Args:
127
+ zip_path (str): Zip dosyasının yolu
128
+ extract_path (str): Çıkarılacak dizin
129
+
130
+ Returns:
131
+ tuple: (success: bool, message: str)
132
+ """
133
+ try:
134
+ # Çıkarma dizinini oluştur
135
+ os.makedirs(extract_path, exist_ok=True)
136
+
137
+ # Zip dosyasını aç ve çıkar
138
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
139
+ zip_ref.extractall(extract_path)
140
+
141
+ print(f"✅ Paket çıkarıldı: {extract_path}")
142
+ return True, "Paket başarıyla çıkarıldı"
143
+
144
+ except zipfile.BadZipFile:
145
+ return False, "Geçersiz zip dosyası"
146
+ except Exception as e:
147
+ return False, f"Çıkarma hatası: {e}"
148
+
149
+ def validate_manifest_file(manifest_path):
150
+ """
151
+ Manifest dosyasını doğrular.
152
+
153
+ Args:
154
+ manifest_path (str): Manifest dosyasının yolu
155
+
156
+ Returns:
157
+ tuple: (is_valid: bool, errors: list)
158
+ """
159
+ if not os.path.exists(manifest_path):
160
+ return False, ["Manifest dosyası bulunamadı"]
161
+
162
+ try:
163
+ with open(manifest_path, 'r', encoding='utf-8') as f:
164
+ manifest = json.load(f)
165
+
166
+ return validate_manifest_verbose(manifest)
167
+
168
+ except json.JSONDecodeError as e:
169
+ return False, [f"JSON formatı hatalı: {e}"]
170
+ except Exception as e:
171
+ return False, [f"Dosya okuma hatası: {e}"]
172
+
173
+ def create_package_from_directory(source_dir, output_path=None):
174
+ """
175
+ Dizinden .clapp paketi oluşturur.
176
+
177
+ Args:
178
+ source_dir (str): Kaynak dizin
179
+ output_path (str): Çıktı dosyası yolu (opsiyonel)
180
+
181
+ Returns:
182
+ tuple: (success: bool, message: str, output_file: str)
183
+ """
184
+ try:
185
+ # Manifest dosyasını kontrol et
186
+ manifest_path = os.path.join(source_dir, "manifest.json")
187
+ is_valid, errors = validate_manifest_file(manifest_path)
188
+
189
+ if not is_valid:
190
+ error_msg = "Manifest doğrulama hatası:\n" + "\n".join(errors)
191
+ return False, error_msg, None
192
+
193
+ # Manifest'i yükle
194
+ with open(manifest_path, 'r', encoding='utf-8') as f:
195
+ manifest = json.load(f)
196
+
197
+ app_name = manifest['name']
198
+
199
+ # Çıktı dosyası yolunu belirle
200
+ if not output_path:
201
+ output_path = f"{app_name}.clapp.zip"
202
+
203
+ # Zip dosyasını oluştur
204
+ with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zip_ref:
205
+ for root, dirs, files in os.walk(source_dir):
206
+ for file in files:
207
+ file_path = os.path.join(root, file)
208
+ arc_path = os.path.relpath(file_path, source_dir)
209
+ zip_ref.write(file_path, arc_path)
210
+
211
+ return True, f"✅ Paket oluşturuldu: {output_path}", output_path
212
+
213
+ except Exception as e:
214
+ return False, f"Paket oluşturma hatası: {e}", None
215
+
216
+ def install_from_directory(source_dir, force=False):
217
+ """
218
+ Dizinden doğrudan uygulama yükler.
219
+
220
+ Args:
221
+ source_dir (str): Kaynak dizin
222
+ force (bool): Mevcut uygulamanın üzerine yazılmasına izin ver
223
+
224
+ Returns:
225
+ tuple: (success: bool, message: str)
226
+ """
227
+ try:
228
+ # Manifest dosyasını kontrol et
229
+ manifest_path = os.path.join(source_dir, "manifest.json")
230
+ is_valid, errors = validate_manifest_file(manifest_path)
231
+
232
+ if not is_valid:
233
+ error_msg = "Manifest doğrulama hatası:\n" + "\n".join(errors)
234
+ return False, error_msg
235
+
236
+ # Manifest'i yükle
237
+ with open(manifest_path, 'r', encoding='utf-8') as f:
238
+ manifest = json.load(f)
239
+
240
+ app_name = manifest['name']
241
+
242
+ # Uygulama zaten var mı kontrol et
243
+ if app_exists(app_name) and not force:
244
+ return False, f"Uygulama '{app_name}' zaten yüklü. --force kullanarak üzerine yazabilirsiniz."
245
+
246
+ # Giriş dosyasının varlığını kontrol et
247
+ entry_file = manifest['entry']
248
+ entry_path = os.path.join(source_dir, entry_file)
249
+ if not os.path.exists(entry_path):
250
+ return False, f"Giriş dosyası bulunamadı: {entry_file}"
251
+
252
+ # Hedef dizini oluştur
253
+ target_dir = os.path.join("apps", app_name)
254
+
255
+ # Mevcut dizini sil (eğer varsa)
256
+ if os.path.exists(target_dir):
257
+ shutil.rmtree(target_dir)
258
+
259
+ # Dosyaları kopyala
260
+ shutil.copytree(source_dir, target_dir)
261
+
262
+ return True, f"✅ '{app_name}' başarıyla yüklendi!"
263
+
264
+ except Exception as e:
265
+ return False, f"Yükleme hatası: {e}"
266
+
267
+ def uninstall_package(app_name):
268
+ """
269
+ Uygulamayı kaldırır.
270
+
271
+ Args:
272
+ app_name (str): Kaldırılacak uygulama adı
273
+
274
+ Returns:
275
+ tuple: (success: bool, message: str)
276
+ """
277
+ try:
278
+ if not app_exists(app_name):
279
+ return False, f"Uygulama '{app_name}' bulunamadı"
280
+
281
+ # Uygulama dizinini sil
282
+ app_dir = os.path.join("apps", app_name)
283
+ shutil.rmtree(app_dir)
284
+
285
+ return True, f"✅ '{app_name}' başarıyla kaldırıldı!"
286
+
287
+ except Exception as e:
288
+ return False, f"Kaldırma hatası: {e}"
289
+
290
+ def list_installable_files(directory="."):
291
+ """
292
+ Dizindeki yüklenebilir .clapp dosyalarını listeler.
293
+
294
+ Args:
295
+ directory (str): Aranacak dizin
296
+
297
+ Returns:
298
+ list: .clapp dosyalarının listesi
299
+ """
300
+ clapp_files = []
301
+
302
+ for file in os.listdir(directory):
303
+ if file.endswith('.clapp.zip') or file.endswith('.zip'):
304
+ file_path = os.path.join(directory, file)
305
+ clapp_files.append(file_path)
306
+
307
+ return clapp_files
308
+
309
+ if __name__ == "__main__":
310
+ # Test için örnek kullanım
311
+ print("clapp Installer Test")
312
+ print("=" * 30)
313
+
314
+ # Mevcut dizindeki .clapp dosyalarını listele
315
+ installable = list_installable_files()
316
+ if installable:
317
+ print("Yüklenebilir dosyalar:")
318
+ for file in installable:
319
+ print(f" - {file}")
320
+ else:
321
+ print("Yüklenebilir dosya bulunamadı")
322
+
323
+ print("\nTest tamamlandı.")
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ list_command.py - clapp List Command
4
+
5
+ Bu modül 'clapp list' komutunu uygular.
6
+ Kurulu uygulamaları farklı formatlarda listeler.
7
+ """
8
+
9
+ import os
10
+ import json
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import List, Dict, Any, Optional
14
+
15
+ def get_apps_directory() -> str:
16
+ """Uygulamaların kurulu olduğu dizini döndürür"""
17
+ # Kullanıcının home dizininde .clapp klasörü
18
+ home_dir = Path.home()
19
+ clapp_dir = home_dir / ".clapp"
20
+ apps_dir = clapp_dir / "apps"
21
+
22
+ return str(apps_dir)
23
+
24
+ def load_app_manifest(app_path: str) -> Optional[Dict[str, Any]]:
25
+ """Uygulama manifest'ini yükler"""
26
+ manifest_path = os.path.join(app_path, "manifest.json")
27
+
28
+ if not os.path.exists(manifest_path):
29
+ return None
30
+
31
+ try:
32
+ with open(manifest_path, 'r', encoding='utf-8') as f:
33
+ return json.load(f)
34
+ except Exception:
35
+ return None
36
+
37
+ def get_installed_apps_with_info() -> List[Dict[str, Any]]:
38
+ """Kurulu uygulamaların detaylı bilgilerini döndürür"""
39
+ apps_dir = get_apps_directory()
40
+ apps = []
41
+
42
+ if not os.path.exists(apps_dir):
43
+ return apps
44
+
45
+ for item in os.listdir(apps_dir):
46
+ item_path = os.path.join(apps_dir, item)
47
+
48
+ if not os.path.isdir(item_path) or item.startswith('.'):
49
+ continue
50
+
51
+ manifest = load_app_manifest(item_path)
52
+
53
+ if manifest:
54
+ app_info = {
55
+ 'name': manifest.get('name', item),
56
+ 'version': manifest.get('version', '?'),
57
+ 'language': manifest.get('language', '?'),
58
+ 'description': manifest.get('description', 'Açıklama yok'),
59
+ 'entry': manifest.get('entry', '?'),
60
+ 'dependencies': manifest.get('dependencies', []),
61
+ 'folder': item,
62
+ 'path': item_path
63
+ }
64
+ else:
65
+ app_info = {
66
+ 'name': item,
67
+ 'version': '?',
68
+ 'language': '?',
69
+ 'description': 'Manifest bulunamadı',
70
+ 'entry': '?',
71
+ 'dependencies': [],
72
+ 'folder': item,
73
+ 'path': item_path
74
+ }
75
+
76
+ apps.append(app_info)
77
+
78
+ return apps
79
+
80
+ def format_table_output(apps: List[Dict[str, Any]]) -> str:
81
+ """Uygulamaları tablo formatında formatlar"""
82
+ if not apps:
83
+ return "📭 Hiç uygulama kurulu değil."
84
+
85
+ # Başlık
86
+ output = []
87
+ output.append(f"📦 Kurulu Uygulamalar ({len(apps)})")
88
+ output.append("=" * 80)
89
+
90
+ # Sütun başlıkları
91
+ output.append(f"{'Ad':<20} {'Sürüm':<10} {'Dil':<10} {'Açıklama':<30}")
92
+ output.append("-" * 80)
93
+
94
+ # Uygulamalar
95
+ for app in sorted(apps, key=lambda x: x['name'].lower()):
96
+ name = app['name'][:19]
97
+ version = app['version'][:9]
98
+ language = app['language'][:9]
99
+ description = app['description'][:29]
100
+
101
+ output.append(f"{name:<20} {version:<10} {language:<10} {description:<30}")
102
+
103
+ return "\n".join(output)
104
+
105
+ def format_simple_output(apps: List[Dict[str, Any]]) -> str:
106
+ """Uygulamaları basit liste formatında formatlar"""
107
+ if not apps:
108
+ return "📭 Hiç uygulama kurulu değil."
109
+
110
+ output = []
111
+ output.append(f"📦 Kurulu Uygulamalar ({len(apps)}):")
112
+
113
+ for app in sorted(apps, key=lambda x: x['name'].lower()):
114
+ name = app['name']
115
+ version = app['version']
116
+ language = app['language']
117
+ output.append(f" • {name} (v{version}) - {language}")
118
+
119
+ return "\n".join(output)
120
+
121
+ def format_json_output(apps: List[Dict[str, Any]]) -> str:
122
+ """Uygulamaları JSON formatında formatlar"""
123
+ return json.dumps(apps, indent=2, ensure_ascii=False)
124
+
125
+ def format_detailed_output(apps: List[Dict[str, Any]]) -> str:
126
+ """Uygulamaları detaylı formatda formatlar"""
127
+ if not apps:
128
+ return "📭 Hiç uygulama kurulu değil."
129
+
130
+ output = []
131
+ output.append(f"📦 Kurulu Uygulamalar ({len(apps)})")
132
+ output.append("=" * 60)
133
+
134
+ for i, app in enumerate(sorted(apps, key=lambda x: x['name'].lower()), 1):
135
+ output.append(f"\n{i}. {app['name']}")
136
+ output.append(f" Sürüm: {app['version']}")
137
+ output.append(f" Dil: {app['language']}")
138
+ output.append(f" Açıklama: {app['description']}")
139
+ output.append(f" Entry: {app['entry']}")
140
+ output.append(f" Klasör: {app['folder']}")
141
+ output.append(f" Yol: {app['path']}")
142
+
143
+ if app['dependencies']:
144
+ output.append(f" Bağımlılıklar: {', '.join(app['dependencies'])}")
145
+ else:
146
+ output.append(f" Bağımlılıklar: Yok")
147
+
148
+ return "\n".join(output)
149
+
150
+ def filter_apps_by_language(apps: List[Dict[str, Any]], language: str) -> List[Dict[str, Any]]:
151
+ """Uygulamaları dile göre filtreler"""
152
+ return [app for app in apps if app['language'].lower() == language.lower()]
153
+
154
+ def filter_apps_by_name(apps: List[Dict[str, Any]], search_term: str) -> List[Dict[str, Any]]:
155
+ """Uygulamaları isme göre filtreler"""
156
+ search_term = search_term.lower()
157
+ return [app for app in apps if search_term in app['name'].lower() or search_term in app['description'].lower()]
158
+
159
+ def list_apps(format_type: str = "table", language_filter: str = None, search_term: str = None) -> str:
160
+ """
161
+ Ana list fonksiyonu
162
+
163
+ Args:
164
+ format_type: Çıktı formatı (table, simple, json, detailed)
165
+ language_filter: Dil filtresi (python, lua, vb.)
166
+ search_term: Arama terimi
167
+
168
+ Returns:
169
+ Formatlanmış çıktı
170
+ """
171
+ apps = get_installed_apps_with_info()
172
+
173
+ # Filtreleri uygula
174
+ if language_filter:
175
+ apps = filter_apps_by_language(apps, language_filter)
176
+
177
+ if search_term:
178
+ apps = filter_apps_by_name(apps, search_term)
179
+
180
+ # Formatı uygula
181
+ if format_type == "json":
182
+ return format_json_output(apps)
183
+ elif format_type == "simple":
184
+ return format_simple_output(apps)
185
+ elif format_type == "detailed":
186
+ return format_detailed_output(apps)
187
+ else: # table (default)
188
+ return format_table_output(apps)
189
+
190
+ def show_help():
191
+ """Yardım mesajını gösterir"""
192
+ help_text = """
193
+ clapp list - Kurulu uygulamaları listeler
194
+
195
+ Kullanım:
196
+ python list_command.py [seçenekler]
197
+
198
+ Seçenekler:
199
+ --format FORMAT Çıktı formatı (table, simple, json, detailed)
200
+ --language LANG Dil filtresi (python, lua, vb.)
201
+ --search TERM Arama terimi (ad veya açıklamada)
202
+ --help Bu yardım mesajını göster
203
+
204
+ Örnekler:
205
+ python list_command.py
206
+ python list_command.py --format json
207
+ python list_command.py --format simple
208
+ python list_command.py --format detailed
209
+ python list_command.py --language python
210
+ python list_command.py --search calculator
211
+ python list_command.py --language python --format detailed
212
+ """
213
+ print(help_text.strip())
214
+
215
+ def main():
216
+ """CLI entry point"""
217
+ # Komut satırı argümanlarını parse et
218
+ args = sys.argv[1:]
219
+
220
+ if "--help" in args or "-h" in args:
221
+ show_help()
222
+ sys.exit(0)
223
+
224
+ format_type = "table"
225
+ language_filter = None
226
+ search_term = None
227
+
228
+ i = 0
229
+ while i < len(args):
230
+ arg = args[i]
231
+
232
+ if arg == "--format" and i + 1 < len(args):
233
+ format_type = args[i + 1]
234
+ i += 2
235
+ elif arg == "--language" and i + 1 < len(args):
236
+ language_filter = args[i + 1]
237
+ i += 2
238
+ elif arg == "--search" and i + 1 < len(args):
239
+ search_term = args[i + 1]
240
+ i += 2
241
+ else:
242
+ print(f"❌ Bilinmeyen argüman: {arg}")
243
+ print("Yardım için: python list_command.py --help")
244
+ sys.exit(1)
245
+
246
+ # Format kontrolü
247
+ valid_formats = ["table", "simple", "json", "detailed"]
248
+ if format_type not in valid_formats:
249
+ print(f"❌ Geçersiz format: {format_type}")
250
+ print(f"Geçerli formatlar: {', '.join(valid_formats)}")
251
+ sys.exit(1)
252
+
253
+ # Uygulamaları listele
254
+ try:
255
+ output = list_apps(format_type, language_filter, search_term)
256
+ print(output)
257
+ except Exception as e:
258
+ print(f"❌ Hata: {e}")
259
+ sys.exit(1)
260
+
261
+ if __name__ == "__main__":
262
+ main()